Skip to main content

rucc_driver/
compile.rs

1//! Running the front end over one file, from the bytes on disk to the typed tree.
2//!
3//! Design: `spec/04-driver-and-cli.md` section 4.3, and the `M2` exit criterion in
4//! `spec/17-milestones.md` that says `--emit=tast` works.
5//!
6//! [`preprocess`](mod@crate::preprocess) stops after phase 4 because `-E` stops there. This
7//! carries on: phase 7, the parse, and the checking. It is one function rather than four composed
8//! ones because of what the four share. The tokens hold interned symbols, the untyped tree holds
9//! tokens, the typed tree holds the untyped tree's spans, and none of them owns the table it is
10//! reading, so one [`Session`] has to outlive all of them and there has to be one place that
11//! holds it.
12
13use std::collections::HashMap;
14use std::path::Path;
15
16use rucc_base::{Interner, Symbol};
17use rucc_codegen::coverage::Fired;
18use rucc_codegen::elsewhere::Elsewhere;
19use rucc_codegen::lowering::Lowerings;
20use rucc_codegen::pipeline::{self, Machine, Recording};
21use rucc_codegen::pressure::Pressure;
22use rucc_cost::Goal;
23use rucc_diag::{Diagnostic, Severity, SourceMap, Span};
24use rucc_ir::{FpContract, Pic as IrPic, Visibility as IrVisibility};
25use rucc_lex::{Convert, Keywords, PpToken, convert};
26use rucc_lower::Protector as LowerProtector;
27use rucc_sema::{Checker, Context as CheckContext};
28use rucc_session::{
29    Contract, EmitKind, FileSystem, Options, Padding, Pic, Protector, Session, Visibility,
30};
31use rucc_target::TargetInfo;
32use rucc_tuple::{Arch, ObjectFormat};
33
34use crate::preprocess::render;
35
36/// What a compilation produced, which is text for most of the kinds and bytes for one of them.
37///
38/// Two variants rather than a string, because an object file is not text and a `Vec<u8>` holding
39/// UTF-8 for six kinds and a file format for the seventh would leave every reader guessing which
40/// it had. [`Artifact::Nothing`] is what a compilation that stopped early gives back, and it is
41/// not the same as an empty file: nothing is written for it at all.
42#[derive(Debug, Clone, PartialEq, Eq, Default)]
43pub enum Artifact {
44    /// The compilation stopped before it produced anything, or the kind asked for produces
45    /// nothing yet.
46    #[default]
47    Nothing,
48    /// Text, which is every kind up to and including assembly.
49    Text(String),
50    /// An object file, which is `-c`, and the names a linker can find in it.
51    ///
52    /// The names travel with the bytes rather than beside them because what wants them is the
53    /// archive step, and an index entry that does not match the member is worse than no archive:
54    /// the linker searches the index, pulls the member out, and still reports the name undefined.
55    /// One value holding both is one value the two cannot disagree in.
56    Object {
57        /// The file.
58        bytes: Vec<u8>,
59        /// Every name another object can reach, as the object writer wrote them. Empty is a real
60        /// answer: a translation unit of nothing but `static` functions is a member an archive
61        /// carries and nothing ever pulls out.
62        defines: Vec<String>,
63    },
64}
65
66impl Artifact {
67    /// The bytes to write, which is nothing at all for [`Artifact::Nothing`].
68    #[must_use]
69    pub fn bytes(&self) -> &[u8] {
70        match self {
71            Artifact::Nothing => &[],
72            Artifact::Text(text) => text.as_bytes(),
73            Artifact::Object { bytes, .. } => bytes,
74        }
75    }
76}
77
78/// What compiling one file produced.
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct Compiled {
81    /// What to write, which is nothing when the compilation failed or produced nothing.
82    pub artifact: Artifact,
83    /// The diagnostics, already rendered, one per element, in the order they were reported.
84    pub messages: Vec<String>,
85    /// How many of them were errors.
86    pub errors: u32,
87    /// Which lowering rules this file fired, for `-Zrule-coverage`.
88    ///
89    /// Empty for a compilation that stopped before the back end, which every kind up to and
90    /// including `--emit=ir` does. That is not the same as a rule set nothing reaches and the
91    /// caller unions these rather than reading one, so a file that fired nothing adds nothing.
92    pub fired: Fired,
93    /// What the register allocator had to put on the stack, for `-Zregister-pressure`.
94    ///
95    /// Empty for the same compilations `fired` is empty for and for the same reason, since both
96    /// are written by the back end and neither is a fact a file that stopped before it has.
97    pub pressure: Pressure,
98    /// What the pre-selection lowering group did, for `-Zlowering`.
99    ///
100    /// Empty for the same compilations `fired` is empty for and for the same reason, since the
101    /// group runs in the back end and a file that stopped before it lowered nothing.
102    pub lowerings: Lowerings,
103    /// What `-fdump-ir=` asked to see, in the order the passes ran.
104    ///
105    /// The optimizer does not write files, because nothing below the driver in
106    /// `spec/18-package-layout.md` knows what a file is, so the text comes back here and the
107    /// caller decides where it goes.
108    pub dumps: Vec<rucc_opt::Dump>,
109    /// What `-fopt-info` asked to hear, already rendered, one remark per line.
110    ///
111    /// Empty when the flag was not given, and also empty when it was given and no pass had
112    /// anything of the kinds asked for to say. Those two are the same text and different facts,
113    /// which is why a misspelled keyword is an error rather than a quiet nothing.
114    pub remarks: String,
115    /// Every file an `#include` found, for the `-M` family.
116    ///
117    /// The same list `Preprocessed` carries and for the same reason. A `-MD` writes it beside
118    /// the object, so the compiling path needs it as much as the preprocessing one does.
119    pub deps: Vec<rucc_pp::Dependency>,
120    /// What `-save-temps` asked to be kept, which is nothing at all unless it was given.
121    ///
122    /// It comes back from here rather than being produced by a second run of the compiler under
123    /// different flags, because a second run is a second answer: the file a person reads has to
124    /// be the file that was compiled, and two runs of anything with a `__TIME__` in it are not
125    /// the same text.
126    pub temps: Temps,
127}
128
129/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
130///
131/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
132/// `None` on one that stopped before there was any. Holding the text rather than writing it is
133/// what keeps this function free of the file system, which is what lets it be tested against a
134/// map from path to bytes.
135#[derive(Debug, Clone, PartialEq, Eq, Default)]
136pub struct Temps {
137    /// Phase 4's output, the same text `-E` would have printed.
138    pub preprocessed: Option<String>,
139    /// The assembly the back end produced on the way to the object file.
140    pub assembly: Option<String>,
141}
142
143impl Compiled {
144    /// Whether anything went wrong badly enough that the output should not be used.
145    #[must_use]
146    pub fn failed(&self) -> bool {
147        self.errors > 0
148    }
149
150    /// The text that was produced, and the empty string for anything that is not text.
151    ///
152    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
153    /// matching on a variant it has already ruled out.
154    #[must_use]
155    pub fn text(&self) -> &str {
156        match &self.artifact {
157            Artifact::Text(text) => text,
158            _ => "",
159        }
160    }
161}
162
163/// Compiles one file as far as `opts.emit` asks for and renders the result.
164///
165/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
166/// uses. Every kind but the executable produces something today, and that one runs the same front
167/// end and gives back nothing, so that a file with a mistake in it is reported the same way
168/// whichever kind was asked for, rather than compiling silently until the part that is written
169/// notices.
170///
171/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
172/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
173/// past leaves no declaration behind at all, and every later use of that name would be reported
174/// as undeclared. One mistake is worth one message.
175#[must_use]
176pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
177    let mut sess = Session::new(opts.clone());
178    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
179    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
180    // building this after the expansion would mean building it after `char` had been seen.
181    let keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
182    let mut diagnostics: Vec<Diagnostic> = Vec::new();
183    // Filled in by the back end when there is one, and empty for every kind that stops before it.
184    let mut fired = Fired::new();
185    // The same, and the other thing the back end is asked to record about itself.
186    let mut pressure = Pressure::new();
187    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
188    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
189    let mut dumps = Vec::new();
190    let mut remarks = String::new();
191    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
192    let mut temps = Temps::default();
193
194    let bytes = match fs.read(Path::new(name)) {
195        Ok(bytes) => bytes,
196        Err(e) => return failure(format!("{name}: {e}")),
197    };
198    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
199        return failure(format!("{name}: the source map has no room left for this file"));
200    };
201
202    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
203    // include context borrows the source map that rendering a diagnostic reads and the borrow
204    // has to end before anything is rendered.
205    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
206    let predef = rucc_pp::Predef::for_options(opts);
207    let expanded: Vec<PpToken> = {
208        let mut tokens = Vec::new();
209        // The inner block is the borrow. The printer under `-save-temps` reads the source map
210        // that the include context is holding, so the context has to be gone before it runs, and
211        // nothing happens in between, which is what makes the text it prints the text that is
212        // compiled below rather than a second answer to the same question.
213        {
214            let mut cx =
215                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
216            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
217            cx.pedantic = opts.pedantic;
218            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
219                return failure(format!(
220                    "{name}: the source map has no room for the built in macros"
221                ));
222            }
223            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
224                return failure(format!("{name}: the source map has no room for the command line"));
225            }
226            tokens.append(&mut pp.run(file, &mut cx));
227        }
228        if opts.save_temps.wanted() {
229            temps.preprocessed = Some(rucc_pp::print(
230                file,
231                &tokens,
232                pp.line_directives(),
233                &sess.sources,
234                &sess.interner,
235                rucc_pp::PrintOptions { line_markers: opts.line_markers },
236            ));
237        }
238        tokens.iter().map(|token| token.to_pp()).collect()
239    };
240    diagnostics.extend(pp.take_diagnostics());
241    // Taken here rather than at the end, because the preprocessor is done with and everything
242    // after this is about the tree it produced.
243    let deps = pp.dependencies().to_vec();
244
245    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
246    // a constant of a type.
247    let cx = Convert {
248        keywords: &keywords,
249        interner: &sess.interner,
250        target: &sess.target,
251        std: opts.std,
252        gnu: opts.gnu_extensions,
253        pedantic: opts.pedantic,
254    };
255    let (tokens, complaints) = convert(&expanded, &cx);
256    diagnostics.extend(complaints);
257
258    // Only the ones the file wrote, since a name nothing interned is one nothing can use.
259    let type_names: Vec<Symbol> =
260        sess.target.type_names().iter().filter_map(|&(name, _)| sess.interner.find(name)).collect();
261    let parsed = rucc_parse::parse(
262        &tokens,
263        rucc_parse::Context {
264            interner: &sess.interner,
265            std: opts.std,
266            gnu: opts.gnu_extensions,
267            pedantic: opts.pedantic,
268            error_limit: opts.error_limit as usize,
269            type_names: &type_names,
270        },
271    );
272    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
273    diagnostics.extend(parsed.diagnostics);
274
275    let mut artifact = Artifact::Nothing;
276    // Zero when nothing instruments, which is the truthful summary of a file built without
277    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
278    let mut instrumented = Instrumented::default();
279    if !parse_failed {
280        let mut checker = Checker::new(
281            &parsed.ast,
282            CheckContext {
283                names: &sess.interner,
284                target: &sess.target,
285                std: opts.std,
286                gnu: opts.gnu_extensions,
287                pedantic: opts.pedantic,
288                permissive: opts.permissive,
289                gnu89_inline: opts.gnu89_inline,
290                error_limit: opts.error_limit as usize,
291                // A freestanding program has no C library, so a name that is the library's
292                // everywhere else is the program's own here and means whatever it defined.
293                builtins: opts.builtins && opts.hosted,
294                no_builtin: &opts.no_builtin,
295                short_enums: opts.short_enums,
296                ms_extensions: sess.ms_extensions(),
297                trapping_math: opts.trapping_math,
298            },
299        );
300        checker.check_unit();
301        let checked = checker.finish();
302        if !checked.failed() {
303            match opts.emit {
304                EmitKind::Tast => {
305                    artifact = Artifact::Text(rucc_sema::print(
306                        &checked.tast,
307                        &checked.types,
308                        &sess.interner,
309                    ));
310                }
311                // Nothing past the checker, because a granule is a fact about a layout and a
312                // layout is settled the moment the closing brace is seen. Lowering the
313                // function bodies would take minutes on an amalgamation and answer nothing.
314                EmitKind::TypeGranules => {
315                    artifact = Artifact::Text(rucc_types::granule_report(
316                        &checked.types,
317                        &sess.interner,
318                        &sess.target,
319                    ));
320                }
321                EmitKind::Ir
322                | EmitKind::MirFinal
323                | EmitKind::Asm
324                | EmitKind::Object
325                | EmitKind::Archive
326                | EmitKind::Executable
327                | EmitKind::SafetySummary => {
328                    // What a `.incbin` in an `asm` at file scope names is read through the same
329                    // file system the sources came through, and from where the compiler was run
330                    // rather than from beside the source, because that is where an assembler
331                    // looks for it.
332                    let mut read = |named: &str| {
333                        fs.read(Path::new(named))
334                            .map(|bytes| bytes.as_slice().to_vec())
335                            .map_err(|why| why.to_string())
336                    };
337                    // What the debug information will say about types and signatures, taken
338                    // here because this is the last place the checker's types are readable
339                    // without the back end's borrow of the interner in the way. Nothing at all
340                    // when the build asked for no debug information, since a translation unit
341                    // the size of an amalgamation has tens of thousands of types in it.
342                    let meaning = if opts.debug_info {
343                        crate::shapes::collect(
344                            &checked.tast,
345                            &checked.types,
346                            &sess.target,
347                            &sess.interner,
348                            &sess.sources,
349                        )
350                    } else {
351                        crate::shapes::Meaning::default()
352                    };
353                    let mut lowered = rucc_lower::lower(
354                        crate::phase::source_name(name),
355                        rucc_lower::Context {
356                            tast: &checked.tast,
357                            types: &checked.types,
358                            target: &sess.target,
359                            names: &mut sess.interner,
360                            visibility: match opts.visibility {
361                                Visibility::Default => IrVisibility::Default,
362                                Visibility::Hidden => IrVisibility::Hidden,
363                                Visibility::Protected => IrVisibility::Protected,
364                            },
365                            protector: match opts.protector {
366                                Protector::None => LowerProtector::None,
367                                Protector::Buffers => LowerProtector::Buffers,
368                                Protector::Strong => LowerProtector::Strong,
369                                Protector::All => LowerProtector::All,
370                            },
371                            wrapping: rucc_lower::Wrapping {
372                                signed: opts.wrapping.signed,
373                                pointer: opts.wrapping.pointer,
374                                trap: opts.wrapping.trap,
375                            },
376                            aliasing: opts.strict_aliasing,
377                            padding: opts.padding == Padding::Ignored,
378                            contract: match opts.fp_contract {
379                                Contract::Off => FpContract::Off,
380                                Contract::On => FpContract::On,
381                                Contract::Fast => FpContract::Fast,
382                            },
383                            align: opts.align_functions,
384                            instrument: opts.instrument_functions,
385                            exceptions: opts.exceptions,
386                            read: &mut read,
387                        },
388                    );
389                    // The walk reports what it cannot build, and what it did build is printed
390                    // anyway: a file with one construct missing from it is more use to read
391                    // than nothing at all, and the errors are what stop it being compiled.
392                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
393                    if !failed {
394                        // The verifier runs on everything the walk builds, always. It is the
395                        // one check that a bug in the walk cannot talk its way past, and a
396                        // wrong instruction found here costs a message rather than an hour
397                        // in front of a debugger over the assembly it turned into.
398                        if let Err(errors) = rucc_ir::verify(&lowered.module, &sess.interner) {
399                            for error in errors {
400                                diagnostics.push(internal(&format!("invalid IR, {error}")));
401                            }
402                        } else if let Err(complaints) =
403                            instrument(&mut lowered.module, &mut sess.interner, opts)
404                                .map(|done| instrumented = done)
405                        {
406                            diagnostics.extend(complaints);
407                        } else if let Err(complaints) = optimize(
408                            &mut lowered.module,
409                            &mut sess.interner,
410                            &sess.target,
411                            opts,
412                            name,
413                            &mut dumps,
414                            &mut remarks,
415                        ) {
416                            diagnostics.extend(complaints);
417                        } else if opts.emit == EmitKind::SafetySummary {
418                            // After the optimizer, because the number that matters is how many
419                            // checks are still standing and there is no way to know that before it
420                            // has run. Before the back end, because the back end turns a check into
421                            // a call and a summary of calls is not a summary of checks.
422                            artifact = Artifact::Text(
423                                rucc_safety::summarize(
424                                    &lowered.module,
425                                    &sess.interner,
426                                    name,
427                                    opts.safety.as_str(),
428                                    instrumented.checks,
429                                    instrumented.interposed,
430                                    instrumented.crossings,
431                                )
432                                .render(),
433                            );
434                        } else if opts.emit == EmitKind::Ir {
435                            // After the optimizer rather than before it, so that `--emit=ir -O2`
436                            // is the IR the back end will be given rather than the IR it would
437                            // have been given at `-O0`. There is no other way to see what a pass
438                            // did without reading the assembly it turned into.
439                            artifact =
440                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
441                        } else {
442                            // The back end, which is every pass after the IR and which is
443                            // where a construct nothing has a rule for is finally noticed.
444                            match generate(
445                                &mut lowered.module,
446                                &mut sess.interner,
447                                &sess.target,
448                                opts,
449                                &mut Recording {
450                                    fired: &mut fired,
451                                    pressure: &mut pressure,
452                                    lowerings: &mut lowerings,
453                                },
454                                &mut temps.assembly,
455                                Origin { map: &sess.sources, name, meaning: &meaning },
456                            ) {
457                                Ok(made) => artifact = made,
458                                Err(complaints) => diagnostics.extend(complaints),
459                            }
460                        }
461                    }
462                    diagnostics.extend(lowered.diagnostics);
463                }
464                _ => {}
465            }
466        }
467        diagnostics.extend(checked.diagnostics);
468    }
469    // The back end's remarks after the optimizer's, which is the order the work happened in. Only
470    // the `switch` lowering says anything yet, and what it says is a rewrite.
471    let mut wants = rucc_opt::Wants::none();
472    for spec in &opts.opt_info {
473        // Checked when the arguments were parsed, and again by the optimizer.
474        let _ = wants.add(spec);
475    }
476    if wants.wants(rucc_opt::stats::Kind::Optimized) {
477        remarks.push_str(&lowerings.remarks(name));
478    }
479
480    let mut messages = Vec::with_capacity(diagnostics.len());
481    let mut errors = 0;
482    for diag in &diagnostics {
483        // `-w` drops the warning here rather than at the several hundred places one is raised,
484        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
485        // raised is not a warning there is anything to promote. A warning about something in a
486        // header that came with the machine goes the same way for the same reason, unless
487        // `-Wsystem-headers` asked for it.
488        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
489            continue;
490        }
491        if diag.severity.is_fatal()
492            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
493        {
494            errors += 1;
495        }
496        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
497    }
498    if errors > 0 {
499        // A tree built from a file that did not compile is not a tree anything should read.
500        artifact = Artifact::Nothing;
501    }
502    // Kept even when the compilation failed, because a rule that fired did fire and a report about
503    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
504    Compiled { artifact, messages, errors, fired, pressure, lowerings, dumps, remarks, deps, temps }
505}
506
507/// Reads one file of IR, checks it, and prints it back.
508///
509/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
510/// which is what makes the round trip in the M2 exit criterion something to run rather than
511/// something to believe: what the printer wrote is read back, verified, and written again, and
512/// the two files are either the same bytes or they are not.
513///
514/// The verifier runs here for the reason it runs after the walk. A module that was printed by
515/// this compiler has been through it once already, and one that a person edited has not.
516#[must_use]
517pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
518    let mut sess = Session::new(opts.clone());
519    if opts.emit != EmitKind::Ir {
520        return failure(format!(
521            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
522             the C in front of it became",
523            opts.emit.as_str()
524        ));
525    }
526    let bytes = match fs.read(Path::new(name)) {
527        Ok(bytes) => bytes,
528        Err(e) => return failure(format!("{name}: {e}")),
529    };
530    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
531        return failure(format!("{name}: this is not text, so it is not IR"));
532    };
533
534    let module = match rucc_ir::parse(text, &mut sess.interner) {
535        Ok(module) => module,
536        Err(error) => {
537            return failure(format!("{name}:{}: {}", error.line, error.message));
538        }
539    };
540    let mut diagnostics: Vec<Diagnostic> = Vec::new();
541    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
542        for error in errors {
543            diagnostics.push(invalid(&format!("invalid IR, {error}")));
544        }
545    }
546    let mut messages = Vec::with_capacity(diagnostics.len());
547    for diag in &diagnostics {
548        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
549    }
550    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
551    let artifact = if errors > 0 {
552        Artifact::Nothing
553    } else {
554        Artifact::Text(rucc_ir::print(&module, &sess.interner))
555    };
556    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
557    Compiled {
558        artifact,
559        messages,
560        errors,
561        fired: Fired::new(),
562        pressure: Pressure::new(),
563        lowerings: Lowerings::new(),
564        dumps: Vec::new(),
565        remarks: String::new(),
566        deps: Vec::new(),
567        temps: Temps::default(),
568    }
569}
570
571/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
572/// `-fsafety=` asked for them.
573///
574/// Between the walk and the optimizer, which is where section 15.3 of
575/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
576/// checks go in while the addresses the program computes still exist, and the optimizer then
577/// discharges the ones it can prove. Every sanitizer that came before instruments after the
578/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
579///
580/// The calls to the C library are redirected here too, and in the same window and for a related
581/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
582/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
583/// optimizer sees the call rather than after.
584///
585/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
586/// every function in the module, and a pass that produced IR nothing else accepts should say so
587/// here rather than in the assembly it turned into.
588///
589/// # Errors
590///
591/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
592/// this compiler and not in the program being compiled.
593fn instrument(
594    module: &mut rucc_ir::Module,
595    names: &mut Interner,
596    opts: &Options,
597) -> Result<Instrumented, Vec<Diagnostic>> {
598    if !opts.safety.instruments() {
599        return Ok(Instrumented::default());
600    }
601    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
602    // The one check that is about a call rather than about an access, so it is a walk of its own
603    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
604    // version is that deciding it means resolving a name, which takes the interner.
605    //
606    // Before the redirection for the same reason the redirection is before the optimizer: what this
607    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
608    // else would leave it with a name this one has no row for.
609    checks.freed = rucc_safety::ending::checks(module, names);
610    // Before the optimizer rather than beside the check lowering, which is what
611    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
612    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
613    // check insertion has already finished walking past.
614    let interposed = rucc_safety::redirect(module, names);
615    // After the redirection, so that a call this build models with a wrapper is not also counted
616    // as a crossing it did not model.
617    let crossings = rucc_safety::witness(module, names);
618    match rucc_ir::verify(module, names) {
619        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
620        Err(errors) => Err(errors
621            .iter()
622            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
623            .collect()),
624    }
625}
626
627/// What the instrumentation did, which nothing but the summary reads.
628///
629/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
630/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
631/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
632#[derive(Clone, Copy, Debug, Default)]
633struct Instrumented {
634    /// How many checks of each class went in.
635    checks: rucc_safety::Counts,
636    /// How many calls were pointed at an interposition wrapper.
637    interposed: usize,
638    /// How many places a pointer crosses to or from code this build did not instrument.
639    crossings: rucc_safety::Sites,
640}
641
642/// Runs the optimizer over the module, and collects whatever the dumps asked for.
643///
644/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
645/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
646/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
647///
648/// # Errors
649///
650/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
651/// not in the program being compiled, so it is reported as an internal error the way a bad
652/// lowering is.
653fn optimize(
654    module: &mut rucc_ir::Module,
655    names: &mut Interner,
656    target: &TargetInfo,
657    opts: &Options,
658    file: &str,
659    dumps: &mut Vec<rucc_opt::Dump>,
660    remarks: &mut String,
661) -> Result<(), Vec<Diagnostic>> {
662    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
663    // What the analyses that read a body may believe about it. The same question the back end asks
664    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
665    // that a name it exports is the one that will run, which is what every distribution builds a
666    // library with. It says nothing about how an address is reached, and gcc does not change that
667    // under the flag either, so the back end is not given this value.
668    settings.interposition = match opts.interposition {
669        true => replaceable(target, opts),
670        false => IrPic::Executable,
671    };
672    settings.toggles.clone_from(&opts.passes);
673    // The same pair the front end reads a call to a standard name with, which is section 20.1's
674    // three way split: `-ffreestanding` says the library is not there, `-fno-builtin` says it is
675    // there and is not to be assumed to do what the standard says, and a fold that leaves behind a
676    // call to `puts` needs both of those to be off.
677    settings.builtins = opts.builtins && opts.hosted;
678    settings.no_builtin.clone_from(&opts.no_builtin);
679    settings.fuel = opts.pass_fuel.iter().cloned().collect();
680    settings.global_fuel = opts.pass_fuel_global;
681    settings.verify |= opts.verify_each;
682    for (on, spec) in &opts.pass_gates {
683        // Same argument as the dumps below: every spelling in here was checked while the
684        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
685        if let Err(why) = settings.gates.add(*on, spec) {
686            return Err(vec![internal(&why)]);
687        }
688    }
689    for spec in &opts.dump_ir {
690        // Every spelling in here was checked while the arguments were parsed, so a rejection
691        // now is this compiler disagreeing with itself rather than the command line being wrong.
692        if let Err(why) = settings.dumps.add(spec) {
693            return Err(vec![internal(&why)]);
694        }
695    }
696    let mut wants = rucc_opt::Wants::none();
697    for spec in &opts.opt_info {
698        // Same argument as the dumps above: every spelling was checked while the arguments were
699        // parsed, so a rejection now is the compiler disagreeing with itself.
700        if let Err(why) = wants.add(spec) {
701            return Err(vec![internal(&why)]);
702        }
703    }
704    let report = rucc_opt::run(module, names, &settings);
705    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
706    dumps.extend(report.dumps);
707    match report.broke.is_empty() {
708        true => Ok(()),
709        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
710    }
711}
712
713/// Runs the back end over every function in `module` and writes what came out.
714///
715/// One machine function per definition in the module, in the order the module holds them, every
716/// register physical and every frame offset a constant. A declaration has no body and is skipped,
717/// because there is nothing in it to compile.
718///
719/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
720/// three read the same functions and differ in whether they are printed as machine IR, printed as
721/// assembly, or encoded and put in a file, which is the point of section 11.1 of
722/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
723/// worse than no listing, and the way to make that impossible is to have one description of an
724/// instruction and two ways of writing it down.
725///
726/// # Errors
727///
728/// One diagnostic per function the back end could not compile, or one about the target when no
729/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
730/// file with three constructs missing from the rule set reports three rather than one at a time.
731///
732/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
733/// which is the same functions written the other way rather than a second compilation of the same
734/// file. A listing that disagrees with the object beside it would be worse than none.
735/// Whether a name this file exports is one another object may define or replace.
736///
737/// The link that reads the object decides half of what is in it, and the command line is where that
738/// is said, which is why the flag reaches this far down. See #756.
739///
740/// ELF only, because it is a question about a format rather than about a machine and the other two
741/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
742/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
743/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
744/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
745/// what this does is decline to say the ELF answer about them.
746fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
747    match (target.tuple.os().object_format(), opts.pic) {
748        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
749        _ => IrPic::Executable,
750    }
751}
752
753/// Where the file being generated came from, which is what the debug information is about.
754///
755/// The three together rather than separately because none of them is any use on its own here: a
756/// span without the map it points into is a pair of numbers, a name without the spans is a file
757/// nothing in the object refers to, and a signature without the name of the function it belongs to
758/// is an entry with nothing to attach it to.
759#[derive(Clone, Copy)]
760struct Origin<'a> {
761    /// Where every span in the module points.
762    map: &'a SourceMap,
763    /// What the command line called the file, which is what `DW_AT_name` says.
764    name: &'a str,
765    /// The types and the signatures, and empty where the build wanted no debug information.
766    meaning: &'a crate::shapes::Meaning,
767}
768
769fn generate(
770    module: &mut rucc_ir::Module,
771    names: &mut Interner,
772    target: &TargetInfo,
773    opts: &Options,
774    recording: &mut Recording<'_>,
775    assembly: &mut Option<String>,
776    origin: Origin<'_>,
777) -> Result<Artifact, Vec<Diagnostic>> {
778    let Some(machine) = Machine::for_target(target) else {
779        return Err(vec![unsupported(&format!(
780            "there is no back end for {} in this compiler yet, so there is nothing to generate",
781            target.tuple
782        ))]);
783    };
784    // Refused rather than dropped. A command line that asks for a stack protector on a target
785    // that has nowhere to keep the word one is compared against would otherwise get code with no
786    // protection in it and no indication that the flag did nothing, which is the one outcome worse
787    // than the error. Windows is the case: it has a protector and it is a different mechanism.
788    if opts.protector != Protector::None && machine.conv.guard.is_none() {
789        return Err(vec![unsupported(&format!(
790            "{} is not supported for {} yet, because the stack protector on that target is not \
791             the one this compiler writes",
792            opts.protector, target.tuple
793        ))]);
794    }
795    // The same answer for the same reason. What says a file was built to have its control flow
796    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
797    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
798    // the same hardware and asks for it a different way, which is a bit in the image the linker is
799    // told to set rather than anything a compiler writes into an object.
800    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
801        return Err(vec![unsupported(&format!(
802            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
803             for it there is not the note this compiler writes",
804            opts.control, target.tuple
805        ))]);
806    }
807    // And once more. A profiled build is one whose functions call a routine the runtime provides,
808    // and a target whose runtime provides no such routine would get a call to a name nothing
809    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
810    // build by calling something else, asked for a different way and taking its argument in a
811    // register, so it is not this hook spelled differently.
812    let profile = match machine.conv.trace {
813        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
814        None if opts.profile => {
815            return Err(vec![unsupported(&format!(
816                "-pg is not supported for {} yet, because the profiler's hook on that target is \
817                 not the one this compiler calls",
818                target.tuple
819            ))]);
820        }
821        None => None,
822    };
823    // And once more. The room a patcher was promised is only half the feature: the other half is a
824    // section listing where every function's room is, and both the section's shape and the way it
825    // points at the text it belongs to are ELF's. A format that has no such section would take the
826    // nops and quietly lose the list, which is a build that looks patchable and is not.
827    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
828        return Err(vec![unsupported(&format!(
829            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
830             the room is there is not the section this compiler writes",
831            target.tuple
832        ))]);
833    }
834    let flags = pipeline::Flags {
835        frame_pointer: opts.keeps_frame_pointer(),
836        red_zone: opts.red_zone,
837        stack_clash: opts.stack_clash,
838        landing: opts.control.branch(),
839        profile: match profile {
840            None => pipeline::Profile::No,
841            Some(true) => pipeline::Profile::Early,
842            Some(false) => pipeline::Profile::Late,
843        },
844        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
845        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
846        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
847        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
848        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
849        // the blocks come out in the order they were written and a person stepping through the
850        // code walks down the screen.
851        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
852        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
853        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
854        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
855        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
856        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
857        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
858        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
859        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
860        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
861        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
862        // Off unless asked for. gcc pads loops at `-O2` and `-O3`. gcc's padding here cost a third
863        // of a percent of the corpus's text and more than a percent of SQLite's for no speed
864        // anybody could measure, which is tamnd/rucc#1823. The padding this asks for now keeps a
865        // small loop inside one line, which is 18% on AMD EPYC and nothing on an Intel Core, so no
866        // level asks for it on every machine's behalf. See tamnd/rucc#1838.
867        align_loops: opts.align_loops.unwrap_or(false),
868        // Whatever the command line said, and the model's own answer when it said nothing.
869        accurate: opts.cycle_accurate_model,
870        // The same flag that turns the IR verifier on in a release build, since what it says is
871        // that this run should check itself and the back end has checks of its own.
872        verify: opts.verify_each,
873        // What the level asked for. The back end had no way to know until now, which is
874        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
875        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
876        // rather than matched against, so a level added later answers this without editing it.
877        goal: Goal::for_size(opts.opt_level.is_size()),
878        // Only when somebody is measuring, and checked when the arguments were parsed.
879        switch: opts.switch_shape.as_deref().and_then(rucc_codegen::switch::Force::named),
880        // On from `-O2` and at `-Os`, which is where gcc turns `-foptimize-sibling-calls` on.
881        sibling: opts.sibling_calls.unwrap_or_else(|| opts.opt_level.sibling_calls()),
882    };
883
884    // The checks become calls here rather than beside the insertion, because the id each one
885    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
886    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
887    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
888    //
889    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
890    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
891    // for the machine.
892    if opts.safety.instruments() {
893        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
894        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
895        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
896        // capability for a pointer an allocator just returned is the one capability that is exact
897        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
898        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
899        //
900        // Safe to run twice and safe to run late, because it only ever sets the flag and never
901        // clears one, so a build that had it already gets the same module back.
902        rucc_opt::heap::annotate(module, names);
903        // Which calls hand their capabilities to the callee and which say there are none. Here and
904        // not beside the insertion, because the rule is what each function still has left to check
905        // and the optimizer is what makes that small: running before it would give every callee a
906        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
907        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
908        // buckets it prints describe the code that was actually built.
909        rucc_safety::handover::arrange(module);
910        rucc_safety::lower(module, names);
911        if let Err(errors) = rucc_ir::verify(module, names) {
912            return Err(errors
913                .iter()
914                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
915                .collect());
916        }
917    }
918
919    // Worked out before the loop and not inside it, because it reads the whole module and the loop
920    // is holding one function of it. It has to be after the check lowering above, since that adds
921    // calls to the runtime and so can add a name this file does not define.
922    //
923    // The link that reads the object decides half of what is in it, and the command line is where
924    // that is said, which is why the flag reaches this far down. See #756. The format decides the
925    // other half, since a table only exists on a format that has one to reach through.
926    //
927    // Only x86-64 copies a variable into the executable for a reference from the instruction
928    // pointer, so on the other machines a variable this file only declares is read from the table.
929    let copies = target.tuple.arch() == Arch::X86_64;
930    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format, copies);
931
932    let mut funcs = Vec::new();
933    let mut complaints = Vec::new();
934    for id in module.funcs() {
935        if module[id].is_declaration() {
936            continue;
937        }
938        match pipeline::compile_recording(
939            &mut module[id],
940            names,
941            &machine,
942            &elsewhere,
943            flags,
944            recording,
945        ) {
946            Ok(func) => funcs.push(func),
947            Err(why) => {
948                let name = names.resolve(module[id].name).to_owned();
949                // The function knows where the instruction came from, so the message lands on
950                // the line somebody wrote rather than on the file as a whole.
951                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
952                let said = format!("cannot generate code for '{name}': {why}");
953                complaints.push(unsupported_at(&said, span));
954            }
955        }
956    }
957    if !complaints.is_empty() {
958        return Err(complaints);
959    }
960    // The variables the file defines, which go through the back end the way the functions did not:
961    // there is nothing in a variable to select instructions for, so the module is what says what
962    // one is right up to the point where it is written down.
963    // The second names go the same way and for the same reason, and they are neither a function
964    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
965    let (globals, aliases) = match opts.emit {
966        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
967            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
968            rucc_asm::aliases(module, names).map_err(refused)?,
969        ),
970        _ => (rucc_asm::Globals::default(), Vec::new()),
971    };
972    // A failure in either of the last two is a bug here rather than a program this compiler is
973    // behind on, because every instruction in a function that got this far came out of the same
974    // description both of them read and every register in it has been allocated.
975    let unwind = opts.unwinds();
976    match opts.emit {
977        EmitKind::Asm => {
978            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
979                .map(Artifact::Text)
980                .map_err(refused)
981        }
982        // An executable is an object as far as this gets: one is what each file of a link
983        // contributes, and the linker is what turns them into the other. An archive is the same
984        // again, with the archive writer in place of the linker.
985        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
986            if opts.save_temps.wanted() {
987                let listing = rucc_asm::print(
988                    &funcs,
989                    &globals,
990                    &aliases,
991                    names,
992                    target,
993                    unwind,
994                    output(opts, target),
995                );
996                *assembly = Some(listing.map_err(refused)?);
997            }
998            // A template kept as text has no bytes until an assembler reads it. Most are read on
999            // their own where they are, but one may jump to a label another statement's text
1000            // defines or switch section halfway through, and a unit with one of those in it is
1001            // assembled the way gcc assembles every unit: written out as a listing and read back.
1002            // A build that asked for debug information gets a label in front of every instruction,
1003            // and where the reader placed those is the row the encoder would have recorded.
1004            //
1005            // Every unit for AArch64 goes this way for now. The listing is already written from
1006            // the encoder's own tables, so reading it back is the encoder run over the same values,
1007            // and it is one path to get right rather than two.
1008            let aarch64 = target.tuple.arch() == Arch::Aarch64;
1009            if aarch64 || rucc_asm::kept(&funcs, names, target) {
1010                let print = if opts.debug_info { rucc_asm::print_marked } else { rucc_asm::print };
1011                let listing =
1012                    print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1013                        .map_err(refused)?;
1014                let read = rucc_asm::read(&listing, target.tuple.arch()).map_err(|trouble| {
1015                    let what = if aarch64 {
1016                        "a unit for aarch64"
1017                    } else {
1018                        "an `asm` template kept as text"
1019                    };
1020                    vec![unsupported(&format!(
1021                        "{what}, whose listing the assembler stopped at on line {}: {}",
1022                        trouble.line, trouble.why
1023                    ))]
1024                })?;
1025                let info = if opts.debug_info {
1026                    let assembled =
1027                        placed(&read, &funcs, names, target).map_err(|why| vec![internal(&why)])?;
1028                    describe(&assembled, &globals.image(), &funcs, origin, opts, target)
1029                        .map_err(|why| vec![internal(&why)])?
1030                } else {
1031                    rucc_object::Info::default()
1032                };
1033                let defines = rucc_object::assembled_defines(&read);
1034                let bytes =
1035                    rucc_object::assembled_described(&read, &TargetInfo::new(opts.target), &info)
1036                        .map_err(wrote)?;
1037                return Ok(Artifact::Object { bytes, defines });
1038            }
1039            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
1040                .map_err(refused)?;
1041            let data = globals.image();
1042            // The line table, from the spans the assembler kept beside the bytes. Empty when the
1043            // build asked for no debug information, which is the case the rows above are not even
1044            // recorded in.
1045            let info = if opts.debug_info {
1046                describe(&assembled, &data, &funcs, origin, opts, target)
1047                    .map_err(|why| vec![internal(&why)])?
1048            } else {
1049                rucc_object::Info::default()
1050            };
1051            let text = assembled.text;
1052            // A format with no writer is a target this compiler is behind on and anything else
1053            // the writer refused is a bug here, and the two are not the same news to get.
1054            let bytes =
1055                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
1056                    .map_err(wrote)?;
1057            // Asked of the writer rather than worked out from the same three values here, so that
1058            // what the archive's index says and what is in the member cannot come apart. It is
1059            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
1060            // worth a second path.
1061            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
1062            Ok(Artifact::Object { bytes, defines })
1063        }
1064        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
1065    }
1066}
1067
1068/// The rows a listing marked by [`rucc_asm::print_marked`] would have had from the encoder, read
1069/// off where the reader placed each label.
1070///
1071/// Each function is where its own symbol is and as long as its `.size` says, and each row is its
1072/// label's distance from the symbol. The row for the front of the function is the one the encoder
1073/// writes from `Func::declared`, and it is written here the same way.
1074///
1075/// # Errors
1076///
1077/// A function or a label the reader did not place, which is a listing this compiler wrote and got
1078/// wrong.
1079fn placed(
1080    read: &rucc_object::Assembled,
1081    funcs: &[rucc_mir::Func],
1082    names: &Interner,
1083    target: &TargetInfo,
1084) -> Result<rucc_asm::Assembled, String> {
1085    let at: HashMap<&str, &rucc_object::Name> =
1086        read.names.iter().map(|name| (name.name.as_str(), name)).collect();
1087    let offset = |name: &str| match at.get(name).map(|name| name.at) {
1088        Some(rucc_object::Held::In { part, offset }) => Some((part, offset)),
1089        _ => None,
1090    };
1091    let mut text = rucc_object::Text::default();
1092    let mut lines = Vec::with_capacity(funcs.len());
1093    for (which, func) in funcs.iter().enumerate() {
1094        let name = names.resolve(func.name);
1095        let Some((part, start)) = offset(name) else {
1096            return Err(format!("the listing has no label for the function '{name}'"));
1097        };
1098        let mut rows = Vec::with_capacity(func.inst_count() + 1);
1099        if !func.declared.is_dummy() {
1100            rows.push(rucc_asm::Row { at: 0, span: func.declared, inst: None });
1101        }
1102        for block in func.blocks() {
1103            for inst in func.insts(block) {
1104                let label = rucc_asm::mark(target, which, inst);
1105                let Some((held, here)) = offset(&label) else {
1106                    return Err(format!("the listing has no label '{label}'"));
1107                };
1108                if held != part || here < start {
1109                    return Err(format!("the label '{label}' is not inside '{name}'"));
1110                }
1111                let at = usize::try_from(here - start).map_err(|why| why.to_string())?;
1112                rows.push(rucc_asm::Row { at, span: func.span(inst), inst: Some(inst) });
1113            }
1114        }
1115        let len = at.get(name).map_or(0, |name| name.size);
1116        text.funcs.push(rucc_object::Extent {
1117            name: name.to_owned(),
1118            start: usize::try_from(start).map_err(|why| why.to_string())?,
1119            len: usize::try_from(len).map_err(|why| why.to_string())?,
1120            align: func.align.unwrap_or(rucc_object::FUNC_ALIGN),
1121            binding: rucc_object::Binding::Global,
1122            visibility: rucc_object::Visibility::Default,
1123            patch: None,
1124        });
1125        lines.push(rows);
1126    }
1127    Ok(rucc_asm::Assembled { text, lines, frames: None })
1128}
1129
1130/// The debug sections for what was just assembled, as bytes and relocations.
1131///
1132/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
1133/// because the source map is the driver's and because the paths in it are still paths at this point.
1134/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
1135/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
1136/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
1137/// directory it was compiled in all go through it.
1138///
1139/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
1140/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
1141/// program counter is in the middle of is better told the line before than told a line that is not
1142/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
1143///
1144/// # Errors
1145///
1146/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
1147/// behind on.
1148fn describe(
1149    assembled: &rucc_asm::Assembled,
1150    data: &rucc_object::Data,
1151    machine: &[rucc_mir::Func],
1152    origin: Origin<'_>,
1153    opts: &Options,
1154    target: &TargetInfo,
1155) -> Result<rucc_object::Info, String> {
1156    let rucc_asm::Assembled { text, lines, frames } = assembled;
1157    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
1158    // The file table, built as the rows are walked rather than up front, because what belongs in it
1159    // is the files the code came from and not the files the preprocessor opened. A header that
1160    // contributed nothing but declarations is not one of them, and one that holds a definition is
1161    // in it twice over: once for the rows and once for the line the definition is declared on.
1162    let mut files: Vec<String> = Vec::new();
1163    let mut funcs = Vec::with_capacity(text.funcs.len());
1164    for ((extent, rows), built) in text.funcs.iter().zip(lines).zip(machine) {
1165        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1166        for row in rows {
1167            if row.span.is_dummy() {
1168                continue;
1169            }
1170            let Some(at) = origin.map.presumed(row.span.lo) else {
1171                continue;
1172            };
1173            let which = interned(&mut files, rewrite(at.name));
1174            let place = rucc_debug::Row {
1175                at: row.at as u64,
1176                file: which,
1177                line: at.line,
1178                column: at.column,
1179            };
1180            // Two rows at one address is one row, and the first of the two wins. The only place it
1181            // happens is the front of a function, where the row the assembler writes for the
1182            // declaration and the row for the first instruction land on the same byte, which is
1183            // what a function this compiler built no prologue for looks like: two instructions
1184            // cannot start at one address, so nowhere else has the question. The declaration is the
1185            // better answer there because it is the answer gcc gives, which it gives because gcc
1186            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1187            // about. A breakpoint on a function wants the line of the function rather than the line
1188            // of whatever its first statement happened to be.
1189            match out.last() {
1190                Some(last) if last.at == place.at => {}
1191                _ => out.push(place),
1192            }
1193        }
1194        // And the front of the function, for a function whose declaration had no span to give. The
1195        // assembler writes a row there from `Func::declared` and that is the usual way this is
1196        // covered, but a function that came from something other than a C source has no such span,
1197        // and the front of one is the one part of it no row would otherwise cover. A program
1198        // counter in there would get no answer at all rather than a slightly early one, and no
1199        // answer is the worse of the two for anybody reading a backtrace.
1200        if let Some(first) = out.first_mut() {
1201            first.at = 0;
1202        }
1203        // And what the function is, for the one this unit holds a definition of. A function the
1204        // walk above found and this did not is one whose name in the object is not the name the
1205        // declaration had, which `__asm__` on a declaration is the way to arrange, and one whose
1206        // signature could not be described. Both get rows and no entry, which leaves a debugger
1207        // where it is for every function today rather than anywhere worse.
1208        let known = origin.meaning.funcs.get(&extent.name);
1209        let decl = known.map(|known| rucc_debug::Place {
1210            file: interned(&mut files, rewrite(&known.file)),
1211            line: known.line,
1212        });
1213        // And where each of its locals is, for the ones the frame gave a slot. The back end hands
1214        // back the declaration each of them is and how far below the frame base it ended up, and
1215        // this is where a number turns back into a name, a type and a line, because this is the
1216        // last place the checker's declarations are still in hand.
1217        //
1218        // A parameter goes on the entry the signature already wrote for it rather than getting one
1219        // of its own, which is what the parameter numbers on the function are for. Two entries of
1220        // one name in one scope is a debugger's problem rather than a reader's.
1221        let mut sig = known.and_then(|known| known.sig.clone());
1222        let mut placed: Vec<(u32, i32)> = built.locals.clone();
1223        let mut spots = stretches(extent, rows, built, target);
1224        // And a local in the frame that shares its bytes and has no stretch at all, which still
1225        // gets its entry so that a debugger says it is not available rather than that there is no
1226        // such name. That is a function whose instructions were scheduled, where no stretch can be
1227        // given, and the whole of it is then somewhere the local may not be.
1228        for &decl in &built.sharing {
1229            if !spots.iter().any(|(at, _)| *at == decl) {
1230                spots.push((decl, Vec::new()));
1231            }
1232        }
1233        if let (Some(sig), Some(known)) = (sig.as_mut(), known) {
1234            for (param, decl) in sig.params.iter_mut().zip(&known.params) {
1235                let Some(decl) = *decl else { continue };
1236                if let Some(which) = placed.iter().position(|&(at, _)| at == decl) {
1237                    let at = rucc_debug::Held::Frame(i64::from(placed.remove(which).1));
1238                    param.spot = Some(rucc_debug::Spot::Always(at));
1239                    continue;
1240                }
1241                // Or the stretches, for a parameter the front end kept in a value rather than in
1242                // the frame, which is what a scalar parameter whose address is never taken is at
1243                // every optimization level including this one.
1244                let Some(which) = spots.iter().position(|(at, _)| *at == decl) else { continue };
1245                param.spot = Some(rucc_debug::Spot::Over(spots.remove(which).1));
1246            }
1247        }
1248        // Whatever is left, which is the locals that are not parameters, in the order the slots
1249        // were asked for. A number with nothing to look up is one whose declaration had no name,
1250        // which is a compound literal rather than anything the program can ask the value of.
1251        let mut locals = Vec::with_capacity(placed.len() + spots.len());
1252        // And which scope each of them was declared in, kept beside the list rather than on it,
1253        // because what goes on the entry is a place in this function's own table of scopes and that
1254        // table is not known until every local has been looked up.
1255        let mut wants: Vec<Option<usize>> = Vec::with_capacity(locals.capacity());
1256        for (decl, at) in placed {
1257            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1258            wants.push(named.scope);
1259            locals.push(rucc_debug::Local {
1260                name: named.name.clone(),
1261                ty: named.ty,
1262                decl: Some(rucc_debug::Place {
1263                    file: interned(&mut files, rewrite(&named.file)),
1264                    line: named.line,
1265                }),
1266                spot: rucc_debug::Spot::Always(rucc_debug::Held::Frame(i64::from(at))),
1267                scope: None,
1268            });
1269        }
1270        // And the ones with no slot at all, which are the locals the front end kept in a value.
1271        // Sorted by declaration, which is the order the program declared them in, so that what
1272        // comes out does not depend on the order the back end happened to hand registers out in.
1273        spots.sort_by_key(|(decl, _)| *decl);
1274        for (decl, spans) in spots {
1275            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1276            wants.push(named.scope);
1277            locals.push(rucc_debug::Local {
1278                name: named.name.clone(),
1279                ty: named.ty,
1280                decl: Some(rucc_debug::Place {
1281                    file: interned(&mut files, rewrite(&named.file)),
1282                    line: named.line,
1283                }),
1284                spot: rucc_debug::Spot::Over(spans),
1285                scope: None,
1286            });
1287        }
1288        // And the scopes the locals were declared in, which is where a name declared in an inner
1289        // block stops being one of the function's own. The numbers the walk over the tree handed out
1290        // are over the whole unit, and what goes on an entry is a place in this function's table, so
1291        // the two are joined here.
1292        let (scopes, at) = nests(&wants, &origin.meaning.scopes, extent, rows);
1293        for (local, want) in locals.iter_mut().zip(&wants) {
1294            local.scope = want.and_then(|want| at.get(&want).copied());
1295        }
1296        funcs.push(rucc_debug::Function {
1297            name: extent.name.clone(),
1298            len: extent.len as u64,
1299            rows: out,
1300            decl,
1301            sig,
1302            external: known.is_some_and(|known| known.external),
1303            locals,
1304            scopes,
1305        });
1306    }
1307    // And the file-scope variables, from the objects the back end laid out rather than from the
1308    // declarations, so that a name with an entry here is a name with a symbol to relocate against.
1309    // One the walk found and this did not is a `static` nothing read, and one this found and the
1310    // walk did not is a name the compiler made up rather than one the program wrote, a string
1311    // literal and a compound literal being the two: both are in the file and neither is a variable
1312    // anybody can ask the value of by name.
1313    let mut globals = Vec::new();
1314    for object in &data.objects {
1315        let Some(held) = origin.meaning.objects.get(&object.name) else { continue };
1316        globals.push(rucc_debug::Global {
1317            name: object.name.clone(),
1318            ty: held.ty,
1319            decl: Some(rucc_debug::Place {
1320                file: interned(&mut files, rewrite(&held.file)),
1321                line: held.line,
1322            }),
1323            external: held.external,
1324        });
1325    }
1326    let unit = rucc_debug::Unit {
1327        name: rewrite(origin.name),
1328        // A single dot when the process could not say where it was, which is a directory name every
1329        // debugger understands and which leaves a relative file name meaning what it already meant.
1330        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1331        producer: format!("rucc {}", crate::VERSION),
1332        files,
1333        types: origin.meaning.types.clone(),
1334        funcs,
1335        globals,
1336        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1337        // Whether a function can say where its frame base is, which it can when the build writes a
1338        // table that answers the question: the unwind table, or `.debug_frame` in its place. Read
1339        // off what was written rather than asked again, so the two cannot disagree about whether
1340        // the table a frame base is read through is there.
1341        frames: opts.unwinds() || frames.is_some(),
1342    };
1343    let mut info = rucc_debug::write(&unit).map_err(|why| why.to_string())?;
1344    info.chunks.extend(frames.clone());
1345    Ok(info)
1346}
1347
1348/// Where each local the back end kept in a register is, as stretches of the function's addresses.
1349///
1350/// The back end names a stretch by the instruction at either end of it, because a machine
1351/// instruction has no length until something encodes it. This is where it gets one: the assembler
1352/// writes a row per instruction for the line table and the row says how far into the function the
1353/// instruction begins, so the row after it is where it ends. The last instruction of a function
1354/// ends where the function does.
1355///
1356/// Grouped by declaration on the way out, since one local is in one place over one stretch and
1357/// somewhere else over the next, and that is the shape the debugging information wants.
1358fn stretches(
1359    extent: &rucc_object::Extent,
1360    rows: &[rucc_asm::Row],
1361    built: &rucc_mir::Func,
1362    target: &TargetInfo,
1363) -> Vec<(u32, Vec<rucc_debug::Span>)> {
1364    // A target nobody has written a calling convention down for has no DWARF numbering either, so
1365    // there is no way to name the register a local is in and nothing to say.
1366    let (false, Some(regs)) = (built.kept.is_empty(), target.call_regs) else {
1367        return Vec::new();
1368    };
1369    let ends = ends(extent, rows);
1370    let mut bounds = vec![None; built.inst_count()];
1371    for (which, row) in rows.iter().enumerate() {
1372        let Some(inst) = row.inst else { continue };
1373        bounds[inst.index()] = Some((row.at as u64, ends[which]));
1374    }
1375    let mut spots: Vec<(u32, Vec<rucc_debug::Span>)> = Vec::new();
1376    for kept in &built.kept {
1377        let (Some((from, _)), Some((_, to))) = (bounds[kept.from.index()], bounds[kept.to.index()])
1378        else {
1379            continue;
1380        };
1381        if to <= from {
1382            continue;
1383        }
1384        let held = match kept.at {
1385            // A register is named by the number this target's DWARF numbering gives it, which is a
1386            // fact about the class and the register together rather than about either alone.
1387            rucc_mir::Where::Reg { reg, class } => match regs.dwarf(class, reg) {
1388                Some(number) => rucc_debug::Held::Reg(number),
1389                None => continue,
1390            },
1391            rucc_mir::Where::Frame(at) => rucc_debug::Held::Frame(i64::from(at)),
1392        };
1393        let span = rucc_debug::Span { from, len: to - from, held };
1394        match spots.iter_mut().find(|(decl, _)| *decl == kept.decl) {
1395            Some((_, spans)) => spans.push(span),
1396            None => spots.push((kept.decl, vec![span])),
1397        }
1398    }
1399    for (_, spans) in &mut spots {
1400        *spans = settle(std::mem::take(spans));
1401    }
1402    spots.retain(|(_, spans)| !spans.is_empty());
1403    spots
1404}
1405
1406/// Where the instruction each of a function's line table rows was written for ends.
1407///
1408/// The row after it, which is where the next instruction begins, and the end of the function for the
1409/// last one. The row after it at a different address rather than simply the row after it, because an
1410/// instruction that encodes to nothing leaves two rows on one byte and the one in front of it is not
1411/// where anything ends.
1412///
1413/// Backwards, because that is one pass rather than a search from each row for the next address that
1414/// differs, and a function the size of `sqlite3VdbeExec` has tens of thousands of rows.
1415fn ends(extent: &rucc_object::Extent, rows: &[rucc_asm::Row]) -> Vec<u64> {
1416    let mut out = vec![extent.len as u64; rows.len()];
1417    let mut next = extent.len as u64;
1418    for which in (0..rows.len()).rev() {
1419        let at = rows[which].at as u64;
1420        // The answer the row behind got, for a row sharing an address with the one in front of it,
1421        // since the two end in the same place and the one in front has already been asked.
1422        out[which] = match next > at {
1423            true => next,
1424            false => out.get(which + 1).copied().unwrap_or(extent.len as u64),
1425        };
1426        next = next.min(at);
1427    }
1428    out
1429}
1430
1431/// The scopes one function's locals were declared in, as the debug writer wants them, and which of
1432/// its entries each of the unit's scopes became.
1433///
1434/// Only the ones a local of this function is in, and their ancestors. The unit's table holds every
1435/// scope in the translation unit, and a function reaches its own by walking up from the locals the
1436/// back end handed over, which is both the filter and the answer to which function a scope belongs
1437/// to. A scope no local of this function is in is not this function's business even if the numbers
1438/// happen to sit next to each other.
1439///
1440/// The addresses come from the source. A scope is a run of source bytes, every row of the line table
1441/// says which source bytes its instruction was built for, and the rows already say where each
1442/// instruction is, so the addresses of a scope are the addresses of the instructions whose bytes are
1443/// inside it. Nothing had to be carried down the compiler for this, and the nesting comes out right
1444/// on its own: a scope's bytes hold the bytes of every scope inside it, so its addresses hold
1445/// theirs.
1446fn nests(
1447    wants: &[Option<usize>],
1448    scopes: &[crate::shapes::Scope],
1449    extent: &rucc_object::Extent,
1450    rows: &[rucc_asm::Row],
1451) -> (Vec<rucc_debug::Scope>, HashMap<usize, usize>) {
1452    let mut needed: Vec<usize> = Vec::new();
1453    for &want in wants {
1454        let mut up = want;
1455        while let Some(which) = up {
1456            if needed.contains(&which) {
1457                break;
1458            }
1459            needed.push(which);
1460            up = scopes.get(which).and_then(|scope| scope.parent);
1461        }
1462    }
1463    // In the order the unit wrote them, which puts a scope after the one it is inside, because that
1464    // is the order the writer wants and is what lets a parent be named by an entry already made.
1465    needed.sort_unstable();
1466    let at: HashMap<usize, usize> =
1467        needed.iter().enumerate().map(|(which, &scope)| (scope, which)).collect();
1468    let ends = ends(extent, rows);
1469    let out = needed
1470        .iter()
1471        .map(|&which| {
1472            let scope = &scopes[which];
1473            rucc_debug::Scope {
1474                parent: scope.parent.and_then(|parent| at.get(&parent).copied()),
1475                over: spread(scope.span, &ends, rows),
1476            }
1477        })
1478        .collect();
1479    (out, at)
1480}
1481
1482/// Which of a function's addresses were built for a run of its source bytes.
1483///
1484/// A row whose own bytes are inside the run is code the run asked for, and the addresses of a scope
1485/// are the addresses of every such row joined up. Two rows that meet or overlap are one stretch,
1486/// which is what almost all of a scope is: the rows of a block are next to each other unless
1487/// something moved them, and a block the back end split into pieces is exactly the case a list is
1488/// for.
1489fn spread(span: Span, ends: &[u64], rows: &[rucc_asm::Row]) -> Vec<rucc_debug::Reach> {
1490    let mut out: Vec<rucc_debug::Reach> = Vec::new();
1491    for (which, row) in rows.iter().enumerate() {
1492        if row.span.is_dummy() || row.span.lo < span.lo || row.span.hi > span.hi {
1493            continue;
1494        }
1495        let (from, to) = (row.at as u64, ends[which]);
1496        if to <= from {
1497            continue;
1498        }
1499        match out.last_mut() {
1500            Some(last) if last.from + last.len >= from => {
1501                last.len = to.saturating_sub(last.from).max(last.len);
1502            }
1503            _ => out.push(rucc_debug::Reach { from, len: to - from }),
1504        }
1505    }
1506    out
1507}
1508
1509/// One declaration's stretches with the disagreements taken out and the neighbours joined up.
1510///
1511/// Two stretches of one declaration can cover the same address. That is what a program that assigns
1512/// to a local from something already live looks like: both values are live across the assignment,
1513/// the old one because something else still reads it. A stretch never runs past the end of its
1514/// block, so two that overlap are in one block, where the addresses go the way the instructions
1515/// run, and one that starts inside the other starts where the declaration was given its value:
1516/// where the value was computed, or where the assignment was for a value it took from another
1517/// declaration. From there the declaration holds the new value and not the old one, so the one
1518/// that started first ends there.
1519///
1520/// What is still left is two stretches that start at the same address, which is two values both
1521/// live into a block with nothing here to say which of them the declaration holds. Where the two
1522/// agree the answer is the same either way and they become one stretch, and where they disagree the
1523/// address is left out, so a debugger says the variable is unavailable there rather than printing
1524/// whichever register this walk reached first. A wrong answer is worse than none.
1525fn settle(mut spans: Vec<rucc_debug::Span>) -> Vec<rucc_debug::Span> {
1526    spans.sort_by_key(|span| (span.from, span.len));
1527    for which in 0..spans.len() {
1528        let (from, end, held) =
1529            (spans[which].from, spans[which].from + spans[which].len, spans[which].held);
1530        let later = spans[which + 1..]
1531            .iter()
1532            .take_while(|later| later.from < end)
1533            .find(|later| later.from > from && later.held != held);
1534        if let Some(later) = later {
1535            spans[which].len = later.from - from;
1536        }
1537    }
1538    // Every address a stretch begins or ends at, which cuts the function into pieces no stretch is
1539    // partly over: a piece is inside a stretch or outside it and never half of each.
1540    let mut edges: Vec<u64> =
1541        spans.iter().flat_map(|span| [span.from, span.from + span.len]).collect();
1542    edges.sort_unstable();
1543    edges.dedup();
1544    let mut out: Vec<rucc_debug::Span> = Vec::new();
1545    let mut first = 0;
1546    for pair in edges.windows(2) {
1547        let (from, to) = (pair[0], pair[1]);
1548        // Nothing before this can cover this piece or any piece after it, since the pieces only
1549        // ever move forward. The list is in the order the stretches start in, so the walk below
1550        // stops at the first one that starts too late as well.
1551        while spans.get(first).is_some_and(|span| span.from + span.len <= from) {
1552            first += 1;
1553        }
1554        let mut held = None;
1555        let mut agreed = true;
1556        for span in &spans[first..] {
1557            if span.from >= to {
1558                break;
1559            }
1560            if span.from > from || span.from + span.len < to {
1561                continue;
1562            }
1563            match held {
1564                None => held = Some(span.held),
1565                Some(seen) => agreed &= seen == span.held,
1566            }
1567        }
1568        let (Some(held), true) = (held, agreed) else { continue };
1569        match out.last_mut() {
1570            Some(last) if last.from + last.len == from && last.held == held => {
1571                last.len += to - from
1572            }
1573            _ => out.push(rucc_debug::Span { from, len: to - from, held }),
1574        }
1575    }
1576    out
1577}
1578
1579/// Where a file name is in the table, putting it there if it is not there yet.
1580///
1581/// A walk rather than a map because the table holds the files one object's code came from, which is
1582/// a handful even for an amalgamation: everything the preprocessor opened and nothing was generated
1583/// out of stays out of it.
1584fn interned(files: &mut Vec<String>, name: String) -> usize {
1585    match files.iter().position(|have| *have == name) {
1586        Some(which) => which,
1587        None => {
1588            files.push(name);
1589            files.len() - 1
1590        }
1591    }
1592}
1593
1594/// What the command line decided about the file being written, in the words the assembler and the
1595/// object writer use.
1596///
1597/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1598/// writers want is the object format's. The conversion is here rather than in either of them so
1599/// that the two output paths are handed the same thing and cannot come to disagree about what is
1600/// in a file.
1601///
1602/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1603/// that wanted its control flow checked would want a property of its own with a key of its own, so
1604/// writing this one there would be recording something untrue rather than recording nothing.
1605fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1606    let mut features = 0;
1607    if target.tuple.arch() == Arch::X86_64 {
1608        if opts.control.branch() {
1609            features |= rucc_object::Property::IBT;
1610        }
1611        if opts.control.ret() {
1612            features |= rucc_object::Property::SHSTK;
1613        }
1614    }
1615    rucc_object::Output {
1616        sections: rucc_object::Sections {
1617            functions: opts.function_sections,
1618            data: opts.data_sections,
1619        },
1620        property: rucc_object::Property { features },
1621    }
1622}
1623
1624/// What the object writer said, as the kind of news it is.
1625///
1626/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1627/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1628/// here, because every value it was handed came out of this compiler.
1629fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1630    match why {
1631        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1632        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1633    }
1634}
1635
1636/// What the assembler said, as the kind of news it is.
1637///
1638/// Three of these are about a program and the rest are about this compiler. A thread-local
1639/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1640/// the back end does not build yet, and everything else the assembler refuses is something that
1641/// should never have reached it.
1642fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1643    match why {
1644        rucc_asm::Error::Thread { .. }
1645        | rucc_asm::Error::IFunc { .. }
1646        | rucc_asm::Error::Frame { .. } => {
1647            vec![unsupported(&why.to_string())]
1648        }
1649        _ => vec![internal(&why.to_string())],
1650    }
1651}
1652
1653/// A diagnostic about a program this compiler is not finished enough to compile.
1654///
1655/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1656/// the back end that would handle it has not been written. The note says so, so that a report
1657/// about one of these is filed against the milestone rather than as a miscompilation.
1658fn unsupported(message: &str) -> Diagnostic {
1659    unsupported_at(message, Span::DUMMY)
1660}
1661
1662/// The same, about somewhere in the file rather than about the file.
1663///
1664/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1665/// about the plan: a reader who follows it wants to know whether the construct in front of them
1666/// is already written down as work, and the milestone list does not answer that.
1667fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1668    Diagnostic::error(message.to_owned(), span)
1669        .with_code("E0653")
1670        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1671}
1672
1673/// A diagnostic about IR that was handed to us rather than built by us.
1674fn invalid(message: &str) -> Diagnostic {
1675    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1676}
1677
1678/// A diagnostic about this compiler rather than about the program it was given.
1679fn internal(message: &str) -> Diagnostic {
1680    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1681        .with_code("E0652")
1682        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1683}
1684
1685/// A result that is nothing but one message, for the failures that happen before there is
1686/// anything to compile.
1687fn failure(message: String) -> Compiled {
1688    Compiled {
1689        artifact: Artifact::Nothing,
1690        messages: vec![format!("rucc: error: {message}")],
1691        errors: 1,
1692        fired: Fired::new(),
1693        pressure: Pressure::new(),
1694        lowerings: Lowerings::new(),
1695        dumps: Vec::new(),
1696        remarks: String::new(),
1697        deps: Vec::new(),
1698        temps: Temps::default(),
1699    }
1700}
1701
1702#[cfg(test)]
1703mod tests {
1704    use rucc_session::{MemoryFileSystem, Std};
1705    use rucc_target::Triple;
1706
1707    use super::*;
1708
1709    fn options() -> Options {
1710        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1711        opts.emit = EmitKind::Tast;
1712        // The tests here read the code a function turns into, and a frame pointer in every one
1713        // of them is noise that says nothing about what each test is about.
1714        opts.frame_pointer = Some(false);
1715        opts
1716    }
1717
1718    fn run(opts: &Options, source: &str) -> Compiled {
1719        let mut fs = MemoryFileSystem::new();
1720        fs.insert("/main.c", source.to_owned().into_bytes());
1721        compile(opts, "/main.c", &fs)
1722    }
1723
1724    /// Options with the compiler's own headers on the search path and nothing else, which is
1725    /// what a freestanding compilation is. There is no file system underneath these tests,
1726    /// so a header that reached for one would fail to resolve and say so.
1727    fn freestanding() -> Options {
1728        let mut opts = options();
1729        opts.hosted = false;
1730        opts.search.push_system(rucc_session::runtime::DIR);
1731        opts
1732    }
1733
1734    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1735    fn shipped(source: &str) -> String {
1736        let result = run(&freestanding(), source);
1737        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1738        result.text().to_owned()
1739    }
1740
1741    /// The typed tree of `source`, insisting that it compiled cleanly.
1742    fn tast(source: &str) -> String {
1743        let result = run(&options(), source);
1744        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1745        result.text().to_owned()
1746    }
1747
1748    #[test]
1749    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1750        let text = shipped(concat!(
1751            "#include <stdarg.h>\n",
1752            "int sum(int n, ...) {\n",
1753            "  va_list ap, copy;\n",
1754            "  va_start(ap, n);\n",
1755            "  va_copy(copy, ap);\n",
1756            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1757            "  va_end(ap);\n",
1758            "  va_end(copy);\n",
1759            "  return total;\n",
1760            "}\n",
1761        ));
1762        assert!(text.contains("va-start"), "{text}");
1763        assert!(text.contains("va-copy"), "{text}");
1764        assert!(text.contains("va-arg"), "{text}");
1765        assert!(text.contains("va-end"), "{text}");
1766    }
1767
1768    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1769    /// what it wants is the type without the four macro names. Answering the whole header
1770    /// would put `va_start` in the way of a program that has its own.
1771    #[test]
1772    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1773        let text = shipped(concat!(
1774            "#define __need___va_list\n",
1775            "#include <stdarg.h>\n",
1776            "int vprint(const char *f, __gnuc_va_list ap);\n",
1777            "#ifdef va_start\n",
1778            "#error va_start should not be defined\n",
1779            "#endif\n",
1780            "#ifdef _VA_LIST_DEFINED\n",
1781            "#error va_list should not have been made\n",
1782            "#endif\n",
1783        ));
1784        assert!(text.contains("vprint"), "{text}");
1785    }
1786
1787    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1788    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1789    #[test]
1790    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1791        let text = shipped(concat!(
1792            "#define __need_size_t\n",
1793            "#include <stddef.h>\n",
1794            "#ifdef offsetof\n",
1795            "#error offsetof should not be defined yet\n",
1796            "#endif\n",
1797            "#define __need_ptrdiff_t\n",
1798            "#include <stddef.h>\n",
1799            "#include <stddef.h>\n",
1800            "size_t a;\n",
1801            "ptrdiff_t b;\n",
1802            "wchar_t c;\n",
1803            "max_align_t d;\n",
1804            "void *e = NULL;\n",
1805            "struct P { int x; long y; };\n",
1806            "size_t f = offsetof(struct P, y);\n",
1807        ));
1808        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1809        assert!(text.contains("decl #1 b : long"), "{text}");
1810    }
1811
1812    #[test]
1813    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1814        let text = shipped(concat!(
1815            "#include <limits.h>\n",
1816            "#include <float.h>\n",
1817            "int bits = CHAR_BIT;\n",
1818            "long big = LONG_MAX;\n",
1819            "int low = INT_MIN;\n",
1820            "int radix = FLT_RADIX;\n",
1821            "int digits = DBL_MANT_DIG;\n",
1822        ));
1823        assert!(text.contains("const 8 : int"), "{text}");
1824        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1825        assert!(text.contains("const 2 : int"), "{text}");
1826        assert!(text.contains("const 53 : int"), "{text}");
1827    }
1828
1829    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1830    /// whole set out itself. The widths are the ones the target picked, which is the only
1831    /// reason this header is the compiler's.
1832    #[test]
1833    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1834        let text = shipped(concat!(
1835            "#include <stdint.h>\n",
1836            "int64_t a = INT64_C(1);\n",
1837            "uint_least16_t b;\n",
1838            "intptr_t c;\n",
1839            "uintmax_t d = UINTMAX_MAX;\n",
1840            "int wide = sizeof(int_fast64_t);\n",
1841        ));
1842        assert!(text.contains("decl #0 a : long"), "{text}");
1843        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1844        assert!(text.contains("decl #2 c : long"), "{text}");
1845    }
1846
1847    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1848    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1849    /// header that is nothing but definitions fails as a whole or not at all.
1850    ///
1851    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1852    /// only interesting next to another compiler's. Every intrinsic in the header was built
1853    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1854    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1855    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1856    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1857    #[test]
1858    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1859        let text = shipped(concat!(
1860            "#include <mmintrin.h>\n",
1861            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1862            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1863            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1864            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1865            "void done(void) { _mm_empty(); }\n",
1866        ));
1867        assert!(text.contains("add"), "{text}");
1868        assert!(text.contains("pack"), "{text}");
1869        assert!(text.contains("shift"), "{text}");
1870    }
1871
1872    /// The allocator beside the vector headers, which is the one piece of the family that is
1873    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1874    /// library, and the point of the test is that the reach resolves with nothing on the
1875    /// search path but the compiler's own directory.
1876    #[test]
1877    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
1878        let text = shipped(concat!(
1879            "#include <mm_malloc.h>\n",
1880            "void *get(void) { return _mm_malloc(64, 16); }\n",
1881            "void put(void *p) { _mm_free(p); }\n",
1882        ));
1883        assert!(text.contains("get"), "{text}");
1884        assert!(text.contains("put"), "{text}");
1885    }
1886
1887    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
1888    /// program that includes this one alone has to get all three. What the intrinsics answer is
1889    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
1890    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
1891    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
1892    /// `-O2` and `-Os`.
1893    ///
1894    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
1895    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
1896    /// differ while both sit inside the relative error Intel documents, which the same program
1897    /// checks directly rather than by comparing bits.
1898    #[test]
1899    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
1900        let text = shipped(concat!(
1901            "#include <xmmintrin.h>\n",
1902            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1903            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
1904            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
1905            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
1906            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
1907            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
1908            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
1909            "void *room(void) { return _mm_malloc(64, 16); }\n",
1910            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
1911        ));
1912        assert!(text.contains("add"), "{text}");
1913        assert!(text.contains("mask"), "{text}");
1914        assert!(text.contains("pick"), "{text}");
1915        assert!(text.contains("wide"), "{text}");
1916    }
1917
1918    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
1919    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
1920    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
1921    /// this is what notices if one is ever quietly defined to something close.
1922    ///
1923    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
1924    #[test]
1925    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
1926        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
1927        for absent in [
1928            "_mm_sqrt_ps",
1929            "_mm_sqrt_ss",
1930            "_mm_rsqrt_ps",
1931            "_mm_rsqrt_ss",
1932            "_mm_getcsr",
1933            "_mm_setcsr",
1934        ] {
1935            let defined = text.contains(&format!("{absent}("));
1936            assert!(!defined, "{absent} is defined and the header says it is not");
1937            assert!(text.contains(absent), "{absent} is absent and unexplained");
1938        }
1939    }
1940
1941    #[test]
1942    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
1943        let text = shipped(concat!(
1944            "#include <emmintrin.h>\n",
1945            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
1946            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
1947            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
1948            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
1949            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
1950            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
1951            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
1952            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
1953            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
1954            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
1955            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
1956            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
1957            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
1958            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
1959        ));
1960        assert!(text.contains("wide"), "{text}");
1961        assert!(text.contains("pack"), "{text}");
1962        assert!(text.contains("near"), "{text}");
1963        assert!(text.contains("half"), "{text}");
1964    }
1965
1966    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
1967    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
1968    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
1969    #[test]
1970    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
1971        let text = shipped(concat!(
1972            "#include <immintrin.h>\n",
1973            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
1974            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
1975            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
1976            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
1977            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
1978            "}\n",
1979            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
1980            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1981        ));
1982        assert!(text.contains("matching"), "{text}");
1983        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
1984        assert!(text.contains("single"), "the SSE header is not reached: {text}");
1985    }
1986
1987    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
1988    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
1989    /// that has never heard of an intrinsic gets here through `<windows.h>`.
1990    #[test]
1991    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
1992        let text = shipped(concat!(
1993            "#include <x86intrin.h>\n",
1994            "void barriers(void *p) {\n",
1995            "  _mm_lfence();\n",
1996            "  _mm_sfence();\n",
1997            "  _mm_mfence();\n",
1998            "  _mm_pause();\n",
1999            "  _mm_clflush(p);\n",
2000            "}\n",
2001            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2002        ));
2003        assert!(text.contains("barriers"), "{text}");
2004        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
2005    }
2006
2007    /// Including it twice is the same as including it once, and so is including it beside the
2008    /// header it reaches. A program that includes both spellings is the usual case rather than an
2009    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
2010    #[test]
2011    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
2012        let text = shipped(concat!(
2013            "#include <immintrin.h>\n",
2014            "#include <emmintrin.h>\n",
2015            "#include <immintrin.h>\n",
2016            "#include <x86intrin.h>\n",
2017            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2018        ));
2019        assert!(text.contains("twice"), "{text}");
2020    }
2021
2022    /// The AArch64 intrinsics, as xxhash uses them in `XXH3_accumulate_512_neon`: a load, a
2023    /// reinterpretation, the halves of a vector and a widening multiply added into a sum.
2024    #[test]
2025    fn the_shipped_arm_neon_has_what_xxhash_asks_it_for() {
2026        let mut opts = freestanding();
2027        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2028        let source = concat!(
2029            "#include <arm_neon.h>\n",
2030            "uint64x2_t acc(uint64x2_t sum, const void *in, const void *key) {\n",
2031            "  uint8x16_t data = vld1q_u8((const uint8_t *)in);\n",
2032            "  uint8x16_t k = vld1q_u8((const uint8_t *)key);\n",
2033            "  uint64x2_t mixed = vreinterpretq_u64_u8(veorq_u8(data, k));\n",
2034            "  uint32x2_t lo = vmovn_u64(mixed);\n",
2035            "  uint32x2_t hi = vshrn_n_u64(mixed, 32);\n",
2036            "  return vmlal_u32(sum, lo, hi);\n",
2037            "}\n",
2038            "uint32x4x2_t pair(uint32x4_t a, uint32x4_t b) { return vzipq_u32(a, b); }\n",
2039            "uint32_t total(uint32x4_t a) { return vaddvq_u32(a); }\n",
2040        );
2041        let result = run(&opts, source);
2042        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2043        assert!(result.text().contains("pair"), "{}", result.text());
2044        assert!(result.text().contains("total"), "{}", result.text());
2045    }
2046
2047    /// Off AArch64 the header says so, rather than failing on a type the target does not have.
2048    #[test]
2049    fn the_shipped_arm_neon_refuses_another_target() {
2050        let result = run(&freestanding(), "#include <arm_neon.h>\n");
2051        let said = result.messages.join("\n");
2052        assert!(said.contains("arm_neon.h is for AArch64"), "{said}");
2053    }
2054
2055    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
2056    /// both headers write down. A later change that quietly defines one as an approximation
2057    /// would be a wrong answer nobody sees, so the absence is held in place here.
2058    #[test]
2059    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
2060        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
2061        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
2062            let defined = text.contains(&format!("{absent}("));
2063            assert!(!defined, "{absent} is defined and the header says it is not");
2064            assert!(text.contains(absent), "{absent} is absent and unexplained");
2065        }
2066    }
2067
2068    #[test]
2069    fn the_three_formality_headers_still_have_to_work() {
2070        let text = shipped(concat!(
2071            "#include <stdbool.h>\n",
2072            "#include <stdalign.h>\n",
2073            "#include <iso646.h>\n",
2074            "#include <stdnoreturn.h>\n",
2075            "int t = true and not false;\n",
2076            "_Alignas(16) char buf[16];\n",
2077            "int a = alignof(long);\n",
2078        ));
2079        assert!(text.contains("decl #0 t : int"), "{text}");
2080        assert!(text.contains("const 8 : unsigned long"), "{text}");
2081    }
2082
2083    /// Including everything twice has to change nothing, because that is what happens in any
2084    /// program large enough to matter and a guard that is wrong shows up nowhere else.
2085    ///
2086    /// Stated as the two trees being the same rather than as a fact about what is in either
2087    /// one. A header that carries definitions puts them in the tree and moves everything
2088    /// after them along, so an assertion about where the program's own declaration landed is
2089    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
2090    #[test]
2091    fn every_shipped_header_can_be_included_twice() {
2092        // This is x86-64, and `<arm_neon.h>` is for AArch64 only, so it is held to the same
2093        // thing by the AArch64 test below.
2094        let once: String = rucc_session::runtime::names()
2095            .iter()
2096            .filter(|name| **name != "arm_neon.h")
2097            .map(|name| format!("#include <{name}>\n"))
2098            .collect();
2099        let twice = once.repeat(2);
2100        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
2101
2102        let mut opts = freestanding();
2103        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2104        let tree = |source: &str| {
2105            let result = run(&opts, source);
2106            assert_eq!(
2107                result.messages,
2108                Vec::<String>::new(),
2109                "expected this to compile:\n{source}"
2110            );
2111            result.text().to_owned()
2112        };
2113        let neon = "#include <arm_neon.h>\n";
2114        assert_eq!(tree(&format!("{neon}int x;\n")), tree(&format!("{neon}{neon}int x;\n")));
2115    }
2116
2117    #[test]
2118    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
2119        let fs = MemoryFileSystem::new();
2120        let result = compile(&options(), "/nope.c", &fs);
2121        assert!(result.failed());
2122        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
2123        assert!(result.text().is_empty());
2124    }
2125
2126    #[test]
2127    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
2128        let text = tast("int x = 1;\n");
2129        let expected = "\
2130decl #0 x : int object external static defined
2131  init
2132    +0
2133      const 1 : int
2134";
2135        assert_eq!(text, expected);
2136    }
2137
2138    #[test]
2139    fn the_macros_are_expanded_before_anything_is_parsed() {
2140        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
2141        // converted from a preprocessing number to a constant of a type, parsed as an
2142        // expression, and folded to the number the array type carries.
2143        let text = tast("#define N 2\nint a[N];\n");
2144        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
2145    }
2146
2147    /// A pragma survives the preprocessor on purpose, since what one means is not its
2148    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
2149    /// the parser reads and every other line is walked past. Both spellings are here because
2150    /// they arrive by different routes and only one of them was ever on a line of its own in
2151    /// the source.
2152    #[test]
2153    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
2154        let text = tast(concat!(
2155            "#pragma pack(4)\n",
2156            "struct s { int a; };\n",
2157            "#pragma pack()\n",
2158            "int b;\n",
2159            "_Pragma(\"GCC visibility push(default)\") int c;\n",
2160        ));
2161        assert!(text.contains("decl #0 b : int"), "{text}");
2162        assert!(text.contains("decl #1 c : int"), "{text}");
2163    }
2164
2165    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
2166    /// rather than reasoned about, which is why they are written as assertions the program
2167    /// makes about itself: a compilation with no messages is every one of them holding.
2168    ///
2169    /// This half is the attributes. `packed` takes the padding out, on the record or on one
2170    /// member, `aligned` raises and never lowers, and the two written together are the
2171    /// combination that packs and then aligns the whole thing.
2172    #[test]
2173    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
2174        tast(concat!(
2175            "struct A { char c; int i; } __attribute__((packed));\n",
2176            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2177            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2178            // `aligned` with nothing in the parentheses is the largest alignment the target
2179            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
2180            "struct B { char c; int i; } __attribute__((aligned));\n",
2181            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
2182            "struct C { char c; int i __attribute__((packed)); };\n",
2183            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
2184            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
2185            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
2186            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
2187            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
2188            "struct E { char c; _Alignas(8) int i; };\n",
2189            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
2190            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
2191            "struct F { char c; int i __attribute__((aligned(8))); };\n",
2192            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
2193            // Two the record already had, so the attribute asks for nothing new, and two
2194            // where four was already there, so the attribute is ignored rather than obeyed.
2195            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
2196            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
2197            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
2198            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
2199            // `packed` on a member takes the padding out in front of that member alone, so on
2200            // the first one it does nothing and on the second one it does all of it.
2201            "struct I { [[gnu::packed]] char c; int i; };\n",
2202            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2203            "struct J { char c; [[gnu::packed]] int i; };\n",
2204            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
2205            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
2206            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
2207            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
2208            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
2209            "union L { char c; int i; } __attribute__((packed));\n",
2210            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
2211            // The armoured spellings, which are the ones a system header writes, since a
2212            // program is entitled to a macro called `packed` and is not entitled to one called
2213            // `__packed__`. The two names are one attribute and the layout is the same one.
2214            "struct O { char c; int i; } __attribute__((__packed__));\n",
2215            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
2216            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
2217            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
2218        ));
2219    }
2220
2221    /// The attribute that changes what a call means rather than what a record lays out.
2222    ///
2223    /// Both halves are here. A call hands a value to a parameter of the union type and the value
2224    /// goes into the member that takes it, which is a compound literal of the union and is the
2225    /// same object the GNU cast to a union builds. And a declaration written with a member's type
2226    /// declares the same function as one written with the union, which is what lets a pointer to
2227    /// either be assigned from the other, and is what gnulib's signature checks do.
2228    ///
2229    /// The `void *` member is last on purpose: the search takes a member whose type the value
2230    /// already has wherever it sits, and falls back to a pointer member that would take the value
2231    /// silently only when there is no such member, so `char *` reaches the catch-all past two
2232    /// members that are not it.
2233    #[test]
2234    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
2235        let text = tast(concat!(
2236            "struct one { int x; };\n",
2237            "struct two { long y; };\n",
2238            "typedef union { struct one *a; struct two *b; void *any; }\n",
2239            "  __attribute__((__transparent_union__)) arg;\n",
2240            "int takes(arg v);\n",
2241            "int f(struct one *p, struct two *q, char *c) {\n",
2242            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
2243            "}\n",
2244            // The other half, which is about declarations and not about values.
2245            "int takes(struct one *p);\n",
2246            "int (*as_a_member)(struct one *) = takes;\n",
2247            "int (*as_the_union)(arg) = takes;\n",
2248        ));
2249        assert!(text.contains("compound-literal"), "{text}");
2250    }
2251
2252    /// The other place glibc writes it, which is the one that matters.
2253    ///
2254    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
2255    /// closing brace, so a compiler that reads only the second position reads nothing at all of
2256    /// the eleven pointer union that `bind` and `connect` and five others take.
2257    #[test]
2258    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
2259        let text = tast(concat!(
2260            "struct sockaddr { int family; };\n",
2261            "struct sockaddr_in { int family; int addr; };\n",
2262            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
2263            "  addr_arg __attribute__((__transparent_union__));\n",
2264            "int bind_to(int fd, addr_arg where);\n",
2265            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
2266        ));
2267        assert!(text.contains("compound-literal"), "{text}");
2268    }
2269
2270    /// What the attribute promises has to be a promise this can keep, and is checked rather than
2271    /// believed.
2272    ///
2273    /// A union wider than its first member is not passed the way that member is, and a structure
2274    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
2275    /// cases with a warning and compiles the program, because the type is still a perfectly good
2276    /// type and only the extra rule is gone.
2277    #[test]
2278    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
2279        let result = run(
2280            &options(),
2281            concat!(
2282                "union wider { int small; double large; } __attribute__((transparent_union));\n",
2283                "struct plain { int x; } __attribute__((transparent_union));\n",
2284            ),
2285        );
2286        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2287        assert!(!result.failed(), "{:?}", result.messages);
2288        for message in &result.messages {
2289            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
2290        }
2291        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
2292        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
2293    }
2294
2295    /// What an access to a packed member is allowed to assume about where it starts.
2296    ///
2297    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
2298    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
2299    /// is aligned to one. The number on the access has to say so, because it is what the back end
2300    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
2301    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
2302    /// program that is doing nothing wrong.
2303    #[test]
2304    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
2305        let packed = body(concat!(
2306            "struct P { char c; int v; } __attribute__((packed));\n",
2307            "int f(struct P *p) { return p->v; }\n",
2308        ));
2309        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
2310        // The same record without the attribute, which is where the type's own answer is right.
2311        let plain = body(concat!(
2312            "struct P { char c; int v; };\n",
2313            "int f(struct P *p) { return p->v; }\n",
2314        ));
2315        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
2316    }
2317
2318    /// The same, for the two ways of being further in than the member itself.
2319    ///
2320    /// An array member is stepped through rather than offset to, and a record member is offset to
2321    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
2322    /// number of elements leaves what the element width and the address had in common, which for
2323    /// a one byte aligned base is one byte however wide the elements are.
2324    #[test]
2325    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
2326        let stepped = body(concat!(
2327            "struct P { char c; int v[4]; } __attribute__((packed));\n",
2328            "int f(struct P *p, int i) { return p->v[i]; }\n",
2329        ));
2330        assert!(stepped.contains(", align 1,"), "{stepped}");
2331        assert!(!stepped.contains(", align 4,"), "{stepped}");
2332        let nested = body(concat!(
2333            "struct Inner { int v; };\n",
2334            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
2335            "int f(struct P *p) { return p->in.v; }\n",
2336        ));
2337        assert!(nested.contains(", align 1,"), "{nested}");
2338        assert!(!nested.contains(", align 4,"), "{nested}");
2339    }
2340
2341    /// The other way an access gets an alignment its type would not have given it, which is a
2342    /// typedef that lowered one.
2343    ///
2344    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
2345    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
2346    /// buffer nothing aligned is what every compression library does and this is how they write
2347    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
2348    /// `*(const unalign32 *)ptr`.
2349    ///
2350    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
2351    /// because that asks about the type and the type knew. The access was wrong, because the type
2352    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
2353    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
2354    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
2355    /// the monitor refused fifty six of zstd's reads, all of them correct.
2356    #[test]
2357    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
2358        let through = body(concat!(
2359            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2360            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
2361        ));
2362        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
2363        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
2364        // offset, so both read the pointee the same way and both have to come out the same.
2365        let stepped = body(concat!(
2366            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2367            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
2368        ));
2369        assert!(stepped.contains(", align 1,"), "{stepped}");
2370        assert!(!stepped.contains(", align 4,"), "{stepped}");
2371        // And the same typedef without the attribute, which is where the type's own answer is the
2372        // right one and nothing above should have changed it.
2373        let plain = body(concat!(
2374            "typedef unsigned int word;\n",
2375            "unsigned int f(const void *p) { return *(const word *)p; }\n",
2376        ));
2377        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
2378    }
2379
2380    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
2381    /// is and is the reason the intrinsic header exists at all.
2382    ///
2383    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
2384    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
2385    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
2386    /// covers, and then the return has to read the object as aligned as the object is rather than
2387    /// as aligned as the type it is being returned as: a vector comes back in registers on this
2388    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
2389    /// what lays the two pieces out rather than what either read may claim.
2390    #[test]
2391    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
2392        let prefix = concat!(
2393            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
2394            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
2395        );
2396        let loaded =
2397            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
2398        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
2399        assert!(!loaded.contains("align 16"), "{loaded}");
2400        // The store side, which travels as a copy into whatever the pointer names and so carries
2401        // one number for both ends of it.
2402        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
2403        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
2404        // And the aligned spelling of the same two, which is where sixteen is the right answer.
2405        let aligned =
2406            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
2407        assert!(aligned.contains("align 16"), "{aligned}");
2408    }
2409
2410    /// The same attribute on a declaration rather than on a type, which asks that this object or
2411    /// this function be at a multiple of that, and which is where a program that has to hand a
2412    /// buffer to hardware or keep two counters off one cache line writes it.
2413    ///
2414    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
2415    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
2416    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
2417    /// because that is the question a program asking it is asking.
2418    #[test]
2419    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
2420        tast(concat!(
2421            "int v __attribute__((aligned(64)));\n",
2422            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
2423            // Written on the specifiers rather than after the declarator, which asks the same
2424            // thing and is the spelling a header is more likely to use.
2425            "__attribute__((aligned(32))) int w;\n",
2426            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
2427            "[[gnu::aligned(16)]] int x;\n",
2428            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
2429            // Two below the four an `int` already has, so nothing is asked for and nothing is
2430            // said, and the type still answers for the object.
2431            "int y __attribute__((aligned(2)));\n",
2432            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
2433            // A local, which is the same question one scope down.
2434            "void f(void) { int a __attribute__((aligned(128)));\n",
2435            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
2436            // The type is untouched by any of it: `aligned` on a declaration says where that
2437            // declaration goes and says nothing about every other `int` in the program.
2438            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2439            // A function, which has no alignment of its own for this to be measured against and
2440            // takes whatever was asked for.
2441            "void g(void) __attribute__((aligned(256)));\n",
2442            "void g(void) {}\n",
2443            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
2444        ));
2445    }
2446
2447    /// And what the object file says, which is the half that makes the answer above true. A
2448    /// function is at a fixed offset inside the text section, so it is at a multiple of two
2449    /// hundred and fifty six only if the section is at one too.
2450    #[test]
2451    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
2452        let text = asm(concat!(
2453            "int v __attribute__((aligned(64)));\n",
2454            "void g(void) __attribute__((aligned(256)));\n",
2455            "void g(void) {}\n",
2456            "void plain(void) {}\n",
2457        ));
2458        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
2459        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2460        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
2461    }
2462
2463    /// The same question asked by the command line instead of by a declaration, which is
2464    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
2465    /// floor: a function that named a larger boundary itself keeps it, and one that named a
2466    /// smaller one is moved up, because the attribute is a requirement about one function and the
2467    /// flag is a preference about all of them.
2468    #[test]
2469    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
2470        let source = concat!(
2471            "void g(void) __attribute__((aligned(256)));\n",
2472            "void g(void) {}\n",
2473            "void small(void) __attribute__((aligned(4)));\n",
2474            "void small(void) {}\n",
2475            "void plain(void) {}\n",
2476        );
2477        let listing = |align: Option<u32>| {
2478            let mut opts = options();
2479            opts.emit = EmitKind::Asm;
2480            opts.align_functions = align;
2481            let result = run(&opts, source);
2482            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
2483            result.text().to_owned()
2484        };
2485
2486        let text = listing(Some(32));
2487        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
2488        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
2489        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
2490
2491        // And the negative form, which asks for the smallest boundary the target has and is the
2492        // one spelling that takes a function below the sixteen bytes it would get anyway.
2493        let text = listing(Some(8));
2494        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
2495        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2496    }
2497
2498    /// And the one position where the attribute means something else. On a declaration it raises
2499    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
2500    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
2501    /// `int` at a multiple of two and a record with one in it really is smaller for it.
2502    ///
2503    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
2504    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
2505    /// and gcc refuses an array of one rather than padding the elements out to fit.
2506    #[test]
2507    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
2508        tast(concat!(
2509            "typedef int L __attribute__((aligned(2)));\n",
2510            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
2511            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
2512            // Below what an `int` has, which is the half a declaration cannot ask for.
2513            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
2514            "struct T { char c; L x; };\n",
2515            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
2516            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
2517            // And upwards, which is the ordinary direction and the one a header writes.
2518            "typedef int H __attribute__((aligned(16)));\n",
2519            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
2520            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
2521            "struct U { char c; H x; };\n",
2522            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
2523            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
2524            // A typedef of a typedef, where the nearer one is the one the declaration was
2525            // written with and is the one that answers.
2526            "typedef L M __attribute__((aligned(8)));\n",
2527            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
2528            // And one that asked for nothing, which still has whatever the one behind it asked
2529            // for because it is the same type spelled again.
2530            "typedef L N;\n",
2531            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
2532            // The type it stands for is untouched by any of it.
2533            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2534        ));
2535        let text = asm(concat!(
2536            "typedef int L __attribute__((aligned(2)));\n",
2537            "typedef int H __attribute__((aligned(16)));\n",
2538            "L low;\n",
2539            "H high;\n",
2540        ));
2541        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
2542        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
2543    }
2544
2545    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
2546    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
2547    /// one is that operator over each lane.
2548    ///
2549    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
2550    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
2551    /// size, which is what a machine that has the registers wants and what gcc gives one here.
2552    #[test]
2553    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
2554        tast(concat!(
2555            "typedef int __attribute__((vector_size(16))) v4si;\n",
2556            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
2557            "typedef char __attribute__((vector_size(16))) v16qi;\n",
2558            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
2559            // One lane, which is a power of two and is a vector rather than the type it was
2560            // written on: the operators it takes are the vector's and not the scalar's.
2561            "typedef int __attribute__((vector_size(4))) v1si;\n",
2562            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
2563            // The armoured spelling and the bracket one, which are the same attribute.
2564            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
2565            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
2566            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
2567            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
2568            // A lane is what a subscript answers with, and a vector is not a pointer: there is
2569            // nothing to decay and the lane type is the one the arithmetic happens in.
2570            "v4si g;\n",
2571            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
2572            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
2573            // A scalar beside a vector stands for itself in every lane, so the answer is still
2574            // the vector and not the wider of the two types.
2575            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
2576            // An array of them, which is the ordinary way a program holds several.
2577            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
2578        ));
2579    }
2580
2581    /// A whole vector written into an array of them, and a vector named by a type name rather
2582    /// than by a typedef.
2583    ///
2584    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
2585    /// a list is written into it, so a braced element that is itself a vector has to be taken
2586    /// whole rather than started as the first lane, and the type of what was written is the only
2587    /// thing that says which was meant. And a type name is where a compound literal and a cast
2588    /// spell the type out, which a macro taking a lane type and a lane count does, so the
2589    /// attribute has to be read there and not only on a declaration.
2590    #[test]
2591    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
2592        tast(concat!(
2593            "typedef int __attribute__((vector_size(8))) v2si;\n",
2594            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
2595            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
2596            // The size written out rather than named, which is the spelling a macro expands to.
2597            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
2598            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
2599            // A lane is still a lane, so a list of them fills the vector the way it always did
2600            // and the rule above did not turn brace elision off.
2601            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
2602            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
2603        ));
2604    }
2605
2606    /// A lane written rather than read, and a shift whose two vectors are not the same type.
2607    ///
2608    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
2609    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
2610    /// has an address, and a qualifier written on the vector reaches every lane the way it does
2611    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
2612    /// single type, since the right side counts rather than computes.
2613    #[test]
2614    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
2615        let result = run(
2616            &options(),
2617            concat!(
2618                "typedef int __attribute__((vector_size(16))) v4si;\n",
2619                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
2620                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
2621                "  v4si v = { 1, 2, 3, 4 };\n",
2622                "  v[0] = n;\n",
2623                "  v[1] += n;\n",
2624                "  v[2]++;\n",
2625                "  *&v[3] = n;\n",
2626                // The count is signed and the value is not, which no other operator allows.
2627                "  v4ui shifted = a >> b;\n",
2628                "  shifted <<= b;\n",
2629                // A scalar stands in every lane on either side of a shift, which is the half
2630                // that looks wrong: the shape of the answer comes off the count here.
2631                "  *out = v + (v4si)shifted + (1 << b);\n",
2632                "}\n",
2633                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
2634                // to write to.
2635                "void refused(const v4si c) {\n",
2636                "  c[0] = 1;\n",
2637                "}\n",
2638            ),
2639        );
2640        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
2641        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
2642    }
2643
2644    /// The third layout attribute, and the one that moves nothing. It says the scalars in the
2645    /// record are stored in the byte order it names, so on a target whose order is the other one
2646    /// every load through a member swaps its bytes and so does every store. The record is the size
2647    /// and the alignment it would be without it and every member is where it would be, which is
2648    /// what gcc 16.2.0 does and what was measured before any of this was written.
2649    ///
2650    /// All four spellings are here because a header writes the armoured one, the attribute may be
2651    /// written in front of the body as well as behind it, and the C23 spelling in gcc's namespace
2652    /// is the same attribute a fourth way. The order the target already has is the fifth case and
2653    /// asks for nothing, since a program saying what would have happened anyway is entitled to be
2654    /// compiled as though it had said nothing.
2655    #[test]
2656    fn a_record_that_asks_for_the_other_byte_order_swaps_every_scalar_it_holds() {
2657        let read = "int f(struct s *p) { return p->i; }\n";
2658        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2659        assert!(body(&format!("{big}{read}")).contains("bswap"), "{big}");
2660
2661        let armoured =
2662            "struct s { int i; } __attribute__((__scalar_storage_order__(\"big-endian\")));\n";
2663        assert!(body(&format!("{armoured}{read}")).contains("bswap"), "{armoured}");
2664
2665        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
2666        assert!(body(&format!("{front}{read}")).contains("bswap"), "{front}");
2667
2668        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
2669        assert!(body(&format!("{standard}{read}")).contains("bswap"), "{standard}");
2670
2671        let same =
2672            "struct s { int i; } __attribute__((scalar_storage_order(\"little-endian\")));\n";
2673        assert!(!body(&format!("{same}{read}")).contains("bswap"), "{same}");
2674
2675        // A member one byte wide has only one order, and neither has the record itself.
2676        let byte = "struct s { char c; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2677        let source = format!("{byte}int f(struct s *p) {{ return p->c; }}\n");
2678        assert!(!body(&source).contains("bswap"), "{byte}");
2679
2680        tast(concat!(
2681            "struct s { int i; short h; char c; }",
2682            " __attribute__((scalar_storage_order(\"big-endian\")));\n",
2683            "_Static_assert(sizeof(struct s) == 8 && _Alignof(struct s) == 4, \"s\");\n",
2684            "_Static_assert(__builtin_offsetof(struct s, h) == 4, \"s.h\");\n",
2685            "_Static_assert(__builtin_offsetof(struct s, c) == 6, \"s.c\");\n",
2686        ));
2687    }
2688
2689    /// A bit-field in one of these records lies in the same bytes and is counted from the top of
2690    /// them rather than from the bottom. `execute/20230630-2.c` is the program that says so:
2691    /// `short i : 12` in front of four one bit fields holds 341 in the two bytes `15 5f`, so the
2692    /// twelve bits are the top twelve and reading them is a shift right by four rather than a mask
2693    /// alone. The plain record shifts nothing, since there the field is already at the bottom.
2694    #[test]
2695    fn a_bit_field_in_one_of_those_records_is_counted_from_the_top_of_its_bytes() {
2696        let members = "short i : 12; char c1 : 1; char c2 : 1; char c3 : 1; char c4 : 1;";
2697        let read = "int f(struct s *p) { return p->i; }\n";
2698        let plain = format!("struct s {{ {members} }};\n{read}");
2699        let reversed = format!(
2700            "struct s {{ {members} }} __attribute__((scalar_storage_order(\"big-endian\")));\n\
2701             {read}"
2702        );
2703        assert!(body(&plain).contains("shl"), "{}", body(&plain));
2704        assert!(!body(&plain).contains("bswap"), "{}", body(&plain));
2705        // The two loaded bytes the other way round and then the top twelve bits of them, which
2706        // is the arithmetic shift right on its own with nothing to move the field up to the top.
2707        let built = body(&reversed);
2708        assert!(built.contains("bswap"), "{built}");
2709        assert!(!built.contains("shl"), "{built}");
2710        assert!(built.contains("ashr"), "{built}");
2711    }
2712
2713    /// The one thing a program may not do with a member of one of these records. The bytes are
2714    /// there and they are the other way round, so a pointer to them is a pointer to a value of
2715    /// that type which is not the value the member holds. gcc refuses it in these words, and it
2716    /// refuses only the scalars: the address of a nested record or of an array member is an
2717    /// address of the bytes as they lie, and an access through it asks its own type which order
2718    /// it is in.
2719    #[test]
2720    fn the_address_of_a_scalar_stored_the_other_way_round_is_refused() {
2721        let opts = options();
2722        let record = "struct s { int i; int a[2]; struct in { int n; } w; }\n\
2723                      __attribute__((scalar_storage_order(\"big-endian\")));\n";
2724        let taken = format!("{record}int *f(struct s *p) {{ return &p->i; }}\n");
2725        assert_eq!(
2726            run(&opts, &taken).messages,
2727            ["/main.c:3:30: error: cannot take address of scalar with reverse storage order \
2728              [E0712]"]
2729        );
2730        let element = format!("{record}int *f(struct s *p) {{ return &p->a[0]; }}\n");
2731        let messages = run(&opts, &element).messages;
2732        assert!(messages[0].contains("[E0712]"), "{messages:?}");
2733
2734        let whole = format!("{record}int *f(struct s *p) {{ return (int *) &p->w; }}\n");
2735        assert_eq!(run(&opts, &whole).messages, Vec::<String>::new(), "{whole}");
2736    }
2737
2738    /// An argument that names neither order, which gcc answers with the two words it does take.
2739    /// A program that writes one of these is reading a wire format and would rather be told the
2740    /// spelling it got wrong than be handed a record laid out in the order it did not ask for.
2741    #[test]
2742    fn a_storage_order_that_names_neither_end_is_refused_with_the_two_words_that_are_taken() {
2743        let opts = options();
2744        let wrong = "struct s { int i; } __attribute__((scalar_storage_order(\"middle\")));\n";
2745        assert_eq!(
2746            run(&opts, wrong).messages,
2747            ["/main.c:1:36: error: 'scalar_storage_order' argument must be one of \"big-endian\" \
2748              or \"little-endian\" [E0688]"]
2749        );
2750        let bare = "struct s { int i; } __attribute__((scalar_storage_order));\n";
2751        let messages = run(&opts, bare).messages;
2752        assert!(messages[0].contains("[E0688]"), "{messages:?}");
2753    }
2754
2755    /// Where a bit-field goes, which packing decides and which is the part of all this that
2756    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
2757    /// make it span more storage than its own type occupies, and then it moves to the next
2758    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
2759    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
2760    ///
2761    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
2762    /// and every size below comes out the same either way, so what is asked is the byte a read
2763    /// of the field loads from.
2764    #[test]
2765    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
2766        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
2767        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
2768        assert_eq!(
2769            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
2770            1
2771        );
2772        assert_eq!(
2773            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
2774            1
2775        );
2776        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
2777        // A thirty bit field after a byte, which is the case the rule was written for.
2778        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
2779        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
2780        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
2781        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
2782        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
2783    }
2784
2785    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
2786    fn bit_field_byte(record: &str) -> u64 {
2787        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
2788        let body = body(&source);
2789        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
2790        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
2791        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
2792    }
2793
2794    /// An attribute in the middle of a specifier list, which is where a member usually carries
2795    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
2796    /// written in front of the declaration are collected as the list is walked and the
2797    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
2798    /// over each other rather than joined.
2799    #[test]
2800    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
2801        tast(concat!(
2802            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
2803            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
2804            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
2805            "struct b { char c; __attribute__((packed)) int i; };\n",
2806            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
2807            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
2808            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
2809            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
2810        ));
2811    }
2812
2813    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
2814    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
2815    /// member the program asked to align as well, which is where the two differ. It is read
2816    /// at the closing brace of the body, so a line written in the middle of one settles the
2817    /// whole record rather than the members after it, and `push` and `pop` nest.
2818    #[test]
2819    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
2820        tast(concat!(
2821            "#pragma pack(1)\n",
2822            "struct A { char c; int i; };\n",
2823            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2824            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2825            "#pragma pack()\n",
2826            "struct B { char c; int i; };\n",
2827            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
2828            "#pragma pack(2)\n",
2829            "struct C { char c; int i; double d; };\n",
2830            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
2831            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
2832            // A member the program aligned, which `pack` caps and `packed` would not.
2833            "struct K { char c; int i __attribute__((aligned(8))); };\n",
2834            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
2835            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
2836            // The record's own `aligned` is not a member's, so it is not capped.
2837            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
2838            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
2839            "#pragma pack()\n",
2840            "#pragma pack(push, 1)\n",
2841            "struct D { char c; short s; };\n",
2842            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
2843            "#pragma pack(pop)\n",
2844            "struct E { char c; short s; };\n",
2845            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
2846            // Written in the middle of a body, and it still settles the whole record.
2847            "struct H { char c;\n",
2848            "#pragma pack(1)\n",
2849            "  int i; };\n",
2850            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
2851            "#pragma pack(1)\n",
2852            "struct I { char c;\n",
2853            "#pragma pack()\n",
2854            "  int i; };\n",
2855            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2856            "#pragma pack()\n",
2857            // Nested pushes, each one giving back what the one under it had.
2858            "#pragma pack(push, 8)\n",
2859            "#pragma pack(push, 1)\n",
2860            "struct P { char c; int i; };\n",
2861            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
2862            "#pragma pack(pop)\n",
2863            "struct Q { char c; int i; };\n",
2864            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
2865            "#pragma pack(pop)\n",
2866            // A cap above what every member already asks for changes nothing at all.
2867            "#pragma pack(16)\n",
2868            "struct R { char c; int i; };\n",
2869            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
2870            "#pragma pack()\n",
2871            "#pragma pack(1)\n",
2872            "struct S { char c; int i : 5; int j : 20; };\n",
2873            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
2874            "union T { char c; int i; };\n",
2875            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
2876            "#pragma pack()\n",
2877        ));
2878    }
2879
2880    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
2881    /// what GCC does with one, and these are its words for each of them. The last line is the
2882    /// one nothing else would reach, since it stands after every record in the file.
2883    #[test]
2884    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
2885        let result = run(
2886            &options(),
2887            concat!(
2888                "#pragma pack 4\n",
2889                "#pragma pack(pop)\n",
2890                "#pragma pack(3)\n",
2891                "#pragma pack(1) junk\n",
2892                "#pragma pack(push, 1\n",
2893                "#pragma pack(x)\n",
2894                // These two are well formed and say nothing. Zero is how a line asks for the
2895                // target's own alignments back without writing empty parentheses.
2896                "#pragma pack(0)\n",
2897                "#pragma pack(push)\n",
2898                "struct s { char c; int i; };\n",
2899                "#pragma pack(pop)\n",
2900                "#pragma pack(pop, foo)\n",
2901            ),
2902        );
2903        let expected = [
2904            "missing `(` after `#pragma pack` - ignored",
2905            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
2906            "alignment must be a small power of two, not 3",
2907            "junk at end of `#pragma pack`",
2908            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
2909            "unknown action `x` for `#pragma pack` - ignored",
2910            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
2911        ];
2912        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
2913        for (message, want) in result.messages.iter().zip(expected) {
2914            assert!(message.contains(want), "expected {want:?} in {message:?}");
2915        }
2916    }
2917
2918    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
2919    /// written first on that next line has to hand the line on rather than take it away. This
2920    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
2921    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
2922    /// Without it the pragma swallows the declaration, the program is left without it, and the
2923    /// only thing said about any of it is that there was junk on the pragma.
2924    #[test]
2925    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
2926        let result = run(
2927            &options(),
2928            concat!(
2929                "#pragma pack(push, 1)\n",
2930                "#pragma pack(pop)\n",
2931                "#define API\n",
2932                "API const char version[] = \"3.53.4\";\n",
2933                "const char *get(void) { return version; }\n",
2934            ),
2935        );
2936        assert!(result.messages.is_empty(), "{:?}", result.messages);
2937    }
2938
2939    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
2940    /// than as typedefs in a header, which is the only way a program that includes nothing at
2941    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
2942    #[test]
2943    fn the_wide_integer_answers_to_all_three_of_its_names() {
2944        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
2945        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
2946        assert!(text.contains("decl #1 b : __int128"), "{text}");
2947        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
2948    }
2949
2950    #[test]
2951    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
2952        // The point of a typed tree. The source has one operator and the output has the
2953        // widening that operator asked for, spelled out, so that nothing downstream has to
2954        // work out the conversion rules a second time.
2955        let text = tast("long f(int a, long b) { return a + b; }\n");
2956        assert!(text.contains("convert arithmetic"), "{text}");
2957    }
2958
2959    #[test]
2960    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
2961        for source in [
2962            "#error stop\n",
2963            "int f(void) { return 1 + ; }\n",
2964            "int f(void) { return undeclared; }\n",
2965        ] {
2966            let result = run(&options(), source);
2967            assert!(result.failed(), "expected this to fail:\n{source}");
2968            assert!(
2969                result.text().is_empty(),
2970                "a file that did not compile wrote a tree:\n{source}"
2971            );
2972        }
2973    }
2974
2975    #[test]
2976    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
2977        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
2978        // outside. Three uses of a name that was never declared, and the operators over them
2979        // say nothing at all.
2980        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
2981        assert_eq!(result.errors, 1, "{:?}", result.messages);
2982    }
2983
2984    #[test]
2985    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
2986        // The reason the checking is skipped after a failed parse. The parser gave up on the
2987        // first line and there is no `x` in the tree, so a checker run over it would report
2988        // every use of `x` below as undeclared, which is a second message about one mistake.
2989        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
2990        assert_eq!(result.errors, 1, "{:?}", result.messages);
2991    }
2992
2993    #[test]
2994    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
2995        let source = "int f(void) { char c = 300; return c; }\n";
2996        let plain = run(&options(), source);
2997        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
2998        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
2999        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
3000
3001        let mut opts = options();
3002        opts.warnings_are_errors = true;
3003        let strict = run(&opts, source);
3004        assert!(strict.failed());
3005        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
3006        for message in &strict.messages {
3007            assert!(!message.contains("warning:"), "{message}");
3008        }
3009    }
3010
3011    #[test]
3012    fn w_drops_the_warning_before_werror_can_promote_it() {
3013        let source = "int f(void) { char c = 300; return c; }\n";
3014        let mut opts = options();
3015        opts.warnings = false;
3016        let quiet = run(&opts, source);
3017        assert_eq!(quiet.messages, Vec::<String>::new());
3018        assert_eq!(quiet.errors, 0);
3019        assert!(!quiet.text().is_empty(), "and the file still compiles");
3020
3021        // A build that passes both means it wants neither, and the order it wrote them in is not
3022        // something to make it think about.
3023        opts.warnings_are_errors = true;
3024        let both = run(&opts, source);
3025        assert_eq!(both.messages, Vec::<String>::new());
3026        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
3027    }
3028
3029    #[test]
3030    fn the_dialect_reaches_the_keywords_and_the_checking() {
3031        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
3032        // and a mistake under the other, which is the keyword table being built per dialect.
3033        let source = "typeof(1) x;\n";
3034        let mut opts = options();
3035        opts.std = Std::C23;
3036        opts.gnu_extensions = false;
3037        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
3038
3039        opts.std = Std::C17;
3040        assert!(run(&opts, source).failed());
3041    }
3042
3043    #[test]
3044    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
3045        let mut opts = options();
3046        opts.emit = EmitKind::Object;
3047        let result = run(&opts, "int x = 1;\n");
3048        assert!(!result.failed(), "{:?}", result.messages);
3049        assert!(result.text().is_empty());
3050        // And it still finds what the checking finds, so a later kind on a broken file is not
3051        // a silent success.
3052        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
3053    }
3054
3055    /// The machine code of `source`, insisting that it compiled cleanly.
3056    fn mir(source: &str) -> String {
3057        let mut opts = options();
3058        opts.emit = EmitKind::MirFinal;
3059        let result = run(&opts, source);
3060        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3061        result.text().to_owned()
3062    }
3063
3064    /// The whole compiler in one assertion, which is what this emit kind is for.
3065    ///
3066    /// C in, machine instructions out, every register a real one and every frame offset a
3067    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
3068    /// checked here is that the passes are joined up and that the driver runs them.
3069    #[test]
3070    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
3071        let text = mir("int add(int a, int b) { return a + b; }\n");
3072        assert!(text.starts_with("mfunc @add {"), "{text}");
3073        assert!(text.contains("x64.add_rr_32"), "{text}");
3074        assert!(text.contains("x64.ret"), "{text}");
3075        // A virtual register is what the allocator was there to remove, so one left in the
3076        // output is the difference between code and something that looks like code.
3077        assert!(!text.contains('%'), "{text}");
3078    }
3079
3080    /// A declaration has no body, so there is nothing to generate for one and nothing is.
3081    #[test]
3082    fn a_function_with_no_body_produces_no_machine_function() {
3083        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
3084        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
3085        assert!(text.contains("mfunc @f {"), "{text}");
3086        assert!(text.contains("x64.call"), "{text}");
3087    }
3088
3089    /// Two functions come out in the order the module holds them, which is source order.
3090    #[test]
3091    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
3092        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
3093        let first = text.find("mfunc @a").expect("the first function");
3094        let second = text.find("mfunc @b").expect("the second function");
3095        assert!(first < second, "{text}");
3096    }
3097
3098    /// The target reaches the back end, so the same C is different instructions on Windows.
3099    #[test]
3100    fn the_target_decides_which_convention_the_generated_code_follows() {
3101        let mut opts = options();
3102        opts.emit = EmitKind::MirFinal;
3103        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3104        assert!(linux.contains("$rdi"), "{linux}");
3105
3106        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
3107        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3108        assert!(windows.contains("$rcx"), "{windows}");
3109        assert!(!windows.contains("$rdi"), "{windows}");
3110    }
3111
3112    /// And it reaches the front end, where it decides what an anonymous member is.
3113    ///
3114    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
3115    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
3116    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
3117    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
3118    /// drops it, which loses the names and the eight bytes the member takes up both.
3119    #[test]
3120    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
3121        let source = concat!(
3122            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
3123            "int size(void) { return sizeof(struct S); }\n",
3124            "int f(struct S *s) { s->i = 1; return s->i; }\n",
3125        );
3126
3127        let mut opts = options();
3128        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3129        let windows = run(&opts, source);
3130        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
3131
3132        let linux = run(&options(), source);
3133        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
3134        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
3135
3136        // And the flag answers for either of them, so a program built for Linux against a header
3137        // written for Windows can be read the way the header meant it.
3138        let mut opts = options();
3139        opts.ms_extensions = Some(true);
3140        let asked = run(&opts, source);
3141        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
3142    }
3143
3144    /// A target with no back end says so rather than generating something for another machine.
3145    #[test]
3146    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
3147        let mut opts = options();
3148        opts.emit = EmitKind::MirFinal;
3149        opts.target = "riscv64-unknown-linux-gnu".parse::<Triple>().unwrap();
3150        let result = run(&opts, "int f(int a) { return a; }\n");
3151        assert!(result.failed());
3152        assert!(result.messages[0].contains("no back end for riscv64"), "{:?}", result.messages);
3153        assert!(result.text().is_empty());
3154    }
3155
3156    /// AArch64 is written as its own assembly, with a function that calls keeping its return
3157    /// address in the frame record.
3158    #[test]
3159    fn an_aarch64_target_is_written_as_aarch64_assembly() {
3160        let mut opts = options();
3161        opts.emit = EmitKind::Asm;
3162        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3163        let source = "int g(int);\nint f(int a, int b) { return g(a) + b; }\n";
3164        let result = run(&opts, source);
3165        assert!(!result.failed(), "{:?}", result.messages);
3166        let text = result.text();
3167        for line in ["stp x29, x30, [sp, #-16]!", "mov x29, sp", "bl g", "ldp x29, x30, [sp], #16"]
3168        {
3169            assert!(text.contains(line), "{line} is not in\n{text}");
3170        }
3171        assert!(!text.contains('%'), "{text}");
3172    }
3173
3174    /// An object for AArch64, which is the listing read back by the assembler. The same object
3175    /// with debug information is refused rather than written without its line table.
3176    #[test]
3177    fn an_aarch64_target_reaches_an_object_file() {
3178        let mut opts = options();
3179        opts.emit = EmitKind::Object;
3180        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3181        let source = concat!(
3182            "int g(int);\n",
3183            "int table[4] = {1, 2, 3, 4};\n",
3184            "int f(int a, int b) { return g(a) + table[b & 3]; }\n",
3185        );
3186        let result = run(&opts, source);
3187        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3188        let bytes = match result.artifact {
3189            Artifact::Object { bytes, defines } => {
3190                assert_eq!(defines, ["f", "table"]);
3191                bytes
3192            }
3193            other => panic!("expected an object, got {other:?}"),
3194        };
3195        assert_eq!(&bytes[..4], b"\x7fELF");
3196        assert_eq!(&bytes[18..20], &183u16.to_le_bytes(), "EM_AARCH64");
3197
3198        // And with debug information, which the listing path builds from a label in front of
3199        // every instruction rather than refusing.
3200        opts.debug_info = true;
3201        let result = run(&opts, source);
3202        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3203        let bytes = match result.artifact {
3204            Artifact::Object { bytes, .. } => bytes,
3205            other => panic!("expected an object, got {other:?}"),
3206        };
3207        let has = |name: &[u8]| bytes.windows(name.len()).any(|at| at == name);
3208        assert!(has(b".debug_line\0") && has(b".debug_info\0"));
3209        assert!(!has(b"rucc_row"), "a row label reached the symbol table");
3210    }
3211
3212    /// gcc's AArch64 vector type names are there before any header, which glibc's `<math.h>`
3213    /// needs, a declaration can still hide one, and on x86-64 they are ordinary identifiers.
3214    #[test]
3215    fn the_aarch64_vector_type_names_are_declared_on_that_target_and_nowhere_else() {
3216        let mut opts = options();
3217        opts.emit = EmitKind::Asm;
3218        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3219        let source = "typedef __Float32x4_t f4;\n__SVFloat32_t sv(__SVFloat32_t, __SVBool_t);\n\
3220                      int n = sizeof(f4) + sizeof(__Int8x8_t);\n\
3221                      int f(f4 v) { int __Uint8x16_t = 3; return v[1] + __Uint8x16_t; }\n";
3222        let result = run(&opts, source);
3223        assert!(!result.failed(), "{:?}", result.messages);
3224        assert!(result.text().contains(".long\t24"), "{}", result.text());
3225        opts.target = "x86_64-unknown-linux-gnu".parse::<Triple>().unwrap();
3226        let result = run(&opts, "typedef __Float32x4_t f4;\n");
3227        assert!(result.failed());
3228        let result = run(&opts, "int __Float32x4_t = 1;\n");
3229        assert!(!result.failed(), "{:?}", result.messages);
3230    }
3231
3232    /// A structure too big for registers comes back through the address in x8, which AAPCS64 keeps
3233    /// apart from the arguments, so the argument after it is still in x0.
3234    #[test]
3235    fn an_aarch64_result_in_memory_is_reached_through_x8() {
3236        let mut opts = options();
3237        opts.emit = EmitKind::Asm;
3238        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3239        let source = "struct big { long a, b, c; };\nstruct big make(long v);\n\
3240                      long f(long v) { return make(v).c; }\n\
3241                      struct big g(long v) { struct big b = { v, v, v }; return b; }\n";
3242        let result = run(&opts, source);
3243        assert!(!result.failed(), "{:?}", result.messages);
3244        let text = result.text();
3245        assert!(text.contains("x8"), "{text}");
3246        assert!(text.contains("bl make"), "{text}");
3247    }
3248
3249    /// A remainder is two instructions on AArch64, the division and then a multiply subtract that
3250    /// reads the quotient the division wrote.
3251    #[test]
3252    fn an_aarch64_remainder_is_a_division_and_a_multiply_subtract() {
3253        let mut opts = options();
3254        opts.emit = EmitKind::Asm;
3255        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3256        let source = "int s(int a, int b) { return a % b; }\n\
3257                      unsigned long u(unsigned long a, unsigned long b) { return a % b; }\n";
3258        let result = run(&opts, source);
3259        assert!(!result.failed(), "{:?}", result.messages);
3260        let text = result.text();
3261        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3262        assert!(at("sdiv w") < at("msub w"), "{text}");
3263        assert!(at("udiv x") < at("msub x"), "{text}");
3264    }
3265
3266    /// A dense `switch` on AArch64 reads a cell of a table after the function with `adr` and
3267    /// `ldrsw`, and each cell is the distance from the table to an arm.
3268    #[test]
3269    fn an_aarch64_jump_table_is_reached_with_adr() {
3270        let mut opts = options();
3271        opts.emit = EmitKind::Asm;
3272        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3273        let source = "int f(int x) { switch (x) { case 0: return 10; case 1: return 21; \
3274                      case 2: return 32; case 3: return 43; case 4: return 54; case 5: return 65; \
3275                      case 6: return 76; case 7: return 87; case 8: return 98; case 9: return 9; \
3276                      case 10: return 19; case 11: return 29; default: return 0; } }\n";
3277        let result = run(&opts, source);
3278        assert!(!result.failed(), "{:?}", result.messages);
3279        let text = result.text();
3280        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3281        assert!(at("adr x") < at("ldrsw x"), "{text}");
3282        assert!(at("ldrsw x") < at("br x"), "{text}");
3283        assert!(text.contains("_j0:"), "{text}");
3284        assert!(text.contains(".long"), "{text}");
3285    }
3286
3287    /// An AArch64 Linux `va_start` fills in the five fields AAPCS64 gives a list. The two offsets
3288    /// count up to nothing from minus the size of what is left of each half of the save area, so
3289    /// with one integer named they start at minus fifty six and minus one hundred and twenty eight.
3290    #[test]
3291    fn an_aarch64_va_start_writes_the_five_fields_of_its_list() {
3292        let mut opts = options();
3293        opts.emit = EmitKind::Asm;
3294        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3295        let source = "typedef __builtin_va_list va_list;\n\
3296                      int f(int n, ...) { va_list ap; __builtin_va_start(ap, n); \
3297                      int x = __builtin_va_arg(ap, int); double d = __builtin_va_arg(ap, double); \
3298                      __builtin_va_end(ap); return x + (int)d; }\n";
3299        let result = run(&opts, source);
3300        assert!(!result.failed(), "{:?}", result.messages);
3301        let text = result.text();
3302        assert!(text.contains("#-56"), "{text}");
3303        assert!(text.contains("#-128"), "{text}");
3304        assert!(text.contains("#24]"), "{text}");
3305        assert!(text.contains("#28]"), "{text}");
3306        assert!(text.contains("str q"), "{text}");
3307    }
3308
3309    /// A `long double` on AArch64 Linux is a quad, moved with `ldr q` and `str q` and added with a
3310    /// call to the same routine libgcc has.
3311    #[test]
3312    fn an_aarch64_long_double_is_a_quad_in_a_vector_register() {
3313        let mut opts = options();
3314        opts.emit = EmitKind::Asm;
3315        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3316        let source = "void f(long double *p, long double x) { *p = *p + x; }\n";
3317        let result = run(&opts, source);
3318        assert!(!result.failed(), "{:?}", result.messages);
3319        let text = result.text();
3320        assert!(text.contains("ldr q"), "{text}");
3321        assert!(text.contains("str q"), "{text}");
3322        assert!(text.contains("__addtf3"), "{text}");
3323    }
3324
3325    /// A thread-local variable on AArch64 Linux is initial exec: its offset comes out of the
3326    /// global offset table, the thread pointer out of `tpidr_el0`, and one `add` joins them.
3327    #[test]
3328    fn an_aarch64_thread_local_is_reached_through_tpidr_el0() {
3329        let mut opts = options();
3330        opts.emit = EmitKind::Asm;
3331        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3332        let source = "__thread int n;\nint *f(void) { return &n; }\n\
3333                      void *g(void) { return __builtin_thread_pointer(); }\n";
3334        let result = run(&opts, source);
3335        assert!(!result.failed(), "{:?}", result.messages);
3336        let text = result.text();
3337        assert!(text.contains(":gottprel:n"), "{text}");
3338        assert!(text.contains(":gottprel_lo12:n]"), "{text}");
3339        assert_eq!(text.matches("mrs x").count(), 2, "{text}");
3340        assert!(text.contains("tpidr_el0"), "{text}");
3341    }
3342
3343    /// Apple's platforms reach a thread-local variable by calling through its descriptor, which
3344    /// is what clang writes on both machines, and the variable is the image and the descriptor.
3345    #[test]
3346    fn a_darwin_thread_local_is_reached_through_its_descriptor() {
3347        let source = "__thread int n = 5;\nint *f(void) { return &n; }\n";
3348        for (triple, wanted) in [
3349            ("aarch64-apple-darwin", &["_n@TLVPPAGE\n", "_n@TLVPPAGEOFF]\n", "\tblr x"][..]),
3350            ("x86_64-apple-darwin", &["_n@TLVP(%rip), %rdi\n", "\tcall\t*%"][..]),
3351        ] {
3352            let mut opts = options();
3353            opts.emit = EmitKind::Asm;
3354            opts.target = triple.parse::<Triple>().unwrap();
3355            let result = run(&opts, source);
3356            assert!(!result.failed(), "{triple}: {:?}", result.messages);
3357            let text = result.text();
3358            for want in wanted {
3359                assert!(text.contains(want), "{triple} wanted {want:?}:\n{text}");
3360            }
3361            assert!(text.contains("\n_n:\n\t.quad\t__tlv_bootstrap\n"), "{text}");
3362            assert!(!text.contains("tpidr_el0") && !text.contains("%fs"), "{text}");
3363        }
3364    }
3365
3366    /// The thread pointer itself is somewhere else on Apple's platforms and is still refused.
3367    #[test]
3368    fn the_thread_pointer_is_refused_on_darwin() {
3369        let mut opts = options();
3370        opts.emit = EmitKind::Asm;
3371        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3372        let result = run(&opts, "void *f(void) { return __builtin_thread_pointer(); }\n");
3373        assert!(result.failed());
3374        assert!(result.messages[0].contains("thread pointer"), "{:?}", result.messages);
3375    }
3376
3377    /// Darwin's list is a plain pointer and its variadic arguments are all on the stack, so a
3378    /// variadic definition saves no registers and its `va_start` stores one address.
3379    #[test]
3380    fn a_darwin_variadic_definition_saves_nothing_and_walks_the_stack() {
3381        let mut opts = options();
3382        opts.emit = EmitKind::Asm;
3383        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3384        let source = "int f(int n, ...) { __builtin_va_list ap; __builtin_va_start(ap, n);\n\
3385                      int r = __builtin_va_arg(ap, int); __builtin_va_end(ap); return r; }\n";
3386        let result = run(&opts, source);
3387        assert!(!result.failed(), "{:?}", result.messages);
3388        let text = result.text();
3389        assert!(!text.contains("str q"), "{text}");
3390        assert!(!text.contains("x7"), "{text}");
3391    }
3392
3393    /// A call on Darwin puts every argument past the named ones in memory, even with registers
3394    /// left over, so the `double` here is stored rather than put in `d0`.
3395    #[test]
3396    fn a_darwin_call_puts_its_variadic_arguments_in_memory() {
3397        let mut opts = options();
3398        opts.emit = EmitKind::Asm;
3399        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3400        let source = "int printf(const char *, ...);\n\
3401                      int g(double x) { return printf(\"%d %f\", 7, x); }\n";
3402        let result = run(&opts, source);
3403        assert!(!result.failed(), "{:?}", result.messages);
3404        let text = result.text();
3405        assert!(text.contains("str d0, [sp, #8]"), "{text}");
3406    }
3407
3408    /// Apple's assembler asks for part of an address after the name, a variable another image
3409    /// defines is read through the table because nothing copies it in, and the directive that
3410    /// makes a zeroed variable is also its definition, so its binding goes above it.
3411    #[test]
3412    fn a_darwin_listing_is_one_apples_assembler_reads() {
3413        let mut opts = options();
3414        opts.emit = EmitKind::Asm;
3415        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3416        let source = "extern int ext;\n\
3417                      int g[4];\n\
3418                      int f(int i) { return g[i] + ext; }\n";
3419        let result = run(&opts, source);
3420        assert!(!result.failed(), "{:?}", result.messages);
3421        let text = result.text();
3422        assert!(text.contains(", _g@PAGE\n"), "{text}");
3423        assert!(text.contains(", _g@PAGEOFF\n"), "{text}");
3424        assert!(text.contains(", _ext@GOTPAGE\n"), "{text}");
3425        assert!(text.contains(", _ext@GOTPAGEOFF]\n"), "{text}");
3426        assert!(!text.contains(":lo12:"), "{text}");
3427        assert!(text.contains("\t.globl\t_g\n\t.zerofill\t__DATA,__bss,_g,16,2\n"), "{text}");
3428    }
3429
3430    /// A `signed char` read from memory and added to at 32 bits is widened with its sign first.
3431    ///
3432    /// The widening was being taken out as unneeded, because its source is written as a `w`
3433    /// register and was taken to have 32 bits in it, so `*p + 1` added one to the byte `ldrb` had
3434    /// loaded and -9 came out as 248. At every level, since the pass runs at `-O0` too.
3435    #[test]
3436    fn a_signed_char_on_aarch64_is_widened_with_its_sign_before_it_is_added_to() {
3437        for target in ["aarch64-linux-gnu", "aarch64-apple-darwin"] {
3438            let mut opts = options();
3439            opts.emit = EmitKind::Asm;
3440            opts.target = target.parse::<Triple>().unwrap();
3441            let source = "int f(signed char *p) { return *p + 1; }\n\
3442                          unsigned g(unsigned short *p) { return *p + 1u; }\n";
3443            let result = run(&opts, source);
3444            assert!(!result.failed(), "{:?}", result.messages);
3445            let text = result.text();
3446            let signed = text.contains("\tsxtb w") || text.contains("\tldrsb w");
3447            assert!(signed, "{target}: {text}");
3448        }
3449    }
3450
3451    /// A construct the rule set does not reach yet is named, along with the function it is in.
3452    ///
3453    /// The message is about this compiler being unfinished rather than about the program, which
3454    /// is valid C either way, so it carries the note that says where the work is tracked. Both
3455    /// functions are attempted, so a file that is ahead of the back end in three places says so
3456    /// three times rather than one recompilation at a time.
3457    ///
3458    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
3459    /// stack pointer on, in a function whose frame also grows. The prologue would force the
3460    /// alignment and the array would move the stack pointer afterwards, and those are two frames
3461    /// that each want the one register the rest of the frame is counted from.
3462    #[test]
3463    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
3464        let mut opts = options();
3465        opts.emit = EmitKind::MirFinal;
3466        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3467                      s; s.x = 1; v[0] = s.x; }\n\
3468                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3469                      s; s.x = 1; v[0] = s.x; }\n";
3470        let result = run(&opts, source);
3471        assert!(result.failed());
3472        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
3473        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
3474        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
3475        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
3476        assert!(result.text().is_empty());
3477    }
3478
3479    /// A variable length array walks its pages under the flag that says every page is touched.
3480    ///
3481    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
3482    /// however many the size worked out to, so touching them is a loop written around the
3483    /// declaration rather than anything a prologue can do. What says the loop is there is the
3484    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
3485    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
3486    #[test]
3487    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
3488        let mut opts = options();
3489        opts.emit = EmitKind::MirFinal;
3490        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
3491        let plain = run(&opts, source);
3492        assert!(!plain.failed(), "{:?}", plain.messages);
3493        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
3494
3495        opts.stack_clash = true;
3496        let result = run(&opts, source);
3497        assert!(!result.failed(), "{:?}", result.messages);
3498        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
3499        assert!(result.text().contains("or_mi_8"), "{}", result.text());
3500    }
3501
3502    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
3503    ///
3504    /// The record that platform carries counts every slot in it from where the stack pointer ends
3505    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
3506    /// register pushed after the pointer was established has no row the format can write. The order
3507    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
3508    /// the back end writes there and only there. A variable length array and an `alloca` keep a
3509    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
3510    /// could not be compiled for that target at all. See tamnd/rucc#1403.
3511    #[test]
3512    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
3513        let mut opts = options();
3514        opts.emit = EmitKind::Object;
3515        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3516        let source = concat!(
3517            "void use(void *p);\n",
3518            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
3519            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
3520        );
3521        let result = run(&opts, source);
3522        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3523        let bytes = match result.artifact {
3524            Artifact::Object { bytes, .. } => bytes,
3525            other => panic!("expected an object, got {other:?}"),
3526        };
3527        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3528
3529        // And the same two functions for Linux, so that what the test is measuring is the target
3530        // rather than the program being one this compiler cannot reach yet.
3531        let mut opts = options();
3532        opts.emit = EmitKind::Object;
3533        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3534    }
3535
3536    /// The address of a name this file only declares, on the format with no table to read it out
3537    /// of.
3538    ///
3539    /// Every such name went into the table on every target, and COFF has no table, so the object
3540    /// writer was handed a relocation it has no way to write and refused the whole file. What the
3541    /// name stands for on this format is an address in the image whichever way the link supplies
3542    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
3543    /// the one that found it was a callback stored in a table of its own: a function passed as an
3544    /// argument, one put in a variable that lives past the call, and one called outright, which
3545    /// never needed the table and is here so the test says which of the three changed.
3546    #[test]
3547    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
3548        let source = concat!(
3549            "void other(void *p);\n",
3550            "void takes(void (*f)(void *));\n",
3551            "void (*held)(void *);\n",
3552            "void pass(void) { takes(other); }\n",
3553            "void keep(void) { held = other; }\n",
3554            "void call(void) { other(0); }\n",
3555        );
3556        let mut opts = options();
3557        opts.emit = EmitKind::Object;
3558        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3559        let result = run(&opts, source);
3560        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3561        let bytes = match result.artifact {
3562            Artifact::Object { bytes, .. } => bytes,
3563            other => panic!("expected an object, got {other:?}"),
3564        };
3565        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3566
3567        // And the same source for Linux, which does have a table and still uses it, so what this
3568        // measures is the format rather than the program.
3569        let mut opts = options();
3570        opts.emit = EmitKind::Object;
3571        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3572    }
3573
3574    /// An opcode the rule language has no word for is named anyway, and pointed at.
3575    ///
3576    /// The rule language's spelling is the better name when there is one, but an opcode it has
3577    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
3578    /// type is what makes the message say anything at all in the cases that happen. The span is
3579    /// the instruction's own, so the message lands on the line rather than on the file.
3580    ///
3581    /// The width of the float is what keeps the program refused. Everything else here is split into
3582    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
3583    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
3584    /// float on this target, the runtime has no conversion at that width because the back end has no
3585    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
3586    /// its wide values and reaches the selector the way every function of this width used to.
3587    #[test]
3588    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
3589        let mut opts = options();
3590        opts.emit = EmitKind::MirFinal;
3591        let source =
3592            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
3593        let result = run(&opts, source);
3594        assert!(result.failed());
3595        assert!(
3596            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
3597            "{result:?}"
3598        );
3599        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
3600        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
3601    }
3602
3603    /// The note names the issue tracker, which is where a reader finds out whether it is known.
3604    #[test]
3605    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
3606        let mut opts = options();
3607        opts.emit = EmitKind::MirFinal;
3608        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
3609        let result = run(&opts, source);
3610        assert!(result.failed());
3611        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
3612        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
3613        assert!(!note.contains("spec/17-milestones.md"), "{note}");
3614    }
3615
3616    /// The two frame flags reach the frame, which is the only thing either of them does.
3617    #[test]
3618    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
3619        let source = "int f(int a) { return a; }\n";
3620        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer when told so");
3621
3622        let mut opts = options();
3623        opts.emit = EmitKind::MirFinal;
3624        opts.frame_pointer = Some(true);
3625        let kept = run(&opts, source).text().to_owned();
3626        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
3627
3628        // Nothing said at -O0 is a frame pointer, which is what gcc keeps there.
3629        opts.frame_pointer = None;
3630        let kept = run(&opts, source).text().to_owned();
3631        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
3632    }
3633
3634    /// The assembly of `source`, insisting that it compiled cleanly.
3635    fn asm(source: &str) -> String {
3636        let mut opts = options();
3637        opts.emit = EmitKind::Asm;
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    /// `-S`, which is the same compiler as the kind above it with a different last step.
3644    ///
3645    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
3646    /// target's own description of what an instruction is. What is checked here is that a C file
3647    /// goes all the way to a listing an assembler would take, which means the directives around
3648    /// the function as well as the instructions in it.
3649    #[test]
3650    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
3651        let text = asm("int add(int a, int b) { return a + b; }\n");
3652        assert!(text.contains("\t.globl\tadd\n"), "{text}");
3653        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
3654        assert!(text.contains("\nadd:\n"), "{text}");
3655        assert!(text.contains("\taddl\t"), "{text}");
3656        assert!(text.contains("\tret\n"), "{text}");
3657        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
3658        // Without this the stack the program runs on is executable, which is not a default
3659        // anybody chose and is not a thing a reader would notice missing.
3660        assert!(text.contains(".note.GNU-stack"), "{text}");
3661    }
3662
3663    /// A call through a function pointer, which is a different instruction from a call to a name.
3664    ///
3665    /// Both are in the one function on purpose. What is being read is that the two calls are told
3666    /// apart all the way down: one carries a name the linker resolves and one carries a register,
3667    /// and neither turns into the other on the way.
3668    #[test]
3669    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
3670        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
3671        assert!(text.contains("\tcall\t*%"), "{text}");
3672        assert!(text.contains("\tcall\tg\n"), "{text}");
3673        // The address arrived in the first argument register and the argument the call passes has
3674        // to end up there, so the two cannot be the same register and the compiler has to have
3675        // moved one of them.
3676        assert!(text.contains("%rdi"), "{text}");
3677    }
3678
3679    /// A name at file scope, which is the one address a function cannot compute for itself. The
3680    /// `lea` that computes it is folded into the load that reads through it, so what is left to
3681    /// read is the addressing mode, which is where the instruction pointer shows up.
3682    #[test]
3683    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
3684        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
3685        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
3686    }
3687
3688    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
3689    ///
3690    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
3691    /// arm the comparison is true for and jumps to the other one. That is the half of this most
3692    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
3693    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
3694    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
3695    /// works until an address is above two gigabytes.
3696    #[test]
3697    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
3698        let arms = "return 1; return 2;";
3699        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
3700        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
3701            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
3702            assert!(
3703                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3704                "{operator}: {text}"
3705            );
3706            assert!(!text.contains("\tset"), "{operator}: {text}");
3707            assert!(!text.contains("\ttest"), "{operator}: {text}");
3708        }
3709        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
3710        for (operator, jump) in unsigned {
3711            let source =
3712                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
3713            let text = asm(&source);
3714            assert!(
3715                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3716                "{operator}: {text}"
3717            );
3718        }
3719
3720        // And against a constant, which is four comparisons in five and is where the saving
3721        // mostly is, since the byte that goes was the only reason the constant was in a register.
3722        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
3723        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
3724    }
3725
3726    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
3727    ///
3728    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
3729    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
3730    /// so this is here to say that what was taken out was taken out of one place and not two.
3731    #[test]
3732    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
3733        let text = asm("int f(int a, int b) { return a < b; }\n");
3734        assert!(text.contains("\tsetl\t"), "{text}");
3735    }
3736
3737    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
3738    fn optimized(source: &str) -> String {
3739        let mut opts = options();
3740        opts.emit = EmitKind::Asm;
3741        opts.opt_level = rucc_session::OptLevel::O2;
3742        let result = run(&opts, source);
3743        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3744        result.text().to_owned()
3745    }
3746
3747    /// What each `switch` became is an `-fopt-info` remark, and `-Zswitch=` changes what it says.
3748    #[test]
3749    fn opt_info_says_what_each_switch_became_and_a_forced_shape_is_what_it_says() {
3750        let arms: String = (0..40)
3751            .map(|k| format!("case {}: return g({k});", k * 17))
3752            .collect::<Vec<_>>()
3753            .join(" ");
3754        let source = format!("int g(int);\nint f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n");
3755        let said = |shape: Option<&str>| {
3756            let mut opts = options();
3757            opts.emit = EmitKind::Asm;
3758            opts.opt_level = rucc_session::OptLevel::O2;
3759            opts.opt_info = vec![String::new()];
3760            opts.switch_shape = shape.map(str::to_owned);
3761            let result = run(&opts, &source);
3762            assert_eq!(result.messages, Vec::<String>::new());
3763            let lines: Vec<String> = result
3764                .remarks
3765                .lines()
3766                .filter(|line| line.contains("[switch-lowering]"))
3767                .map(str::to_owned)
3768                .collect();
3769            assert_eq!(lines.len(), 1, "{}", result.remarks);
3770            lines[0].clone()
3771        };
3772        assert!(said(None).contains(": f: optimized: switch of 40 cases lowered as a tree;"));
3773        assert!(said(Some("table")).contains("lowered as a table;"));
3774        assert!(said(Some("walk")).contains("lowered as a walk;"));
3775    }
3776
3777    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
3778    ///
3779    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
3780    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
3781    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
3782    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
3783    ///
3784    /// The comparison is unsigned because the range check is the label minus the lowest one, which
3785    /// is a count and not a number the program wrote.
3786    #[test]
3787    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
3788        let arms: String =
3789            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
3790        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3791        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
3792        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
3793        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
3794    }
3795
3796    /// The same `switch` with one arm off the line, which is a table and not arithmetic.
3797    ///
3798    /// The answers being a line is what licenses the addition, since it answers for every label in
3799    /// the range at once. One label whose arm disagrees is a label it would answer wrongly, so this
3800    /// is here to say that the pass is reading the arms and not counting the labels. What it does
3801    /// instead is look the answer up: one comparison, no jump through a jump table, and the arm off
3802    /// the line is a cell of a constant array in `.rodata`, which is gcc's `CSWTCH` and its shape.
3803    #[test]
3804    fn a_dense_switch_whose_arms_are_not_a_line_is_a_load_from_a_table() {
3805        let arms: String = (0..16)
3806            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
3807            .collect::<Vec<_>>()
3808            .join(" ");
3809        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3810        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
3811        assert!(!text.contains("\tjmp\t*"), "{text}");
3812        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
3813        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
3814        let section = text[..text.find("CSWTCH.0:").unwrap_or(0)].rfind("\t.section\t.rodata");
3815        assert!(section.is_some(), "{text}");
3816        assert_eq!(table.matches("\t.long\t").count(), 16, "{text}");
3817        assert!(table.contains("\t.long\t100\n"), "{text}");
3818    }
3819
3820    /// A `switch` whose arms give string literals is a table of how far each string is from it.
3821    ///
3822    /// gcc 16 keeps the compares here, because its table would hold addresses the loader has to
3823    /// write when the program starts, and that table would have to be in `.data.rel.ro`. This one
3824    /// holds four byte distances the linker writes once, so it stays in `.rodata` with the strings.
3825    #[test]
3826    fn a_switch_whose_arms_give_strings_is_a_table_of_how_far_away_they_are() {
3827        let text = optimized(
3828            "const char *f(int k) { switch (k) { case 0: return \"zero\"; \
3829             case 1: return \"one\"; case 2: return \"two\"; case 3: return \"three\"; } \
3830             return \"many\"; }\n",
3831        );
3832        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
3833        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
3834        assert!(!text.contains(".data.rel.ro"), "{text}");
3835        let at = text.find("CSWTCH.0:").expect("the table is in the output");
3836        assert!(text[..at].rfind("\t.section\t.rodata").is_some(), "{text}");
3837        let table = &text[at..];
3838        assert_eq!(table.matches(" - .\n").count(), 4, "{text}");
3839        assert!(table.contains("\t.long\t.Lstr.1+4 - .\n"), "{text}");
3840    }
3841
3842    /// The same table at `-Os`, where a cell is a byte because every answer fits in one.
3843    ///
3844    /// gcc 16 narrows the cells at `-Os` and not at `-O2`, and so does rucc: sixteen answers under a
3845    /// hundred and twenty eight are sixteen bytes rather than sixty four, and the byte is widened
3846    /// back with its sign.
3847    #[test]
3848    fn a_table_at_os_has_cells_as_narrow_as_its_answers() {
3849        let arms: String = (0..16)
3850            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
3851            .collect::<Vec<_>>()
3852            .join(" ");
3853        let mut opts = options();
3854        opts.emit = EmitKind::Asm;
3855        opts.opt_level = rucc_session::OptLevel::Os;
3856        let result = run(&opts, &format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3857        assert_eq!(result.messages, Vec::<String>::new());
3858        let text = result.text();
3859        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
3860        assert_eq!(table.matches("\t.byte\t").count(), 16, "{text}");
3861        assert!(text.contains("\tmovsbl\t"), "{text}");
3862    }
3863
3864    /// A table whose labels are every value the switched value can hold, which is the range check
3865    /// `rucc_opt::prune` takes out.
3866    ///
3867    /// The operand is `x & 3` and all four values are cases, so the `return -1` is dead. With the
3868    /// default out of the switch every case goes to the load, the switch is a jump, and what is
3869    /// left is the mask and the load with no compare in front of it.
3870    #[test]
3871    fn a_table_that_covers_its_operand_has_no_range_check() {
3872        let text = optimized(
3873            "int f(unsigned x) { switch (x & 3) { case 0: return 5; case 1: return 9; \
3874             case 2: return 2; case 3: return 7; } return -1; }\n",
3875        );
3876        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
3877        assert!(!text.contains("\tcmp"), "{text}");
3878        assert!(!text.contains("$-1"), "{text}");
3879    }
3880
3881    /// A store one path makes to a local the loop has just read, which GCC also turns into a
3882    /// conditional move and an unconditional store. The branch was on data, so it was the one the
3883    /// machine gets wrong half the time. The move reads the flags of the comparison itself, so no
3884    /// byte is set and tested in between.
3885    #[test]
3886    fn a_store_to_a_local_the_loop_just_read_is_a_conditional_move() {
3887        let text = optimized(
3888            "int f(const int *v, int n, int k) { int best[8] = {0}; \
3889             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; \
3890             return best[k & 7]; }\n",
3891        );
3892        assert!(text.contains("\tcmovgl"), "{text}");
3893        assert!(!text.contains("\tset"), "{text}");
3894        assert!(!text.contains("\ttestb"), "{text}");
3895    }
3896
3897    /// The same loop on a global keeps its branch, because another thread may own the slot.
3898    #[test]
3899    fn a_store_to_a_global_the_loop_just_read_keeps_its_branch() {
3900        let text = optimized(
3901            "int best[8]; void f(const int *v, int n) { \
3902             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; }\n",
3903        );
3904        assert!(!text.contains("\tcmov"), "{text}");
3905    }
3906
3907    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
3908    /// `rucc_opt::fold` does with floating point.
3909    ///
3910    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
3911    /// what has to see it. Load forwarding turns the local back into the constant that was stored
3912    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
3913    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
3914    #[test]
3915    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
3916        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
3917        assert!(text.contains("movl\t$2, %eax"), "{text}");
3918        assert!(!text.contains("cvttsd2si"), "{text}");
3919    }
3920
3921    /// A slot of a `const` table read at an index the optimizer works out, which is what
3922    /// `rucc_opt::image` is for.
3923    ///
3924    /// The subscript is not a constant expression and the front end does not fold it. What it
3925    /// writes is the index sign extended, multiplied by four and added to the address of the
3926    /// table, so the offset only exists once `fold` has run and the load only folds after that.
3927    /// What came out before was a `movl t+8(%rip), %eax`.
3928    #[test]
3929    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
3930        let text =
3931            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
3932        assert!(text.contains("movl\t$30, %eax"), "{text}");
3933        assert!(!text.contains("t(%rip)"), "{text}");
3934    }
3935
3936    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
3937    /// scalars an `int` array is written as.
3938    #[test]
3939    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
3940        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
3941        assert!(text.contains("movl\t$98, %eax"), "{text}");
3942    }
3943
3944    /// A global something can write to, which is the condition the fold turns on and therefore
3945    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
3946    /// store that ran last and the load has to happen.
3947    #[test]
3948    fn a_table_that_is_not_read_only_keeps_its_load() {
3949        let text = optimized(
3950            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
3951        );
3952        assert!(!text.contains("movl\t$30, %eax"), "{text}");
3953    }
3954
3955    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
3956    ///
3957    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
3958    /// false, so the program links exactly when the call has been folded away. Getting there is
3959    /// three folds standing on each other: the load of the `const double`, the conversion of it to
3960    /// an `int`, and the comparison against one.
3961    #[test]
3962    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
3963        let text = optimized(
3964            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
3965        );
3966        assert!(!text.contains("call\tlink_error"), "{text}");
3967    }
3968
3969    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
3970    #[test]
3971    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
3972        let text = asm("long f(void *p) { return (long)p; }\n");
3973        // Every instruction in the body is a full width move or the return. The copies are the
3974        // allocator taking no hints, and what matters here is what is not among them: nothing
3975        // narrows the value and nothing widens it again, which is what a cast that did something
3976        // would look like.
3977        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
3978            let mnemonic = line.split_whitespace().next().unwrap_or("");
3979            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
3980        }
3981    }
3982
3983    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
3984    /// where that memory is depends on what the prologue did, so this is checked at the end of the
3985    /// pipeline rather than in the middle of it.
3986    #[test]
3987    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
3988        let six = "long a, long b, long c, long d, long e, long f";
3989        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
3990
3991        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
3992        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
3993        // reads them from too, at `-O0`, though it reads them in three instructions where this
3994        // reads them in two: the second read is the addition's own memory operand, which is
3995        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
3996        // load before the two were put together.
3997        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
3998        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
3999
4000        // A narrower one is read at its own width, because the bits above it are bits the
4001        // convention says nothing about, and one in the other register file with the other file's
4002        // instruction.
4003        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
4004        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
4005        let eight =
4006            "double a, double b, double c, double d, double e, double f, double g, double h";
4007        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
4008        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
4009    }
4010
4011    /// The other end of the same thing. What the caller writes is at the stack pointer, because
4012    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
4013    #[test]
4014    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
4015        let six = "1, 2, 3, 4, 5, 6";
4016        let decl = "long g(long, long, long, long, long, long, long, long);\n";
4017        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
4018
4019        assert!(text.contains("\tmovq\t%"), "{text}");
4020        assert!(text.contains(", (%rsp)\n"), "{text}");
4021        assert!(text.contains(", 8(%rsp)\n"), "{text}");
4022        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
4023        assert!(text.contains("\tsubq\t$"), "{text}");
4024
4025        // A narrower one is written at its own width, matching what the callee reads it back with.
4026        let narrow = "int g(int, int, int, int, int, int, int);\n";
4027        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
4028        assert!(text.contains("\tmovl\t%"), "{text}");
4029        assert!(text.contains(", (%rsp)\n"), "{text}");
4030    }
4031
4032    /// The count a variadic callee on this convention reads is a count of vector registers, so a
4033    /// float that ran out of them and went to memory is not in it.
4034    #[test]
4035    fn a_variadic_call_counts_registers_and_not_arguments() {
4036        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
4037        let decl = "int g(int, ...);\n";
4038        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
4039
4040        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
4041        assert!(text.contains("\tmovsd\t%"), "{text}");
4042        assert!(text.contains(", (%rsp)\n"), "{text}");
4043    }
4044
4045    /// The callee's half of the same convention. Every argument register it was handed is written
4046    /// into its frame on the way in, because which of them hold anything is a thing only the caller
4047    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
4048    /// past them and nothing ever reads their slots.
4049    #[test]
4050    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
4051        let body =
4052            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
4053        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
4054
4055        // Five general purpose registers and eight vector ones, since the one parameter the
4056        // signature names took the first of the six.
4057        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
4058        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
4059        assert!(!text.contains(", 0(%r"), "{text}");
4060        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
4061        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
4062        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
4063        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
4064
4065        // And the area is one of the function's own stack objects, so the frame holds it.
4066        assert!(text.contains("\tsubq\t$"), "{text}");
4067    }
4068
4069    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
4070    /// where the arguments the signature names left the walk over each file's registers.
4071    #[test]
4072    fn va_start_writes_the_four_fields_the_psabi_describes() {
4073        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
4074        let params = "int a, int b, int c, double d";
4075        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
4076
4077        // Three integers took three of the six general purpose registers, and one double took one
4078        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
4079        // sixteen bytes into the second, which begins at forty eight.
4080        assert!(text.contains("	movl	$24, "), "{text}");
4081        assert!(text.contains("	movl	$64, "), "{text}");
4082        // The other two fields are addresses rather than numbers, so each is stored as a word and
4083        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
4084        // arguments are and is the only thing in this function that is not below the stack pointer.
4085        assert!(text.contains(", 8(%r"), "{text}");
4086        assert!(text.contains(", 16(%r"), "{text}");
4087        let frame: u32 = text
4088            .lines()
4089            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
4090            .expect("a variadic function takes a frame for the save area");
4091        let above = |line: &str| {
4092            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
4093            Some(at > frame)
4094        };
4095        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
4096    }
4097
4098    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
4099    /// of the two halves it walks is the type's answer.
4100    #[test]
4101    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
4102        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
4103        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
4104        let text = asm(&ints);
4105
4106        // The last general purpose slot begins at forty, so an offset above it is an argument the
4107        // caller left in its own memory instead.
4108        assert!(text.contains("$40, "), "{text}");
4109        assert!(text.contains("	cmpl	"), "{text}");
4110        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
4111        // of the comparison the front end wrote, because the block falls into the half taken when
4112        // the argument is still in the save area and jumps to the other one.
4113        assert!(text.contains("	ja	"), "{text}");
4114
4115        let arg = "__builtin_va_arg(ap, double)";
4116        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
4117        assert!(text.contains("$160, "), "the last vector slot: {text}");
4118    }
4119
4120    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
4121    /// moves rather than a call to a library this compiler has no way to reach yet.
4122    #[test]
4123    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
4124        let decl = "struct pair { long a, b; };\n";
4125        let body = "struct pair p = *q; return p.a + p.b;";
4126        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
4127
4128        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
4129        assert!(!text.contains("\tcall"), "{text}");
4130        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
4131        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
4132    }
4133
4134    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
4135    /// a byte at a time and a structure of longs eight bytes at a time.
4136    #[test]
4137    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
4138        let decl = "struct bytes { char a[8]; };\n";
4139        let body = "struct bytes p = *q; return p.a[0];";
4140        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
4141
4142        // Eight bytes aligned to one is eight words, and each is a load and a store.
4143        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
4144    }
4145
4146    /// What an initialiser does not name is zero, which the front end writes as a fill and this
4147    /// writes as the byte spread across each word.
4148    #[test]
4149    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
4150        let decl = "struct wide { long a, b, c; };\n";
4151        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
4152
4153        assert!(!text.contains("memset"), "nothing calls the library: {text}");
4154        // Either spelling of a zero in a register, the move of one or the exclusive or of the
4155        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
4156        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
4157        // the register it does not write is cleared rather than left alone.
4158        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
4159    }
4160
4161    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
4162    /// a hosted target and `rucc-builtins` on a freestanding one.
4163    #[test]
4164    fn a_copy_too_large_to_unroll_calls_the_runtime() {
4165        let decl = "struct huge { char a[4096]; };\n";
4166        let mut opts = options();
4167        opts.emit = EmitKind::Asm;
4168        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
4169        let result = run(&opts, &source);
4170        assert!(!result.failed(), "{:?}", result.messages);
4171        let text = result.text();
4172        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
4173        // The size in the register the convention passes the third argument in, which is what
4174        // says the call was built from the convention and not from the shape of the IR.
4175        assert!(text.contains("4096"), "the size travels: {text}");
4176    }
4177
4178    /// And an object passed by value with more words in it than that is the same call again,
4179    /// written in front of the call the object is an argument of.
4180    ///
4181    /// The copy is one the caller owes the callee, since the callee is free to write to what it
4182    /// was handed, so it is not an optimization that the size decides but the only way the call
4183    /// can be made at all.
4184    #[test]
4185    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
4186        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
4187        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
4188
4189        let copy = text.find("call\tmemcpy").expect("the copy");
4190        let call = text.find("call\ttake").expect("the call");
4191        assert!(copy < call, "the copy comes first: {text}");
4192        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
4193        // with the size in the register the convention passes the third argument in. The address
4194        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
4195        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
4196        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
4197        assert!(text.contains("$4096, %edx"), "the size: {text}");
4198    }
4199
4200    /// A frame that had to force its own alignment cannot say how far away the caller's stack
4201    /// pointer was, so it reaches back through the frame pointer instead.
4202    #[test]
4203    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
4204        let six = "long a, long b, long c, long d, long e, long f";
4205        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
4206        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
4207
4208        // The frame pointer is saved and pointed at where it was saved before the alignment is
4209        // forced, so the caller's arguments stay a constant distance from it: one word for the
4210        // saved frame pointer and one for the return address.
4211        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
4212        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
4213        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
4214    }
4215
4216    /// The object format decides the directives, and the target decides the object format.
4217    #[test]
4218    fn the_target_decides_how_the_assembly_is_spelled() {
4219        let mut opts = options();
4220        opts.emit = EmitKind::Asm;
4221        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4222        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
4223        assert!(text.contains("__TEXT,__text"), "{text}");
4224        assert!(text.contains("\n_f:\n"), "{text}");
4225        assert!(!text.contains(".note.GNU-stack"), "{text}");
4226    }
4227
4228    /// The object file of `source`, insisting that it compiled cleanly.
4229    fn obj(source: &str) -> Vec<u8> {
4230        let mut opts = options();
4231        opts.emit = EmitKind::Object;
4232        let result = run(&opts, source);
4233        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4234        match result.artifact {
4235            Artifact::Object { bytes, .. } => bytes,
4236            other => panic!("expected an object, got {other:?}"),
4237        }
4238    }
4239
4240    /// `-c`, which is the last step of the three the back end can end with.
4241    ///
4242    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
4243    /// that a C file goes all the way to one, which is the whole compiler in one line and the
4244    /// thing that stops working when a layer between them changes its mind about something.
4245    #[test]
4246    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
4247        let bytes = obj("int add(int a, int b) { return a + b; }\n");
4248        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
4249        let text = asm("int add(int a, int b) { return a + b; }\n");
4250        assert!(
4251            text.contains("\taddl\t"),
4252            "and the listing of it is the same instructions:\n{text}"
4253        );
4254    }
4255
4256    /// A variable this file defines, which is what a reference to one has to resolve against.
4257    #[test]
4258    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
4259        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
4260        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
4261        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
4262        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
4263        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
4264        // announced to the linker at all, which is the whole of what `static` means here.
4265        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
4266        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
4267        assert!(!text.contains(".globl\thidden"), "{text}");
4268        // Nothing writes through it, so it goes in a page the loader can map read only and every
4269        // process running the program can share.
4270        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4271    }
4272
4273    /// A bit-field with a value in it, which is written as the bytes the value lands in.
4274    ///
4275    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
4276    /// initializer makes are put together first and then taken back out as the run they make,
4277    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
4278    /// used to end the object up in `.bss` with the rest of its value thrown away.
4279    #[test]
4280    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
4281        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
4282        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
4283        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
4284
4285        // Two fields, the first of them zero, which is the same thing said with the zero byte
4286        // inside the run rather than at the front of it.
4287        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
4288        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
4289
4290        // Wider than an `int`, which is the same code and is worth saying because the value no
4291        // longer fits in the thirty two bits a bit-field used to be read at.
4292        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
4293        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
4294
4295        // Nothing in it, which still costs no bytes in the file.
4296        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
4297        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
4298        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
4299    }
4300
4301    /// A string literal, which is a variable the program never named.
4302    #[test]
4303    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
4304        let text = asm("const char *f(void) { return \"hi\"; }\n");
4305        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
4306        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4307        let label = text
4308            .lines()
4309            .find(|line| line.starts_with(".Lstr"))
4310            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
4311        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
4312    }
4313
4314    /// A variable holding the address of another one, which is the only hole an image has in it.
4315    #[test]
4316    fn an_address_in_an_initializer_is_left_to_the_linker() {
4317        let source = "int counter;\nint *p = &counter;\n";
4318        let text = asm(source);
4319        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
4320        // And in the object it is eight zero bytes and a relocation, which is what the two paths
4321        // being one description is for.
4322        let bytes = obj(source);
4323        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
4324    }
4325
4326    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
4327    ///
4328    /// The table is const so nothing in the program writes it, but the addresses in it are not
4329    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
4330    /// leaves a relocation in a section that is never writable, and what the linker does about
4331    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
4332    /// exactly as long as the loader is writing it and read only afterwards, which is what the
4333    /// program asked for in the first place.
4334    #[test]
4335    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
4336        // Both names are `static` and both are defined here, so nothing else can be the one that
4337        // defines them and the linker may lay the table out in the first pages of the segment.
4338        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
4339             struct m { void (*x)(void); void (*y)(void); };\n\
4340             const struct m t = { a, b };\n");
4341        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
4342        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
4343
4344        // One name this file only declares is enough to lose the `.local` half, because a name the
4345        // link resolves from somewhere else is one another object may turn out to define.
4346        let text =
4347            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
4348        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
4349
4350        // And a constant with no address in it stays exactly where it was.
4351        let text = asm("const int fixed = 7;\n");
4352        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4353    }
4354
4355    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
4356    ///
4357    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
4358    /// definition with no way to reach it is a variable nothing can read, and a reference with no
4359    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
4360    /// read as though it were an ordinary global and every thread quietly shares one copy.
4361    #[test]
4362    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
4363        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
4364        // The storage: the section the loader makes a copy of for every thread, and the symbol
4365        // type that makes a linker refuse an ordinary relocation aimed at it.
4366        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
4367        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
4368        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
4369        // this thread's block is, out of the segment register.
4370        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
4371        assert!(text.contains("%fs:0"), "{text}");
4372    }
4373
4374    /// The second half of that on its own, which is what a program asks for when the number it
4375    /// wants is the thread rather than anything in it.
4376    ///
4377    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
4378    /// between that library and a build. gcc 16 writes the same one instruction.
4379    #[test]
4380    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
4381        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
4382        assert!(text.contains("movq\t%fs:0, "), "{text}");
4383        // No table slot and no addition, because there is no variable to find inside the block.
4384        assert!(!text.contains("GOTTPOFF"), "{text}");
4385    }
4386
4387    /// The four hints and the one thing that decides between them, which is the locality.
4388    ///
4389    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
4390    /// effect: the program runs the same whichever of the four it gets, and the whole point of
4391    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
4392    /// programs, measured on x86-64 rather than read off a manual.
4393    ///
4394    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
4395    /// writes it only when the command line says the part has it, so a prefetch for a write is the
4396    /// same instruction as a prefetch for a read, which is the fourth line here.
4397    #[test]
4398    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
4399        for (locality, wanted) in
4400            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
4401        {
4402            let source =
4403                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
4404            let text = asm(&source);
4405            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
4406        }
4407        // The one argument form, which means a read that wants all of the data afterwards.
4408        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
4409        assert!(text.contains("\tprefetcht0\t"), "{text}");
4410        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
4411        // instruction as the read above.
4412        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
4413        assert!(text.contains("\tprefetcht0\t"), "{text}");
4414        assert!(!text.contains("prefetchw"), "{text}");
4415    }
4416
4417    /// The same eight programs on AArch64, where the write hint is in the base instruction set and
4418    /// so is a different instruction, which is what gcc 16.2.0 writes for them.
4419    #[test]
4420    fn an_aarch64_prefetch_is_a_prfm_that_says_the_locality_and_whether_it_writes() {
4421        let mut opts = options();
4422        opts.emit = EmitKind::Asm;
4423        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4424        for (write, kind) in [(0, "pld"), (1, "pst")] {
4425            for (locality, wanted) in [(0, "l1strm"), (1, "l3keep"), (2, "l2keep"), (3, "l1keep")] {
4426                let source = format!(
4427                    "void warm(void *p) {{ __builtin_prefetch(p, {write}, {locality}); }}\n"
4428                );
4429                let result = run(&opts, &source);
4430                assert_eq!(result.messages, Vec::<String>::new(), "{source}");
4431                let text = result.text();
4432                assert!(text.contains("prfm"), "{source}{text}");
4433                assert!(text.contains(&format!("{kind}{wanted}, [x0]")), "{source}{text}");
4434            }
4435        }
4436    }
4437
4438    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
4439    ///
4440    /// What is checked is the instruction and not any effect, because the effect is a fault and a
4441    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
4442    /// program, and it is not a call, which is the half that matters in a kernel and in a
4443    /// freestanding program: neither has an `abort` for a call to reach.
4444    ///
4445    /// The second half is the block going on after it. A statement written under a stop is
4446    /// compiled the way it would have been without one, so the addition is still there, and that
4447    /// is the front end declining to treat a stop as the end of a path.
4448    #[test]
4449    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
4450        let text = asm("void stop(void) { __builtin_trap(); }\n");
4451        assert!(text.contains("\tud2\n"), "{text}");
4452        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
4453
4454        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
4455        assert!(text.contains("\tud2\n"), "{text}");
4456        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
4457    }
4458
4459    /// The promise about the low bits of an address, whose value is the address.
4460    ///
4461    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
4462    /// its first argument and no instruction at all. The claim worth checking end to end is that
4463    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
4464    /// object file defines, which is how this one used to fail to link out of glibc's string
4465    /// headers.
4466    ///
4467    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
4468    /// every optimization level even though it has folded the call away. A constant has nothing to
4469    /// run and is dropped, and a call does, so the second half asks for the callee by name.
4470    #[test]
4471    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
4472        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
4473        assert!(!text.contains("assume_aligned"), "{text}");
4474        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
4475
4476        let source = "unsigned long width(void);\n\
4477                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
4478        let text = asm(source);
4479        assert!(!text.contains("assume_aligned"), "{text}");
4480        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
4481    }
4482
4483    /// Where a frame is, which on this machine is what the frame pointer holds.
4484    ///
4485    /// The first half is a function that would have kept no frame pointer at all, since it is a
4486    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
4487    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
4488    ///
4489    /// The second half is the walk. Each link above zero is one load through the register the last
4490    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
4491    /// 16.2.0 writes for the same programs at `-O2`.
4492    #[test]
4493    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
4494        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
4495        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
4496        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
4497        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
4498
4499        let walk = |depth: u32| {
4500            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
4501            asm(&source).matches("movq\t(%r").count()
4502        };
4503        assert_eq!(walk(1), 1, "one link is one load");
4504        assert_eq!(walk(3), 3, "three links are three loads");
4505    }
4506
4507    /// The address a frame returns to, which is one word above the frame the walk ended at.
4508    ///
4509    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
4510    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
4511    /// frame pointer points at is the link and what is above it is where control goes back to.
4512    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
4513    ///
4514    /// The second half is the same walk the frame address does, with the load at the end of it
4515    /// reading one word further along rather than the register itself being the answer.
4516    #[test]
4517    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
4518        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
4519        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
4520        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
4521        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
4522
4523        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
4524        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
4525        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
4526    }
4527
4528    /// A depth that is not a constant is refused, and so is one past the limit.
4529    ///
4530    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
4531    /// links long, written out, so a number that is not known until the program runs has nothing
4532    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
4533    /// program.
4534    ///
4535    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
4536    /// this refuses a depth no program has a use for rather than filling an object file with loads
4537    /// that fault part way up.
4538    #[test]
4539    fn a_depth_that_is_not_a_small_constant_is_refused() {
4540        let mut opts = options();
4541        opts.emit = EmitKind::Ir;
4542        for source in [
4543            "void *up(int n) { return __builtin_return_address(n); }\n",
4544            "void *up(void) { return __builtin_frame_address(1000); }\n",
4545        ] {
4546            let messages = run(&opts, source).messages;
4547            let named = messages.iter().any(|m| m.contains("E0705"));
4548            assert!(named, "expected a refusal in {messages:?}");
4549        }
4550    }
4551
4552    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
4553    /// moved to.
4554    ///
4555    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
4556    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
4557    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
4558    /// is about how the rounding is written rather than about what it answers.
4559    ///
4560    /// There is no call anywhere in either program. An alloca that had reached the linker would
4561    /// have found the C library's, which is a real function with a real frame and is not what a
4562    /// program writing the builtin asked for.
4563    #[test]
4564    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
4565        let text =
4566            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
4567        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
4568        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
4569        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4570
4571        // The plain name, which a program that declares it the way the C library does means the
4572        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
4573        let plain = concat!(
4574            "extern void *alloca(__SIZE_TYPE__);\n",
4575            "void use(void *p);\n",
4576            "void f(unsigned long n) { use(alloca(n)); }\n",
4577        );
4578        let text = asm(plain);
4579        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
4580        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
4581
4582        // And a program that means something of its own by the name keeps it, which is what the
4583        // declaration is looked at for.
4584        let own = concat!(
4585            "static void *alloca(unsigned long n) { return 0; }\n",
4586            "void *f(unsigned long n) { return alloca(n); }\n",
4587        );
4588        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
4589    }
4590
4591    /// A name nothing declared that the implementation knows the type of is declared with that
4592    /// type rather than with the `extern int f()` C89 6.3.2.2 writes down.
4593    ///
4594    /// That is gcc's rule and it is measurable: gcc 16.2.0 compiles an undeclared `alloca` with
4595    /// no call in it at all, and says `incompatible implicit declaration of built-in function`
4596    /// beside the implicit declaration warning. A C89 declaration would have made the call return
4597    /// an `int` and reach a function no C library defines, since every header that offers
4598    /// `alloca` offers it as a macro for the builtin. Four torture programs turn on it,
4599    /// `execute/20020314-1.c`, `20040223-1.c`, `941202-1.c` and `pr22061-1.c`, each of which
4600    /// calls `alloca` with nothing above it.
4601    ///
4602    /// The rule is the builtin table's rather than this one name's, so an undeclared `strlen` is
4603    /// the builtin too. What it is not is a declaration the program wrote that disagrees with the
4604    /// builtin's type, which gcc keeps and calls, and that was measured as well.
4605    #[test]
4606    fn a_builtin_the_program_never_declared_is_the_builtin_rather_than_the_one_c89_wrote_down() {
4607        // `-fpermissive`, because the implicit declaration itself is an error in every dialect
4608        // after C89 and the program would never get as far as a type without it. Each of the four
4609        // torture programs asks for either that or `-std=gnu89` on its own options line.
4610        let mut opts = options();
4611        opts.permissive = true;
4612        let undeclared = "void use(void *p);
4613void f(unsigned long n) { use(alloca(n)); }
4614";
4615        assert_eq!(
4616            run(&opts, undeclared).messages,
4617            [
4618                "/main.c:2:31: warning: implicit declaration of function 'alloca' [E0521]",
4619                "/main.c:2:31: warning: incompatible implicit declaration of built-in function \
4620                 'alloca' [E0713]",
4621            ]
4622        );
4623
4624        opts.emit = EmitKind::Asm;
4625        let text = run(&opts, undeclared).text().to_owned();
4626        assert!(text.contains("subq\t%rdi, %rsp"), "the bytes come off the stack: {text}");
4627        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4628
4629        // The table's rule and not this one name's, so a name whose whole answer is the library
4630        // function of the same name gets that function's type and still reaches it.
4631        let string = "unsigned long f(void) { return strlen(\"abc\"); }\n";
4632        let text = run(&opts, string).text().to_owned();
4633        assert!(text.contains("call\tstrlen"), "strlen is still a call: {text}");
4634
4635        // A declaration the program wrote is the program's, whatever the table says. gcc keeps
4636        // this one and writes the call, which is what makes the type worth looking at.
4637        let own = concat!(
4638            "static void *alloca(unsigned long n) { return 0; }\n",
4639            "void *f(unsigned long n) { return alloca(n); }\n",
4640        );
4641        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
4642    }
4643
4644    /// The bytes an alloca took live until the function returns and not until the end of the block
4645    /// the call was written in.
4646    ///
4647    /// That is what makes it different from a variable length array, and the way it is kept is that
4648    /// every scope open where the call was written stops giving the stack back. The second program
4649    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
4650    /// inner block gives nothing back either even though an array is in scope that ordinarily
4651    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
4652    /// than read off the manual.
4653    #[test]
4654    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
4655        let inner = "{ use(__builtin_alloca(n)); }";
4656        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
4657            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
4658            let text = asm(&source);
4659            // Every instruction that writes the stack pointer, which in a function that gives
4660            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
4661            // there. A restore would be a third kind, a move out of a register the save wrote.
4662            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
4663                let taking = line.contains("subq");
4664                let leaving = line.contains("%rbp");
4665                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
4666            }
4667        }
4668    }
4669
4670    /// Not a rewording of the check above: what the two paths agree about is the point.
4671    #[test]
4672    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
4673        // A call, because it is the one thing whose spelling in the two differs completely: the
4674        // listing writes a name and the object writes four zero bytes and a relocation asking the
4675        // linker for the same name. If either path had lost the callee, one of these would fail.
4676        let source = "int callee(void); int g(void) { return callee(); }\n";
4677        let bytes = obj(source);
4678        assert!(
4679            bytes.windows(7).any(|w| w == b"callee\0"),
4680            "the object has to name the callee for the linker to find it"
4681        );
4682        let text = asm(source);
4683        assert!(text.contains("\tcall\tcallee\n"), "{text}");
4684    }
4685
4686    /// What a file of a link contributes is an object, and the default emit is a link.
4687    ///
4688    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
4689    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
4690    /// undefined and says nothing about the compilation that produced nothing.
4691    #[test]
4692    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
4693        let mut opts = options();
4694        // What a command line with no `-c` and no `-S` on it asks for.
4695        opts.emit = EmitKind::Executable;
4696        let result = run(&opts, "int main(void) { return 0; }\n");
4697        assert_eq!(result.messages, Vec::<String>::new());
4698        match result.artifact {
4699            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
4700            other => panic!("expected an object, got {other:?}"),
4701        }
4702    }
4703
4704    /// A target with a back end but no object writer says so rather than writing the wrong file.
4705    #[test]
4706    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
4707        let mut opts = options();
4708        opts.emit = EmitKind::Object;
4709        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4710        let result = run(&opts, "int f(void) { return 0; }\n");
4711        assert!(result.failed(), "an object nobody can read is worse than a message");
4712        assert!(
4713            result.messages.iter().any(|m| m.contains("no object writer")),
4714            "{:?}",
4715            result.messages
4716        );
4717    }
4718
4719    /// The IR of `source`, insisting that it compiled cleanly.
4720    fn ir(source: &str) -> String {
4721        let mut opts = options();
4722        opts.emit = EmitKind::Ir;
4723        let result = run(&opts, source);
4724        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4725        result.text().to_owned()
4726    }
4727
4728    /// What was said about `source`, insisting that something was.
4729    fn errors(source: &str) -> Vec<String> {
4730        let mut opts = options();
4731        opts.emit = EmitKind::Ir;
4732        let result = run(&opts, source);
4733        assert!(result.failed(), "expected this to be refused:\n{source}");
4734        result.messages
4735    }
4736
4737    /// The body of the one function in `source`, which is what most of these are about.
4738    fn body(source: &str) -> String {
4739        let text = ir(source);
4740        let (_, rest) = text.split_once("{\n").expect("a function definition");
4741        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
4742        body.to_owned()
4743    }
4744
4745    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
4746    /// module or only a declaration did.
4747    ///
4748    /// The C99 reading is the one an inline definition is written for and is not being changed
4749    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
4750    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
4751    /// those in the GCC torture suite alone.
4752    #[test]
4753    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
4754        let source = "inline int f(int x) { return x + 1; }\n";
4755        let with = |flag: bool| {
4756            let mut opts = options();
4757            opts.emit = EmitKind::Ir;
4758            opts.gnu89_inline = flag;
4759            let result = run(&opts, source);
4760            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4761            result.text().to_owned()
4762        };
4763
4764        // Under C's reading the module holds the declaration and the calls in this unit go to
4765        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
4766        assert!(!with(false).contains("block0"), "no body: {}", with(false));
4767
4768        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
4769        // is one the linker can resolve against.
4770        assert!(with(true).contains("block0"), "a body: {}", with(true));
4771    }
4772
4773    /// Every shape that reads or writes through a C type names that type.
4774    ///
4775    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
4776    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
4777    /// load and nothing on the member load would be a layer that answers for a third of the
4778    /// accesses in a program and is not worth having.
4779    #[test]
4780    fn an_access_through_a_type_names_the_type_it_went_through() {
4781        let source = "\
4782struct s { int a; float b; };\n\
4783union u { int i; float f; };\n\
4784int scalar(int *p) { return *p; }\n\
4785float member(struct s *p) { p->a = 1; return p->b; }\n\
4786int element(int *a, long i) { return a[i]; }\n\
4787float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
4788        let text = ir(source);
4789        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
4790        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
4791        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
4792        // One per access, and a function whose accesses all go through one type says so once per
4793        // access rather than once per function.
4794        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
4795        assert_eq!(named, 6, "six accesses: {text}");
4796    }
4797
4798    /// `-fno-strict-aliasing` is the front end leaving the name off.
4799    ///
4800    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
4801    /// passed this today. What this test is for is the day one does: the flag has to be the
4802    /// absence of the names rather than a condition somewhere downstream, since that is the only
4803    /// version of it that a pass added later cannot forget about.
4804    #[test]
4805    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
4806        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
4807        let mut opts = options();
4808        opts.emit = EmitKind::Ir;
4809        opts.strict_aliasing = false;
4810        let result = run(&opts, source);
4811        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4812        let text = result.text().to_owned();
4813        assert!(!text.contains("tbaa"), "not even the root: {text}");
4814    }
4815
4816    /// `-finstrument-functions` puts one call to the entry hook in front of the body and one call
4817    /// to the exit hook in front of every return, each given the function's own address and the
4818    /// address it returns to. A function declared `no_instrument_function` gets neither, and the
4819    /// hooks are declared that way here as they are in `execute/eeprof-1.c`, since a hook that
4820    /// called itself would never get as far as its body.
4821    #[test]
4822    fn instrumenting_functions_calls_the_hooks_around_every_body_but_the_hooks() {
4823        let source = concat!(
4824            "#define NOCHK __attribute__((no_instrument_function))\n",
4825            "void __cyg_profile_func_enter(void *, void *) NOCHK;\n",
4826            "void __cyg_profile_func_exit(void *, void *) NOCHK;\n",
4827            "int calls;\n",
4828            "int pick(int x) { if (x) return 1; return 2; }\n",
4829            "void quiet(void) NOCHK;\n",
4830            "void quiet(void) { calls++; }\n",
4831            "void __cyg_profile_func_enter(void *fn, void *site) { calls++; }\n",
4832            "void __cyg_profile_func_exit(void *fn, void *site) { calls--; }\n",
4833        );
4834        let mut opts = options();
4835        opts.emit = EmitKind::Ir;
4836        opts.instrument_functions = true;
4837        let result = run(&opts, source);
4838        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4839        let text = result.text().to_owned();
4840        let body = |name: &str| -> String {
4841            let open = format!("func @{name}(");
4842            let start = text.find(&open).unwrap_or_else(|| panic!("no {name}: {text}"));
4843            let rest = &text[start..];
4844            rest[..rest.find("\n}").unwrap_or(rest.len())].to_owned()
4845        };
4846        let pick = body("pick");
4847        assert_eq!(pick.matches("call @__cyg_profile_func_enter(").count(), 1, "{pick}");
4848        assert_eq!(pick.matches("call @__cyg_profile_func_exit(").count(), 2, "{pick}");
4849        assert!(pick.contains("return_address"), "{pick}");
4850        assert!(pick.contains("global_addr @pick"), "{pick}");
4851        for quiet in ["quiet", "__cyg_profile_func_enter", "__cyg_profile_func_exit"] {
4852            assert!(!body(quiet).contains("call "), "{quiet} is left alone: {text}");
4853        }
4854
4855        opts.instrument_functions = false;
4856        let result = run(&opts, source);
4857        assert!(!result.text().contains("call @__cyg_profile"), "off unless asked for");
4858    }
4859
4860    /// Under `-fexceptions` a `cleanup` handler is owed a call on an unwind as well, and nothing
4861    /// here builds the landing pad that would make it. So a call inside a handler's scope is
4862    /// turned down by name, a handler with no call in its scope is left alone, and without the
4863    /// flag the same source compiles as it always did.
4864    #[test]
4865    fn a_call_an_unwind_would_leave_a_cleanup_behind_is_refused_under_exceptions() {
4866        let source = concat!(
4867            "void done(int *p);\n",
4868            "void work(void);\n",
4869            "void calls(void) { int x __attribute__((cleanup(done))) = 1; work(); }\n",
4870            "int quiet(int y) { int x __attribute__((cleanup(done))) = y; return x + 1; }\n",
4871            "void after(void) { { int x __attribute__((cleanup(done))) = 1; } work(); }\n",
4872        );
4873        let mut opts = options();
4874        opts.emit = EmitKind::Ir;
4875        let result = run(&opts, source);
4876        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4877
4878        opts.exceptions = true;
4879        let result = run(&opts, source);
4880        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
4881        assert!(result.messages[0].contains("landing pad"), "{:?}", result.messages);
4882        assert!(result.messages[0].contains(":3:"), "the call in calls: {:?}", result.messages);
4883    }
4884
4885    /// `return;` from a function that promised a value, which only C89 lets through and which
4886    /// therefore only reaches the IR builder under that dialect.
4887    ///
4888    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
4889    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
4890    /// that the branch reaching this never runs, which is a claim about the program rather than
4891    /// about the value and lets the optimizer delete the path that led here.
4892    #[test]
4893    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
4894        let mut opts = options();
4895        opts.emit = EmitKind::Ir;
4896        opts.std = Std::C89;
4897        let compiled = |source: &str| {
4898            let result = run(&opts, source);
4899            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
4900            result.text().to_owned()
4901        };
4902
4903        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
4904        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
4905        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
4906
4907        // A floating point return needs the constant of its own kind rather than an integer one.
4908        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
4909        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
4910    }
4911
4912    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
4913    /// in what was said about it.
4914    ///
4915    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
4916    /// than converted to parameters there are none of. The declaration lasts for the file, which
4917    /// is what makes a second call to the same name ordinary and is why gcc says this once per
4918    /// file rather than once per call.
4919    #[test]
4920    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
4921        let mut opts = options();
4922        opts.emit = EmitKind::Ir;
4923        opts.std = Std::C89;
4924        let compiled = |source: &str| {
4925            let result = run(&opts, source);
4926            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
4927            result.text().to_owned()
4928        };
4929
4930        // An `int` back, which is the whole of what the implicit declaration says.
4931        let text = compiled("int f(void) { return g(); }\n");
4932        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
4933        assert!(text.contains("i32"), "and it gives back an int: {text}");
4934
4935        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
4936        // function whose parameters are unspecified does.
4937        let text = compiled("int f(char c) { return g(c); }\n");
4938        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
4939
4940        // A name written as a value rather than called is still undeclared, since the rule is
4941        // about a call and nothing else.
4942        let mut opts = options();
4943        opts.std = Std::C89;
4944        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
4945        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
4946    }
4947
4948    /// A file that calls a name above the definition of it, which is the shape the implicit
4949    /// declaration has to survive rather than swallow.
4950    ///
4951    /// The definition merges into the declaration the call already made rather than making a
4952    /// second one, so a declaration the tree does not carry at the top level takes the definition
4953    /// down with it: the body is attached to a node nothing walks and no function comes out.
4954    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
4955    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
4956    /// found it, as an undefined reference to a name defined eleven lines further down.
4957    #[test]
4958    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
4959        let mut opts = options();
4960        opts.emit = EmitKind::Ir;
4961        opts.std = Std::C89;
4962        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
4963            .text()
4964            .to_owned();
4965        assert!(text.contains("func @f()"), "the caller is there: {text}");
4966        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
4967        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
4968    }
4969
4970    /// An old style definition whose parameter is narrower than what a call passes it.
4971    ///
4972    /// There is no prototype for a call to convert its argument to, so the argument is promoted
4973    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
4974    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
4975    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
4976    /// checks the parameter against `0xFF`, which is the difference between converting and not.
4977    #[test]
4978    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
4979        let mut opts = options();
4980        opts.emit = EmitKind::Ir;
4981        opts.std = Std::C89;
4982        let compiled = |source: &str| run(&opts, source).text().to_owned();
4983
4984        let text = compiled("f (c) unsigned char c; { return c; }\n");
4985        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
4986        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
4987        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
4988
4989        // A `short` is the same shape and signed, so it comes back the other way.
4990        let text = compiled("f (s) short s; { return s; }\n");
4991        assert!(text.contains("trunc.i16"), "cut down: {text}");
4992        assert!(text.contains("sext.i32"), "and read back signed: {text}");
4993
4994        // A `float` parameter is promoted to `double`, and without the conversion the multiply
4995        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
4996        let text = compiled("f (x) float x; { return x * 2; }\n");
4997        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
4998        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
4999
5000        // A parameter a prototype named arrives as itself and nothing is converted, which is the
5001        // case this must not have changed.
5002        let text = compiled("int f(unsigned char c) { return c; }\n");
5003        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
5004        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
5005    }
5006
5007    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
5008    /// gets depending on the dialect and on `-fpermissive`.
5009    ///
5010    /// The table is a measurement rather than a reading of the release notes. Six files, one per
5011    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
5012    /// with no `-W` flags on any of them, and what came back is what is written here. The three
5013    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
5014    /// there were constraint violations then as well.
5015    #[test]
5016    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
5017        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
5018        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5019        let cases = [
5020            ("static counted;\n", ["", "error", "warning", "error"]),
5021            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
5022            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
5023            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
5024            (
5025                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
5026                ["warning", "error", "warning", "error"],
5027            ),
5028            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
5029            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
5030        ];
5031
5032        for (source, wanted) in cases {
5033            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5034                let mut opts = options();
5035                opts.std = std;
5036                opts.permissive = permissive;
5037                let said = run(&opts, source).messages.join("\n");
5038                let severity = if said.contains(": error: ") {
5039                    "error"
5040                } else if said.contains(": warning: ") {
5041                    "warning"
5042                } else {
5043                    ""
5044                };
5045                let how = if permissive { " -fpermissive" } else { "" };
5046                assert_eq!(
5047                    severity,
5048                    wanted,
5049                    "under -std={}{how}, {source} was answered with `{said}`",
5050                    std.as_str()
5051                );
5052                if wanted.is_empty() {
5053                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
5054                }
5055            }
5056        }
5057    }
5058
5059    /// A first argument that is not a list, which the four variadic operators answer in two ways.
5060    ///
5061    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
5062    /// other three as builtin functions taking the address of a list. The difference is not a
5063    /// naming one: the operator's complaint is its own and is an error under every dialect, and
5064    /// the three functions go through the ordinary rule about an argument of the wrong type,
5065    /// which is one of the rules the table above is about. The same four command lines through
5066    /// gcc 16.2.0 on x86-64 Linux is where these came from.
5067    #[test]
5068    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
5069        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5070        let cases = [
5071            (
5072                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
5073                "first argument to 'va_arg' not of type 'va_list'",
5074                ["error", "error", "error", "error"],
5075            ),
5076            (
5077                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
5078                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
5079                ["warning", "error", "warning", "error"],
5080            ),
5081            (
5082                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
5083                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
5084                 cast",
5085                ["warning", "error", "warning", "error"],
5086            ),
5087            (
5088                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
5089                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
5090                ["warning", "error", "warning", "error"],
5091            ),
5092        ];
5093
5094        for (source, message, wanted) in cases {
5095            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5096                let mut opts = options();
5097                opts.std = std;
5098                opts.permissive = permissive;
5099                let said = run(&opts, source).messages.join("\n");
5100                let how = if permissive { " -fpermissive" } else { "" };
5101                assert!(
5102                    said.contains(&format!(": {wanted}: {message}")),
5103                    "under -std={}{how}, {source} was answered with `{said}`",
5104                    std.as_str()
5105                );
5106            }
5107        }
5108    }
5109
5110    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
5111    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
5112        let mut opts = options();
5113        opts.emit = EmitKind::Ir;
5114        opts.safety = tier;
5115        let result = run(&opts, source);
5116        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5117        result.text().to_owned()
5118    }
5119
5120    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
5121
5122    /// The IR for a source built with a tier and a padding mode.
5123    fn padded_ir(padding: Padding, source: &str) -> String {
5124        let mut opts = options();
5125        opts.emit = EmitKind::Ir;
5126        opts.safety = rucc_session::Safety::Detect;
5127        opts.padding = padding;
5128        let result = run(&opts, source);
5129        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5130        result.text().to_owned()
5131    }
5132
5133    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
5134         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
5135
5136    #[test]
5137    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
5138        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
5139        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
5140        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
5141        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5142        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5143    }
5144
5145    #[test]
5146    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
5147        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
5148        // unwritten and the read of the record that would leak it is the one that reports.
5149        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5150        assert!(!text.contains("owns"), "{text}");
5151    }
5152
5153    #[test]
5154    fn a_member_of_a_union_owns_nothing_after_it() {
5155        // The bytes after a short member of a union belong to a longer member rather than to
5156        // padding, and saying a store through the short one wrote them would be saying the longer
5157        // one holds a value nobody put there.
5158        let text = padded_ir(
5159            Padding::Ignored,
5160            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
5161        );
5162        assert!(!text.contains("owns"), "{text}");
5163    }
5164
5165    #[test]
5166    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
5167        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
5168        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
5169        // Without that the three bytes between them would stay unwritten and a read of the whole
5170        // thing would report.
5171        let text = padded_ir(
5172            Padding::Ignored,
5173            "struct inner { char c; };\n\
5174             struct outer { struct inner in; int x; };\n\
5175             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
5176        );
5177        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5178    }
5179
5180    #[test]
5181    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
5182        // This is the load bearing test of the whole flag. The monitor is being built in the open
5183        // and every build in the world is compiled by this compiler with the flag absent, so a
5184        // check that leaked into that path would be a regression for everybody.
5185        let text = ir(READS_THROUGH_A_POINTER);
5186        assert!(!text.contains("check_"), "{text}");
5187        assert!(!text.contains("cap_of"), "{text}");
5188    }
5189
5190    #[test]
5191    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
5192        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5193        assert!(text.contains("cap_of"), "{text}");
5194        assert!(text.contains("check_bounds"), "{text}");
5195        assert!(text.contains("check_live"), "{text}");
5196        // The subscript is address arithmetic, so J2 applies to it as well as J1.
5197        assert!(text.contains("check_deriv"), "{text}");
5198        // And the read names a type, so it asks the type plane about the bytes as well.
5199        assert!(text.contains("check_type"), "{text}");
5200    }
5201
5202    #[test]
5203    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
5204        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
5205        // Pinning it here means the day they stop agreeing, this test says so rather than the
5206        // difference going unnoticed.
5207        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5208        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
5209            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
5210        }
5211    }
5212
5213    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
5214    fn summary(tier: rucc_session::Safety, source: &str) -> String {
5215        let mut opts = options();
5216        opts.emit = EmitKind::SafetySummary;
5217        opts.safety = tier;
5218        let result = run(&opts, source);
5219        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5220        result.text().to_owned()
5221    }
5222
5223    #[test]
5224    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
5225        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5226        assert!(text.contains("\"tier\": \"detect\""), "{text}");
5227        // One load, so one of each of the two access checks, and the subscript is a derivation.
5228        assert!(
5229            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
5230            "{text}"
5231        );
5232        assert!(
5233            text.contains(
5234                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
5235            ),
5236            "{text}"
5237        );
5238    }
5239
5240    #[test]
5241    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
5242        // Which is the honest summary rather than an error. A build system that emits a summary
5243        // for every unit should get one for the units nobody asked to instrument too, and the
5244        // zeroes are what say that the guarantee over that file is nothing at all.
5245        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
5246        assert!(text.contains("\"tier\": \"off\""), "{text}");
5247        assert!(
5248            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
5249            "{text}"
5250        );
5251    }
5252
5253    #[test]
5254    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
5255        let text = summary(
5256            rucc_session::Safety::Detect,
5257            "void *memcpy(void *, const void *, unsigned long);\n\
5258             int puts(const char *);\n\
5259             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
5260        );
5261        assert!(text.contains("\"interposed\": 1"), "{text}");
5262        assert!(text.contains("\"puts\""), "{text}");
5263        // The wrapper it was pointed at is ours, so it is not on the list of things this build
5264        // failed to model. Counting it there would make instrumenting a file look worse than
5265        // leaving it alone.
5266        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
5267    }
5268
5269    #[test]
5270    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
5271        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
5272        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
5273        // table holds is the real function and the build did not, and section 10.1 says the one it
5274        // did not is named rather than passed over.
5275        let text = summary(
5276            rucc_session::Safety::Detect,
5277            "void *memcpy(void *, const void *, unsigned long);\n\
5278             int puts(const char *);\n\
5279             void *table[2] = { (void *)memcpy, (void *)puts };\n\
5280             void *f(int i) { return table[i]; }\n",
5281        );
5282        assert!(text.contains("\"interposed\": 1"), "{text}");
5283        assert!(text.contains("\"puts\""), "{text}");
5284        assert!(!text.contains("\"memcpy\""), "{text}");
5285    }
5286
5287    #[test]
5288    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
5289        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
5290        // `notes_open` is a library this build did not instrument, so a pointer comes back from
5291        // it. Both are crossings and neither is the other, which is why there are two numbers.
5292        let text = summary(
5293            rucc_session::Safety::Detect,
5294            "void *notes_open(void);\n\
5295             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
5296        );
5297        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
5298        assert!(text.contains("\"notes_open\""), "{text}");
5299    }
5300
5301    #[test]
5302    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
5303        // Nothing outside the file can reach it, so a witness on its parameters would be counting
5304        // a crossing that does not happen.
5305        let text = summary(
5306            rucc_session::Safety::Detect,
5307            "static int len(const char *p) { return p ? 1 : 0; }\n\
5308             int f(void) { return len(\"x\"); }\n",
5309        );
5310        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
5311    }
5312
5313    /// The granule report for `source`, insisting that it compiled cleanly.
5314    fn granules(source: &str) -> String {
5315        let mut opts = options();
5316        opts.emit = EmitKind::TypeGranules;
5317        let result = run(&opts, source);
5318        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5319        result.text().to_owned()
5320    }
5321
5322    #[test]
5323    fn the_granule_report_names_every_record_and_both_keyings() {
5324        let text = granules(
5325            "struct hot { char *p; int a; int b; };\n\
5326             int f(struct hot *h) { return h->a; }\n",
5327        );
5328        assert!(text.contains("struct hot"), "{text}");
5329        // Both keyings are reported because which types count as one is a decision the design
5330        // has not made yet, and a report that picked one would be hiding the cost of the other.
5331        assert!(text.contains("every type distinct"), "{text}");
5332        assert!(text.contains("every pointer one type"), "{text}");
5333        assert!(text.contains("budget"), "{text}");
5334    }
5335
5336    #[test]
5337    fn a_record_nothing_uses_is_still_measured() {
5338        // The measurement is about what a program declares, not about what it runs, so a type
5339        // that is only ever declared still costs the plane whatever its layout costs.
5340        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
5341        assert!(text.contains("struct unused"), "{text}");
5342    }
5343
5344    #[test]
5345    fn the_granule_report_stops_before_anything_is_lowered() {
5346        // A layout is settled at the closing brace, so lowering the function bodies would take
5347        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
5348        // body the back end has no way to compile still produces a report.
5349        let text = granules(
5350            "struct wide { long double d; };\n\
5351             long double f(long double x) { return x * x; }\n",
5352        );
5353        assert!(text.contains("struct wide"), "{text}");
5354    }
5355
5356    #[test]
5357    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
5358        // The count only means anything if the call is really there, and a summary saying one is
5359        // there is not evidence that the back end emitted it.
5360        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
5361        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
5362    }
5363
5364    #[test]
5365    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
5366        let text = summary(
5367            rucc_session::Safety::Detect,
5368            "unsigned long f(int *p) { return (unsigned long) p; }\n",
5369        );
5370        assert!(text.contains("\"exposed\": 1"), "{text}");
5371    }
5372
5373    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
5374    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
5375        let mut opts = options();
5376        opts.emit = EmitKind::Asm;
5377        opts.safety = tier;
5378        let result = run(&opts, source);
5379        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5380        result.text().to_owned()
5381    }
5382
5383    #[test]
5384    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
5385        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5386        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
5387        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
5388        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
5389        // The type check and the init check of one read reach the assembler as the one call that
5390        // asks both planes about it. `rucc_safety::lower::partner` is what recognises the pair.
5391        assert!(text.contains("\tcall\t__rucc_check_typed_init\n"), "{text}");
5392    }
5393
5394    #[test]
5395    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
5396        // Four calls and four descriptors, each in the section the runtime's reporter reads. The
5397        // width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`, and
5398        // the two agreeing is what makes the address a check is handed mean anything. Four rather
5399        // than five because the read's two plane questions are one call carrying one row, which the
5400        // two of them can share because a type check's row and an init check's row are identical.
5401        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5402        let section = format!("\t.section\t{},", rucc_safety::SECTION);
5403        assert_eq!(text.matches(&section).count(), 4, "{text}");
5404        for index in 0..4 {
5405            let name = format!("__rucc_safety_desc_{index}");
5406            // Defined once and referenced once, because a descriptor nothing points at describes
5407            // nothing and a reference with no definition does not link.
5408            assert!(text.contains(&format!("{name}:\n")), "{text}");
5409            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
5410        }
5411        assert!(!text.contains("__rucc_safety_desc_4"), "{text}");
5412    }
5413
5414    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
5415    ///
5416    /// gcc folds it after optimization, so its answer for an argument that is not written as a
5417    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
5418    /// answer, which is the same at every level, and the four cases where gcc gives the same
5419    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
5420    /// zero, a string literal is one and the address of an object is zero.
5421    #[test]
5422    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
5423        let text = ir(concat!(
5424            "int g;\n",
5425            "int a = __builtin_constant_p(1);\n",
5426            "int b = __builtin_constant_p(g);\n",
5427            "int c = __builtin_constant_p(\"abc\");\n",
5428            "int d = __builtin_constant_p(&g);\n",
5429            "int e = __builtin_constant_p(1.5);\n",
5430            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
5431        ));
5432        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5433        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5434        assert!(text.contains("global @c : i32 = 1,"), "{text}");
5435        assert!(text.contains("global @d : i32 = 0,"), "{text}");
5436        assert!(text.contains("global @e : i32 = 1,"), "{text}");
5437        assert!(text.contains("global @h : i32 = 11,"), "{text}");
5438        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
5439
5440        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
5441        // still zero. The second constant is the answer, which nothing reads and which the
5442        // first pass that looks for dead code will take out.
5443        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
5444        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
5445    }
5446
5447    /// A library builtin is the library function of the same name, and the call says so.
5448    ///
5449    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
5450    /// library promises where its own name has been taken by a macro, and to say that the usual
5451    /// meaning is the one intended. So the name in the program and the name in the object file
5452    /// are two different names and the call carries the second one. gcc folds several of these
5453    /// when the arguments allow it, which is an optimization on top of a call that is already
5454    /// right rather than instead of it, so nothing here depends on any folding happening.
5455    #[test]
5456    fn a_call_to_a_library_builtin_reaches_the_library_function() {
5457        let text = body("void f(void) { __builtin_abort(); }\n");
5458        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
5459
5460        // Nothing declared either of these and nothing had to: the prefix is what says the name
5461        // belongs to the implementation, and the type comes out of `features.toml`.
5462        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
5463        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
5464        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
5465        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
5466    }
5467
5468    /// A `_chk` builtin reaches the checking function in the library with the object size still
5469    /// on the end of it.
5470    ///
5471    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
5472    /// the way a distribution builds one is full of, and the whole of what makes the call right
5473    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
5474    /// is known and does no check, which is what the header passes when the destination's object
5475    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
5476    /// call gcc would have folded away in the second.
5477    ///
5478    /// The name is the one place this family reads like an exception and is not one:
5479    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
5480    #[test]
5481    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
5482        let text = ir(concat!(
5483            "char d[8];\n",
5484            "void f(const char *s, unsigned long n) {\n",
5485            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
5486            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
5487            "  __builtin___memset_chk(d, 0, n, 8);\n",
5488            "}\n",
5489        ));
5490        assert!(text.contains("call @__memcpy_chk("), "{text}");
5491        assert!(text.contains("call @__strcpy_chk("), "{text}");
5492        assert!(text.contains("call @__memset_chk("), "{text}");
5493        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
5494        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
5495    }
5496
5497    /// A checking call whose object size says nothing is known is the plain library call.
5498    ///
5499    /// That is the whole of the folding half of the family. The checking function reads the all
5500    /// ones value as do not check, so the call it was going to make is the function it guards with
5501    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
5502    /// function at every level including `-O0`. Where the size is a real number the checking call
5503    /// stands, because the check is the point.
5504    #[test]
5505    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
5506        let text = ir(concat!(
5507            "extern char *p;\n",
5508            "char d[8];\n",
5509            "void f(const char *s, unsigned long n) {\n",
5510            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
5511            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5512            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
5513            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5514            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
5515            "}\n",
5516        ));
5517
5518        // The destination whose object is in sight keeps its check, size and all.
5519        assert!(
5520            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
5521            "{text}"
5522        );
5523
5524        // The three whose object is not lose the argument and the name along with it. The type of
5525        // the call goes with them, which is what says the argument is gone rather than ignored.
5526        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
5527        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
5528        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
5529
5530        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
5531        // writable format is the other half of what it was asked to do.
5532        assert!(text.contains("call @__sprintf_chk("), "{text}");
5533
5534        // Nothing is left behind in the instructions either. The size the folded calls no longer
5535        // take is a constant nobody reads, and no instruction is written for one.
5536        let asm = asm(concat!(
5537            "void f(char *p, const char *s, unsigned long n) {\n",
5538            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5539            "}\n",
5540        ));
5541        assert!(asm.contains("call\tmemcpy"), "{asm}");
5542        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
5543    }
5544
5545    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
5546    /// target chooses the shape of rather than the width of.
5547    ///
5548    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
5549    /// array decays to, which is the same adjustment C makes to any parameter written as an array
5550    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
5551    /// one no argument could ever match.
5552    #[test]
5553    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
5554        let text = ir(concat!(
5555            "char d[64];\n",
5556            "int f(const char *fmt, ...) {\n",
5557            "  __builtin_va_list ap;\n",
5558            "  __builtin_va_start(ap, fmt);\n",
5559            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
5560            "  __builtin_va_end(ap);\n",
5561            "  return n;\n",
5562            "}\n",
5563        ));
5564        assert!(text.contains("call @__vsprintf_chk("), "{text}");
5565        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
5566    }
5567
5568    /// The absolute value family is four instructions and not a call, whoever declared the name.
5569    ///
5570    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
5571    /// means the one the C library promises and the compiler is allowed to know what it does. The
5572    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
5573    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
5574    /// `neg` and a `cmovns` and never calls the definition either.
5575    ///
5576    /// The most negative value comes back as itself, which is what the arithmetic gives and what
5577    /// gcc's pair of instructions gives, and C says the answer is undefined there.
5578    #[test]
5579    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
5580        let text = body(concat!(
5581            "long long llabs(long long);\n",
5582            "long long f(long long x) { return llabs(x); }\n",
5583        ));
5584        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
5585        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
5586        assert!(text.contains("%3 = xor %0, %2"), "{text}");
5587        assert!(text.contains("%4 = sub %3, %2"), "{text}");
5588        assert!(!text.contains("call"), "the call does not happen:\n{text}");
5589
5590        // The narrower two, whose width comes from the type the library gives the name and not
5591        // from anything at the call.
5592        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
5593        assert!(text.contains("iconst.i32 31"), "{text}");
5594        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
5595        assert!(text.contains("iconst.i64 63"), "{text}");
5596
5597        // The prefixed spelling is the same node, and it is what a program writes to reach the
5598        // library's meaning where the plain name has been taken.
5599        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
5600        assert!(!text.contains("call"), "{text}");
5601
5602        // A definition of the name in the same file changes nothing, which is the whole point.
5603        let text = ir(concat!(
5604            "long long llabs(long long b);\n",
5605            "long long g(long long x) { return llabs(x); }\n",
5606            "long long llabs(long long b) { return 7; }\n",
5607        ));
5608        assert!(!text.contains("call @llabs"), "{text}");
5609    }
5610
5611    /// A byte swap is one instruction and not a call, and nothing had to declare it.
5612    ///
5613    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
5614    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
5615    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
5616    /// standing here would not link.
5617    #[test]
5618    fn a_byte_swap_is_arithmetic_and_not_a_call() {
5619        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
5620        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
5621
5622        // The argument is converted by the prototype the way any other call's would be, so the
5623        // swap happens at the width the name says and not at the width the program wrote.
5624        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
5625        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
5626        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
5627    }
5628
5629    /// Each of the three reverses in the width its name says, which is the type of the node.
5630    ///
5631    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
5632    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
5633    /// above the value would be dragged into the answer and the result would be zero.
5634    #[test]
5635    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
5636        for (name, ty, width) in [
5637            ("__builtin_bswap16", "unsigned short", "i16"),
5638            ("__builtin_bswap32", "unsigned", "i32"),
5639            ("__builtin_bswap64", "unsigned long long", "i64"),
5640        ] {
5641            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
5642            let text = body(&source);
5643            assert_eq!(
5644                text,
5645                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
5646                "{name}"
5647            );
5648        }
5649    }
5650
5651    /// The three bit counts the IR has an instruction for are that instruction and not a call.
5652    ///
5653    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
5654    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
5655    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
5656    /// would not link against anything and would be slow if it did.
5657    #[test]
5658    fn the_bit_counts_are_instructions_and_not_calls() {
5659        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
5660        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
5661
5662        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
5663        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
5664
5665        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
5666        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
5667    }
5668
5669    /// The width counted is the operand's and the width answered is `int`, which are two different
5670    /// things at every spelling but the narrowest.
5671    ///
5672    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
5673    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
5674    /// those are different numbers for the same value. What decides it is the prototype the row
5675    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
5676    /// after the count.
5677    #[test]
5678    fn the_bit_counts_ask_about_the_width_their_name_says() {
5679        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
5680        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
5681        assert!(text.contains("%1 = ctlz %0"), "{text}");
5682        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
5683
5684        // The same value asked about at the narrower width, which converts first and so counts
5685        // something else.
5686        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
5687        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
5688        assert!(text.contains("ctlz %1"), "and counted there: {text}");
5689
5690        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
5691        assert!(text.contains("%1 = ctpop %0"), "{text}");
5692        assert!(!text.contains("call"), "{text}");
5693    }
5694
5695    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
5696    ///
5697    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
5698    /// different question, and not the count itself, since C says the answer is zero or one.
5699    #[test]
5700    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
5701        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
5702        assert!(text.contains("%1 = ctpop %0"), "{text}");
5703        assert!(text.contains("iconst.i32 1"), "{text}");
5704        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
5705    }
5706
5707    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
5708    ///
5709    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
5710    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
5711    /// a branch would buy nothing and cost two blocks and a join.
5712    #[test]
5713    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
5714        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
5715        assert!(text.contains("%1 = cttz %0"), "{text}");
5716        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
5717        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
5718        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
5719        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
5720        assert!(!text.contains("br_if"), "no branch: {text}");
5721    }
5722
5723    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
5724    /// count of the value folded onto its own sign.
5725    ///
5726    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
5727    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
5728    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
5729    /// than that count, and the shift left is what takes the one off, with the low bit set on the
5730    /// way so that zero and minus one have something to count: both of them fold to a word with no
5731    /// bits in it, which is the one input a leading zero count says nothing about.
5732    #[test]
5733    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
5734        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
5735        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
5736        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
5737        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
5738        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
5739        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
5740        assert!(text.contains("%7 = ctlz %6"), "{text}");
5741        assert!(!text.contains("call"), "{text}");
5742        assert!(!text.contains("br_if"), "no branch: {text}");
5743    }
5744
5745    /// The unsigned four are the same four instructions answering in the unsigned type.
5746    ///
5747    /// Which on a two's complement machine is the same bits, so what this checks is that the type
5748    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
5749    /// whose magnitude is not representable in the signed type and is representable in this one.
5750    #[test]
5751    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
5752        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
5753        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
5754        assert!(text.contains("%4 = sub %3, %2"), "{text}");
5755        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
5756
5757        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
5758        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
5759
5760        // The answer is the unsigned type and not the signed one, which is what a comparison
5761        // against it is decided by.
5762        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
5763        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
5764    }
5765
5766    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
5767    ///
5768    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
5769    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
5770    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
5771    /// signature was understood at all rather than refused for naming a type the table could not
5772    /// spell.
5773    #[test]
5774    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
5775        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
5776        assert!(text.contains("iconst.i64 63"), "{text}");
5777        assert!(text.contains("%4 = sub %3, %2"), "{text}");
5778        assert!(!text.contains("call"), "{text}");
5779
5780        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
5781        assert!(text.contains("iconst.i64 63"), "{text}");
5782        assert!(!text.contains("call"), "{text}");
5783    }
5784
5785    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
5786    /// argument.
5787    ///
5788    /// gcc says the third argument is there for its type alone, so a call is two operands and a
5789    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
5790    /// the three that write: whether the exact answer would have fit there, which is why the
5791    /// second call below is done at a wider width than the first.
5792    #[test]
5793    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
5794        let text =
5795            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
5796        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
5797        assert!(!text.contains("store"), "nothing is written: {text}");
5798        assert!(!text.contains("call"), "{text}");
5799
5800        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
5801        // what says whether the answer got there, exactly as for the spelling that stores.
5802        let text =
5803            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
5804        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
5805        assert!(!text.contains("store"), "{text}");
5806
5807        // The third argument is a value and not a pointer, and a side effect written in it does
5808        // not happen, because what the argument is there for is its type.
5809        let text = body(concat!(
5810            "int g(void);\n",
5811            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
5812        ));
5813        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
5814    }
5815
5816    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
5817    ///
5818    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
5819    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
5820    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
5821    ///
5822    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
5823    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
5824    /// through the pointer it was handed.
5825    #[test]
5826    fn an_overflow_check_is_arithmetic_and_not_a_call() {
5827        let text =
5828            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
5829        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
5830        assert!(text.contains("store %3 -> %2"), "{text}");
5831        assert!(!text.contains("call"), "{text}");
5832
5833        let text =
5834            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
5835        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
5836
5837        let text =
5838            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
5839        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
5840
5841        // Unsigned operands get the unsigned form, which is a different question about the same
5842        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
5843        let text = body(
5844            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
5845        );
5846        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
5847    }
5848
5849    /// The arithmetic happens at a type that holds every value all three written types can hold.
5850    ///
5851    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
5852    /// bits between them, so the add is done at sixty four with each operand extended the way its
5853    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
5854    /// extending the unsigned one would turn three billion into a negative number before the
5855    /// addition ever saw it.
5856    #[test]
5857    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
5858        let text = body(
5859            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
5860        );
5861        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
5862        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
5863        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
5864
5865        // Three types that agree need no extension at all, which is what nearly every real call
5866        // is written as.
5867        let text = body(
5868            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
5869        );
5870        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
5871        assert!(!text.contains("sext."), "{text}");
5872        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
5873        assert!(!text.contains("zext.i64"), "{text}");
5874    }
5875
5876    /// The wrapped answer is written through the pointer whether or not it fit.
5877    ///
5878    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
5879    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
5880    /// answer being different is the second half of the test: the instruction says whether the
5881    /// arithmetic itself needed more room, and the round trip says whether what came out survived
5882    /// the trip down to where it was going.
5883    #[test]
5884    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
5885        let text =
5886            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
5887        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
5888        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
5889        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
5890        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
5891        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
5892        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
5893    }
5894
5895    /// A call needing more than the widest type there is compiles, by not asking for such a type.
5896    ///
5897    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
5898    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
5899    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
5900    /// inside it, which is what gcc does, so all three of the family compile for that mix.
5901    #[test]
5902    fn a_call_needing_more_than_the_widest_type_still_compiles() {
5903        for name in ["add", "sub", "mul"] {
5904            let source = format!(
5905                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
5906                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
5907            );
5908            let mut opts = options();
5909            opts.emit = EmitKind::MirFinal;
5910            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
5911        }
5912    }
5913
5914    /// An operand that is not an integer at all is the older message, from the type checking every
5915    /// type generic builtin shares.
5916    #[test]
5917    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
5918        let messages =
5919            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
5920        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
5921
5922        let messages =
5923            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
5924        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
5925    }
5926
5927    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
5928    ///
5929    /// Which is the point of the node existing at all. An ordering is not an argument anything is
5930    /// passed, it is a thing the IR says about an access, so the number in the source is read once
5931    /// in the front end and after that the ordering travels on the instruction where every pass
5932    /// that moves code can see it.
5933    ///
5934    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
5935    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
5936    /// calls to the pair.
5937    #[test]
5938    fn an_ordered_access_is_ordered_in_the_ir() {
5939        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
5940        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
5941
5942        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
5943        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
5944
5945        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
5946        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
5947
5948        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
5949        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
5950
5951        // The value is converted to what the pointer points at before it is stored, which is what
5952        // the call would have done if it had a prototype to convert against.
5953        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
5954        assert!(text.contains("trunc.i8 %1"), "{text}");
5955        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
5956    }
5957
5958    /// On this machine the ordered access is the plain instruction, except at the strongest
5959    /// ordering of a store.
5960    ///
5961    /// x86-64 is total store order: every load is already an acquire and every store is already a
5962    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
5963    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
5964    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
5965    /// is what gcc 16.2.0 writes for the same function.
5966    #[test]
5967    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
5968        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
5969        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
5970        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
5971
5972        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
5973        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
5974        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
5975
5976        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
5977        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
5978        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
5979        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
5980    }
5981
5982    /// A barrier is one instruction at the strongest ordering and no instruction below it.
5983    ///
5984    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
5985    /// are already true of every program running on this machine, and what a program wanted from
5986    /// one is that the compiler not move accesses across it, which is already so by the time any
5987    /// instruction is picked. Sequential consistency is the one that costs something.
5988    ///
5989    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
5990    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
5991    #[test]
5992    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
5993        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
5994        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
5995
5996        for weaker in ["1", "2", "3", "4"] {
5997            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
5998            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
5999        }
6000    }
6001
6002    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
6003    ///
6004    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
6005    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
6006    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
6007    /// already carries at `_mm_sfence`.
6008    ///
6009    /// Each carries a signature, so an argument written on one is reported like an argument
6010    /// written on any other call, which is the whole reason they have one.
6011    #[test]
6012    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
6013        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
6014            let source = format!("void f(void) {{ {name}(); }}\n");
6015            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
6016            let text = body(&source);
6017            assert!(text.contains("fence seq_cst"), "{name}: {text}");
6018        }
6019
6020        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
6021        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
6022        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
6023    }
6024
6025    /// The four compare and exchange names are one IR instruction producing two values.
6026    ///
6027    /// Which of the two the expression answers is the difference between three of the four names,
6028    /// and the fourth difference is the C11 pair writing what they found back through the pointer
6029    /// they were handed, which is the branch after the instruction.
6030    #[test]
6031    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
6032        // The older family, whose two names are the same instruction read two ways. Neither has a
6033        // memory order argument and both are a full barrier, which is what `seq_cst` says.
6034        let text =
6035            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
6036        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6037        assert!(text.contains("return %3"), "the value it found: {text}");
6038
6039        let text =
6040            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
6041        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6042        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
6043
6044        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
6045        // and whose answer is whether it happened. The write back is on the path where it did not.
6046        let text = body(
6047            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
6048        );
6049        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6050        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
6051        assert!(text.contains("br_if %5, block2, block1"), "{text}");
6052        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
6053
6054        // And the form that takes the value to put there by pointer as well, which is one more
6055        // read and is otherwise the same node.
6056        let text = body(
6057            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
6058        );
6059        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6060        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
6061        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
6062    }
6063
6064    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
6065    ///
6066    /// The `lock` is what makes the whole of it one step as far as every other processor is
6067    /// concerned, and it is also what makes the instruction a full barrier, which is why the
6068    /// ordering the program wrote changes nothing in what is written here. Every line below is what
6069    /// gcc 16.2.0 writes for the same function.
6070    #[test]
6071    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
6072        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6073        for (ty, suffix, reg) in widths {
6074            let source = format!(
6075                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
6076            );
6077            let text = asm(&source);
6078            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6079            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6080            assert!(text.contains("sete\t"), "{ty}: {text}");
6081        }
6082        let source =
6083            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
6084        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6085
6086        // The ordering the program asked for changes nothing, because a locked instruction on this
6087        // machine orders everything whatever it was asked for, so there is never a barrier beside
6088        // it either.
6089        for order in ["0", "2", "3", "4", "5"] {
6090            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
6091            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
6092            let text = asm(&source);
6093            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
6094            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6095        }
6096    }
6097
6098    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
6099    /// that instruction and one more operation.
6100    ///
6101    /// The instruction answers what was there before, which is the convention every machine and
6102    /// every language in this area uses. Half the names in the family ask for the value afterwards
6103    /// instead, and that is the answer and the operand put together again, which is arithmetic on
6104    /// two values already in registers rather than a second flavour of the instruction.
6105    ///
6106    /// The two lock names are here too. They are not read modify writes in the same sense: one is
6107    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
6108    /// which is the one place in the older family that is not sequential consistency.
6109    #[test]
6110    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
6111        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
6112        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6113        assert!(text.contains("return %2"), "the value that was there: {text}");
6114
6115        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
6116        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6117        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
6118
6119        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
6120        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6121        assert!(text.contains("%3 = sub %2, %1"), "{text}");
6122
6123        // The older family, which passes no ordering and is a full barrier.
6124        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
6125        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6126
6127        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
6128        // acquire rather than the full barrier the rest of that family is.
6129        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
6130        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
6131
6132        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6133        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
6134
6135        // Giving the lock back, which is one of the two names in the family that is handed no value
6136        // to put there, because what it puts there is a zero.
6137        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
6138        assert!(text.contains("release"), "{text}");
6139        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
6140
6141        // And with something after the pointer, which is the list of variables the call promises to
6142        // protect rather than a value to write. Reading it as a value would store whatever the
6143        // caller happened to name there, which is the one thing giving a lock back must not do.
6144        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
6145        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
6146        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6147
6148        // The bitwise four, which look no different here from the arithmetic ones: what the machine
6149        // has an instruction for is a question further down and this level does not ask it.
6150        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
6151        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
6152
6153        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
6154        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
6155        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
6156
6157        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
6158        // against every bit set because the IR has no not and that is what one is.
6159        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
6160        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
6161        assert!(text.contains("%3 = and %2, %1"), "{text}");
6162        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
6163        assert!(text.contains("%5 = xor %3, %4"), "{text}");
6164    }
6165
6166    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
6167    ///
6168    /// The shape is the one every architecture manual writes out by hand: read the word, work out
6169    /// what should be there instead, put it back if nothing else got in first, and go round again
6170    /// when something did. What is checked is that the loop is there at every width, that the
6171    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
6172    /// does.
6173    ///
6174    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
6175    /// value that was read.
6176    #[test]
6177    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
6178        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6179        for (ty, suffix, reg) in widths {
6180            for (name, call, insn) in [
6181                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
6182                ("or", "__sync_fetch_and_or(p, v)", "or"),
6183                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
6184            ] {
6185                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
6186                let text = asm(&source);
6187                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
6188                assert!(
6189                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
6190                    "{ty} {name}: {text}"
6191                );
6192                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
6193                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
6194                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
6195                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
6196            }
6197        }
6198        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
6199        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6200
6201        // The nand, which puts two instructions inside the loop rather than one. The flip is an
6202        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
6203        // machine has, which is what gcc writes here too.
6204        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
6205        assert!(text.contains("cmpxchgl\t"), "{text}");
6206        assert!(text.contains("andl\t"), "{text}");
6207        assert!(text.contains("notl\t"), "{text}");
6208    }
6209
6210    /// The three names that pass a value through a pointer are the same access and one plain one.
6211    ///
6212    /// They exist for an object too big to come back in a register, and the front end takes them at
6213    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
6214    /// the caller handed over somewhere to read from or write into and that is where the value has
6215    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
6216    /// pointer is the caller's own and no other thread has its address, which is what the whole
6217    /// shape is for.
6218    #[test]
6219    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
6220        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
6221        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
6222        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
6223
6224        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
6225        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
6226        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6227
6228        // The exchange, which reads through one pointer and writes through another and is the same
6229        // instruction in between as the spelling that takes and answers values.
6230        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
6231        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6232        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
6233        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
6234    }
6235
6236    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
6237    ///
6238    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
6239    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
6240    /// type the pointer carries says nothing about the access and the width is the implementation's
6241    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
6242    ///
6243    /// The answer is a comparison against zero rather than the byte itself, because the type of the
6244    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
6245    /// and the two agree wherever the flag is only ever touched through this pair.
6246    #[test]
6247    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
6248        for pointer in ["char", "int", "void"] {
6249            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
6250            let text = body(&source);
6251            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
6252            assert!(
6253                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
6254                "{pointer}: {text}"
6255            );
6256            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
6257
6258            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
6259            let text = body(&source);
6260            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
6261        }
6262
6263        // And on this machine, where the exchange carries no `lock` because one with memory locks
6264        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
6265        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
6266        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
6267        assert!(text.contains("setne\t"), "{text}");
6268    }
6269
6270    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
6271    /// an add, at the width of the object.
6272    ///
6273    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
6274    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
6275    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
6276    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
6277    #[test]
6278    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
6279        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
6280        for (ty, suffix, reg) in widths {
6281            let source =
6282                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
6283            let text = asm(&source);
6284            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6285            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6286
6287            let source =
6288                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
6289            let text = asm(&source);
6290            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6291            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
6292        }
6293        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
6294        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
6295
6296        // A subtraction is the same instruction over the negated operand, which is right at every
6297        // width because the machine's arithmetic wraps.
6298        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
6299        let text = asm(source);
6300        assert!(text.contains("negl\t"), "{text}");
6301        assert!(text.contains("xaddl\t"), "{text}");
6302
6303        // The ordering changes nothing, for the reason it changes nothing for a compare and
6304        // exchange: a locked instruction on this machine orders everything whatever it was asked.
6305        for order in ["0", "2", "3", "4", "5"] {
6306            let source =
6307                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
6308            let text = asm(&source);
6309            assert!(text.contains("xaddl\t"), "{order}: {text}");
6310            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6311        }
6312
6313        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
6314        // instruction: the exchange is one already and the store is a release, which this machine
6315        // gives away.
6316        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6317        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
6318        // The zero goes through a register on the way, which is where every constant this
6319        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
6320        // immediate and no rule here does. That is a rule this rule set is missing rather than
6321        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
6322        // The register gets its zero from an exclusive or with itself rather than from a move of a
6323        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
6324        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
6325        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
6326        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
6327        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
6328    }
6329
6330    /// The two lock free questions are numbers in the program rather than calls to anything.
6331    ///
6332    /// Both answer from the size, which has to be a power of two no wider than the widest access
6333    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
6334    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
6335    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
6336    ///
6337    /// The whole point of both names is that the answer is available before the program runs, so
6338    /// what is checked is that a `mov` of a constant is the whole function and that no call was
6339    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
6340    /// this links against.
6341    #[test]
6342    fn the_lock_free_questions_are_answered_as_constants() {
6343        for size in ["1", "2", "4", "8"] {
6344            let source =
6345                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
6346            let text = asm(&source);
6347            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
6348            assert!(!text.contains("call"), "and is not a call: {text}");
6349        }
6350        for size in ["3", "16", "sizeof(long double)"] {
6351            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
6352            let text = asm(&source);
6353            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
6354            assert!(!text.contains("call"), "and is not a call either: {text}");
6355        }
6356
6357        // A size the compiler cannot work out, which is no rather than a refusal, and an object
6358        // whose type is aligned under the size asked about, which is the whole of what the second
6359        // argument is for.
6360        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
6361        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
6362        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
6363        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
6364        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
6365        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
6366    }
6367
6368    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
6369    ///
6370    /// There are three ways the number is not one the operation can take: it is not a constant at
6371    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
6372    /// this operation, which is a release load or an acquire store. All three become sequential
6373    /// consistency, which is stronger than anything the program could have meant, so a program that
6374    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
6375    ///
6376    /// The last two also warn, because the number was written down and is wrong. The first does
6377    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
6378    /// on correct programs.
6379    #[test]
6380    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
6381        let mut opts = options();
6382        opts.emit = EmitKind::Ir;
6383
6384        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
6385        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
6386        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
6387
6388        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
6389        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
6390        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
6391
6392        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
6393        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
6394        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
6395    }
6396
6397    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
6398    ///
6399    /// Every other conversion between a float and an integer is the signed one at some width with a
6400    /// widening in front or a narrowing behind. These two are not, because there is no signed width
6401    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
6402    /// conversion with arithmetic around it that brings the value into range and puts it back.
6403    ///
6404    /// What is checked here is that the conversion happens at all and that it happens without a
6405    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
6406    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
6407    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
6408    #[test]
6409    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
6410        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
6411        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
6412        assert!(text.contains("shrq"), "with the value halved first: {text}");
6413        assert!(text.contains("addsd"), "and doubled after: {text}");
6414        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
6415
6416        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
6417        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
6418        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
6419        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
6420        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
6421    }
6422
6423    /// The plain names are the library's only where nothing else has taken them.
6424    ///
6425    /// Four ways a program says it means something else. A `static` definition is its own
6426    /// function and the name outside the file is somebody else's. A declaration of another type
6427    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
6428    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
6429    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
6430    ///
6431    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
6432    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
6433    #[test]
6434    fn a_plain_name_the_program_took_is_the_programs_own_function() {
6435        let taken = concat!(
6436            "static long long llabs(long long b) { return 7; }\n",
6437            "long long f(long long x) { return llabs(x); }\n",
6438        );
6439        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
6440
6441        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
6442        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
6443
6444        let plain = concat!(
6445            "long long llabs(long long b);\n",
6446            "long long f(long long x) { return llabs(x); }\n",
6447        );
6448        let mut opts = options();
6449        opts.emit = EmitKind::Ir;
6450        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
6451
6452        opts.builtins = false;
6453        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
6454
6455        opts.builtins = true;
6456        opts.no_builtin = vec!["llabs".to_owned()];
6457        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
6458        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
6459        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
6460
6461        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
6462        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
6463        opts.no_builtin = Vec::new();
6464        opts.builtins = false;
6465        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
6466        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
6467    }
6468
6469    /// The hint builtins are their first argument, and nothing is left of the hint.
6470    ///
6471    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
6472    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
6473    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
6474    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
6475    /// widens before it is answered with.
6476    ///
6477    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
6478    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
6479    /// where it is written and the hint goes with it, and a first argument that is not a constant
6480    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
6481    #[test]
6482    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
6483        let text = ir(concat!(
6484            "long a = __builtin_expect(7, 1);\n",
6485            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
6486            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
6487        ));
6488        assert!(text.contains("global @a : i64 = 7,"), "{text}");
6489        assert!(text.contains("global @b : i64 = 9,"), "{text}");
6490        assert!(text.contains("global @c : i64 = 8,"), "{text}");
6491        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
6492
6493        // A narrower argument is widened by the prototype before it is handed back, and it is
6494        // widened with its sign, since the parameter is a signed `long`.
6495        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
6496        assert!(text.contains("sext"), "{text}");
6497
6498        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
6499        // and neither is the third. What is left of each statement is the first argument widened,
6500        // which nothing reads and which the first pass that looks for dead code will take out.
6501        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
6502        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
6503        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
6504        assert_eq!(body(source), one);
6505
6506        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
6507        // an increment in the body and the value it returns is the load after it, which is what
6508        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
6509        // come out the same as the pair above.
6510        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
6511        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
6512        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
6513        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
6514        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
6515    }
6516
6517    /// A point control does not arrive at, in both of the ways the compiler has one.
6518    ///
6519    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
6520    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
6521    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
6522    /// for both of the functions below and nothing else, and the two of them come out byte for
6523    /// byte the same there.
6524    ///
6525    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
6526    /// there because a function whose last instruction is not a return is one that falls into
6527    /// whatever the assembler puts after it.
6528    #[test]
6529    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
6530        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
6531        let text = ir(promised);
6532        assert!(text.contains("    unreachable_hint\n"), "{text}");
6533        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
6534
6535        // The statement after it is still lowered. Continuing to translate a path the program
6536        // promised is dead is one of the things a compiler may do with undefined behaviour, and
6537        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
6538        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
6539        assert!(after.contains("return"), "{after}");
6540
6541        // Both functions are the same instructions, because the hint writes none of them and the
6542        // terminator underneath it writes none either.
6543        let text = asm(promised);
6544        let mine = text.split_once("\nf:\n").expect("a definition").1;
6545        let mine = mine.split_once("\t.size").expect("a definition").0;
6546        let plain = asm("int f(int x) { if (x) return 1; }\n");
6547        let plain = plain.split_once("\nf:\n").expect("a definition").1;
6548        let plain = plain.split_once("\t.size").expect("a definition").0;
6549        assert_eq!(mine, plain);
6550        // The last instruction, rather than the last line, because the unwind record is closed
6551        // after it and a directive is not something the machine runs.
6552        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
6553        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
6554        assert!(!mine.contains("ud2"), "{mine}");
6555    }
6556
6557    /// The two names stay apart, which is what having both of them is for.
6558    ///
6559    /// The one the program wrote is what the call is checked against and what a diagnostic about
6560    /// it says, and the one the library defines is what the call ends up carrying. A compiler
6561    /// that kept only the second would report this against `abort`, which is a function the
6562    /// program never mentions.
6563    #[test]
6564    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
6565        let mut opts = options();
6566        opts.emit = EmitKind::Ir;
6567        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
6568        assert!(
6569            messages.iter().any(|m| m.contains("__builtin_abort")),
6570            "expected the written name in {messages:?}"
6571        );
6572    }
6573
6574    /// A builtin nothing lowers is refused where it is written, rather than at the link.
6575    ///
6576    /// One name is left, which is the last of the atomic family that is refused and is also the
6577    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
6578    /// does the half of the family that carries a prototype. What the message has to carry is the
6579    /// name, because the whole complaint about the link error this replaces is that the name in it
6580    /// was one the compiler chose.
6581    #[test]
6582    fn a_builtin_nothing_lowers_is_refused_by_name() {
6583        let mut opts = options();
6584        opts.emit = EmitKind::Ir;
6585        let builtin = "__atomic_signal_fence";
6586        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
6587        let messages = run(&opts, &source).messages;
6588        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
6589        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
6590    }
6591
6592    /// The refusal is about a call and not about the name, so a program that defines the name
6593    /// itself gets the function it wrote.
6594    ///
6595    /// That is not the reason the refusal exists, but a definition in front of us is a definition
6596    /// and the call to it links. It works here because the name is one with no prototype and no
6597    /// meaning the front end knows, which is what is left once the rest of the family is
6598    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
6599    /// declares, the way gcc answers one.
6600    #[test]
6601    fn what_is_refused_is_the_call_and_not_the_name() {
6602        let text = ir(concat!(
6603            "void __atomic_signal_fence(int order) { (void)order; }\n",
6604            "void f(void) { __atomic_signal_fence(5); }\n",
6605        ));
6606        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
6607    }
6608
6609    /// How many bytes are behind an address is read off the layout, for every shape the walk
6610    /// covers.
6611    ///
6612    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
6613    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
6614    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
6615    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
6616    /// output and the test reads as the table it is.
6617    #[test]
6618    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
6619        let text = ir(concat!(
6620            "struct S { char a[8]; int n; char b[12]; };\n",
6621            "char g[32];\n",
6622            "struct S gs;\n",
6623            "unsigned long whole = __builtin_object_size(g, 0);\n",
6624            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
6625            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
6626            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
6627            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
6628            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
6629            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
6630            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
6631            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
6632            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
6633        ));
6634        for (name, size) in [
6635            ("whole", 32),
6636            ("moved", 28),
6637            ("back", 4),
6638            ("outer", 24),
6639            ("inner", 8),
6640            ("scalar", 4),
6641            ("after", 16),
6642            ("into", 10),
6643            ("text", 6),
6644            ("dyn", 12),
6645        ] {
6646            let said = format!("global @{name} : i64 = {size},");
6647            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6648        }
6649    }
6650
6651    /// A local is as knowable as a global, which is the whole point of asking on the way into a
6652    /// copy.
6653    ///
6654    /// A fortified header expands around the destination the caller wrote, and the destination a
6655    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
6656    /// storage duration, unlike in a constant expression, where the address of a local is exactly
6657    /// what is not allowed.
6658    #[test]
6659    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
6660        let text = body(concat!(
6661            "struct S { char a[8]; int n; char b[12]; };\n",
6662            "unsigned long f(void) {\n",
6663            "  char loc[20];\n",
6664            "  struct S ls;\n",
6665            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
6666            "}\n",
6667        ));
6668        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
6669        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
6670    }
6671
6672    /// An address whose object the walk cannot see answers at whichever end of the range the kind
6673    /// asks for.
6674    ///
6675    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
6676    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
6677    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
6678    /// and zero. That pair is what a fortified header compares against to decide whether to check
6679    /// at all, and getting either of them the wrong way round turns every unknown copy into an
6680    /// abort.
6681    #[test]
6682    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
6683        let text = ir(concat!(
6684            "struct T { int n; char f[]; };\n",
6685            "extern char *p;\n",
6686            "extern struct T *t;\n",
6687            "unsigned long largest = __builtin_object_size(p, 0);\n",
6688            "unsigned long nearest = __builtin_object_size(p, 1);\n",
6689            "unsigned long least = __builtin_object_size(p, 2);\n",
6690            "unsigned long tight = __builtin_object_size(p, 3);\n",
6691            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
6692            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
6693        ));
6694        for name in ["largest", "nearest", "flex"] {
6695            // All ones, printed as the signed rendering of the sixty four bits it is held in.
6696            // `says` is what pins the pattern itself, since it is the comparison a fortified
6697            // header writes and it folds only if every bit is set.
6698            let said = format!("global @{name} : i64 = -1,");
6699            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6700        }
6701        for name in ["least", "tight"] {
6702            let said = format!("global @{name} : i64 = 0,");
6703            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6704        }
6705        assert!(text.contains("global @says : i32 = 1,"), "{text}");
6706    }
6707
6708    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
6709    ///
6710    /// What the builtin reads is the shape of the expression rather than the value it would
6711    /// produce, so there is nothing to run. It matters because a fortified header writes the
6712    /// destination twice, once into the copy and once into the size, and a program whose
6713    /// destination is `*next()` would advance twice if this evaluated.
6714    #[test]
6715    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
6716        let text = body(concat!(
6717            "extern char *side(void);\n",
6718            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
6719        ));
6720        assert!(!text.contains("call"), "nothing is called: {text}");
6721    }
6722
6723    /// The kind has to be a constant in range, because it says which of four questions was asked.
6724    ///
6725    /// A number that is not known until the program runs decides nothing, and one outside the two
6726    /// bits names no question at all. gcc refuses both in one sentence and so does this.
6727    #[test]
6728    fn a_kind_that_is_not_one_of_the_four_is_refused() {
6729        for source in [
6730            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
6731                + "{ return __builtin_object_size(p, k); }\n",
6732            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
6733                .to_owned(),
6734            "extern char *p;\nunsigned long f(void) ".to_owned()
6735                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
6736        ] {
6737            let messages = errors(&source);
6738            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
6739            assert!(named, "expected a complaint about the kind in {messages:?}");
6740        }
6741    }
6742
6743    /// The pair that saves a place in a function and comes back to it, which is not a call.
6744    ///
6745    /// What the IR has to show is one instruction each and no call to anything: there is no
6746    /// function of either name for a call to reach, and a program that got one would fail to link.
6747    /// The save answers an `int`, which is the value that says how control got there.
6748    #[test]
6749    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
6750        let text = ir(concat!(
6751            "void *buf[5];\n",
6752            "int f(void) {\n",
6753            "  if (__builtin_setjmp(buf)) return 2;\n",
6754            "  return 1;\n",
6755            "}\n",
6756            "void g(void) { __builtin_longjmp(buf, 1); }\n",
6757        ));
6758        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
6759        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
6760        assert!(!text.contains("call @"), "neither of them is a call: {text}");
6761    }
6762
6763    /// Every local of a function that saves a place lives in the frame, and not in a value.
6764    ///
6765    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
6766    /// renamed would answer the write that reached the read along the edges there are rather than
6767    /// the write that last ran. The second function here is the same code without the save, where
6768    /// the local is a value and there is no slot at all, which is what makes the first one a rule
6769    /// about the save and not about the shape of the code.
6770    #[test]
6771    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
6772        let text = ir(concat!(
6773            "void *buf[5];\n",
6774            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
6775            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
6776        ));
6777        let (saves, plain) = text.split_once("func @g").expect("both functions");
6778        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
6779        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
6780        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
6781    }
6782
6783    /// What the save writes and where it leaves control, which is a new block.
6784    ///
6785    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
6786    /// address of the word the answer arrives in, which is this compiler's own and is why the
6787    /// block after the save opens with a load. The frame pointer is kept although the function
6788    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
6789    /// after control has come back, and the frame is grown although there is one word in it,
6790    /// since a function control comes back into cannot use the red zone.
6791    #[test]
6792    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
6793        let text =
6794            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
6795        let body = text.split_once("\nf:\n").expect("the function").1;
6796        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
6797        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
6798        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
6799        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
6800        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
6801        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
6802        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
6803        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
6804    }
6805
6806    /// Nothing stays in a register across the save, which is said with a write of every one of
6807    /// them and shows up as the callee-saved registers the function saves and restores.
6808    ///
6809    /// The restore puts back two registers and no others, so a function coming back through one
6810    /// finds every other register holding whatever the code between the two put there. The pushes
6811    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
6812    /// stack the restore put back, rather than whatever is in the registers when control arrives.
6813    #[test]
6814    fn a_save_destroys_every_register_the_allocator_hands_out() {
6815        let text =
6816            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
6817        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
6818            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
6819            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
6820        }
6821    }
6822
6823    /// The restore puts both registers back before it goes, at every level.
6824    ///
6825    /// The jump reads the two of them as well as the address it goes through, which is what keeps
6826    /// it behind them. Without that the two instructions write registers nothing reads, and the
6827    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
6828    /// that is not there.
6829    #[test]
6830    fn the_restore_puts_the_frame_back_before_it_jumps() {
6831        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
6832            let mut opts = options();
6833            opts.emit = EmitKind::Asm;
6834            opts.opt_level = level;
6835            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
6836            let result = run(&opts, source);
6837            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
6838            let text = result.text().to_owned();
6839            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
6840            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
6841            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
6842            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
6843            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
6844        }
6845    }
6846
6847    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
6848    ///
6849    /// This pair does not carry a value back the way the library's `longjmp` does, because what
6850    /// the matching save answers is decided by which way control reached it. So the argument is a
6851    /// place-holder, and a program that wrote anything else meant the library's function.
6852    #[test]
6853    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
6854        for source in [
6855            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
6856            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
6857        ] {
6858            let messages = errors(source);
6859            let named = messages.iter().any(|m| m.contains("E0710"));
6860            assert!(named, "expected a complaint about the value in {messages:?}");
6861        }
6862    }
6863
6864    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
6865    ///
6866    /// The pair is written as one program so that the two answers come out of one walk. What
6867    /// makes the difference is the call in `main` and nothing else about either definition.
6868    #[test]
6869    fn a_static_function_nothing_refers_to_is_not_emitted() {
6870        let text = ir("static int dropped(void) { return 1; }\n\
6871                       static int kept(void) { return 2; }\n\
6872                       int main(void) { return kept(); }\n");
6873        assert!(text.contains("func @kept"), "{text}");
6874        assert!(!text.contains("dropped"), "{text}");
6875    }
6876
6877    /// The set is transitive, so two of them that only call each other are both dropped.
6878    ///
6879    /// Counting the references to a name would keep this pair, since each is named once, and
6880    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
6881    /// definition, and a root is something the file has a reason to emit on its own.
6882    #[test]
6883    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
6884        let text = ir("static int ping(void);\n\
6885                       static int pong(void) { return ping(); }\n\
6886                       static int ping(void) { return pong(); }\n\
6887                       int main(void) { return 0; }\n");
6888        assert!(!text.contains("ping"), "{text}");
6889        assert!(!text.contains("pong"), "{text}");
6890    }
6891
6892    /// Everything that names a function keeps it, whether or not the name is being called.
6893    ///
6894    /// An address taken in a body, an image that holds one, and a body that is only reached
6895    /// through another `static` function are three different ways for a definition to be needed
6896    /// and none of them is a call at the top level of a reachable function.
6897    #[test]
6898    fn naming_a_static_function_anywhere_keeps_it() {
6899        let text = ir("static int by_address(void) { return 1; }\n\
6900                       static int in_an_image(void) { return 2; }\n\
6901                       static int deeper(void) { return 3; }\n\
6902                       static int reaches_deeper(void) { return deeper(); }\n\
6903                       static int (*table[1])(void) = {in_an_image};\n\
6904                       int main(void) {\n\
6905                         int (*p)(void) = by_address;\n\
6906                         return p() + table[0]() + reaches_deeper();\n\
6907                       }\n");
6908        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
6909            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
6910        }
6911    }
6912
6913    /// An attribute that says something outside the file reaches it keeps the definition.
6914    ///
6915    /// None of the five is implemented as anything else yet, and this is the part of each of
6916    /// them that a program notices first: a symbol a linker script names or a function the
6917    /// run-up to `main` calls is not written about anywhere a C file can see.
6918    #[test]
6919    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
6920        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
6921            let source = format!(
6922                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
6923                 int main(void) {{ return 0; }}\n"
6924            );
6925            let text = ir(&source);
6926            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
6927        }
6928    }
6929
6930    /// A function with external linkage is emitted whatever this file does with it, because
6931    /// another one may call it, and that is what external linkage is.
6932    #[test]
6933    fn a_function_anything_could_call_is_emitted_without_being_called() {
6934        let text =
6935            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
6936        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
6937    }
6938
6939    /// Four of the classification builtins are operators C already has, and become those.
6940    ///
6941    /// What the standard's macro promises over the operator is that it does not raise the
6942    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
6943    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
6944    /// spelling a comparison would be a second thing every pass has to know about.
6945    #[test]
6946    fn a_classification_c_has_an_operator_for_is_that_operator() {
6947        for (builtin, operator) in [
6948            ("__builtin_isgreater", "binary >"),
6949            ("__builtin_isgreaterequal", "binary >="),
6950            ("__builtin_isless", "binary <"),
6951            ("__builtin_islessequal", "binary <="),
6952        ] {
6953            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
6954            let text = tast(&source);
6955            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
6956        }
6957    }
6958
6959    /// The rest of the family are comparisons in the IR and never a call to anything.
6960    ///
6961    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
6962    /// there is no function under any of them for a call to reach. `isunordered` and
6963    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
6964    /// is unordered with itself, and the two that ask about a magnitude are written against the
6965    /// infinities. `signbit` is the one that is not a question about the value, since a negative
6966    /// zero compares equal to a positive one, so its answer comes from the bits.
6967    #[test]
6968    fn the_classification_builtins_are_comparisons_and_not_calls() {
6969        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
6970        assert_eq!(
6971            text,
6972            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
6973                          %2\n    return %3\n"
6974        );
6975
6976        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
6977        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
6978        assert!(text.contains("fcmp one %0, %1"), "{text}");
6979
6980        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
6981        assert!(text.contains("fcmp uno %0, %0"), "{text}");
6982
6983        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
6984        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
6985        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
6986        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
6987        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
6988        assert!(text.contains("%5 = or %3, %4"), "{text}");
6989
6990        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
6991        // against either of them is false. That is what makes this one test rather than two.
6992        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
6993        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
6994        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
6995        assert!(text.contains("%5 = and %3, %4"), "{text}");
6996
6997        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
6998        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
6999        assert!(text.contains("icmp slt %1, %2"), "{text}");
7000
7001        // The same question of a value in the target's widest format, where the bits are eighty
7002        // and the object they sit in is sixteen bytes. No integer is that wide, so the sign is
7003        // read from the word at the top of the value once it is in memory.
7004        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
7005        assert!(text.contains("load.i16"), "{text}");
7006        assert!(text.contains("icmp slt"), "{text}");
7007        assert!(!text.contains("i80"), "{text}");
7008
7009        // The operand is evaluated once however many times it is compared, which is the whole
7010        // reason these are nodes rather than a rewriting into the operators.
7011        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
7012        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7013    }
7014
7015    /// A spelling that names a width converts its argument before it asks.
7016    ///
7017    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
7018    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
7019    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
7020    /// here are what gcc 16 gives.
7021    #[test]
7022    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
7023        let text = ir(concat!(
7024            "int a = __builtin_isinff(1e300);\n",
7025            "int b = __builtin_isinf(1e300);\n",
7026            // Folded here rather than compared at run time, because a question about a value has
7027            // an answer as soon as the value is a constant, and an initializer for an object
7028            // with static storage duration has to have one.
7029            "int c = __builtin_isnan(0.0);\n",
7030            "int d = __builtin_signbit(-0.0);\n",
7031            "int e = __builtin_islessgreater(1.0, 2.0);\n",
7032        ));
7033        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7034        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7035        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7036        assert!(text.contains("global @d : i32 = 1,"), "{text}");
7037        assert!(text.contains("global @e : i32 = 1,"), "{text}");
7038    }
7039
7040    /// An argument that is not floating point is refused, in gcc's words.
7041    #[test]
7042    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
7043        let mut opts = options();
7044        opts.emit = EmitKind::Ir;
7045        let source = concat!(
7046            "int a(int x) { return __builtin_isnan(x); }\n",
7047            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
7048            "int c(double x) { return __builtin_isnan(x, x); }\n",
7049        );
7050        let messages = run(&opts, source).messages;
7051        assert_eq!(
7052            messages,
7053            [
7054                "/main.c:1:23: error: non-floating-point argument in call to function \
7055                 '__builtin_isnan' [E0685]",
7056                "/main.c:2:30: error: non-floating-point arguments in call to function \
7057                 '__builtin_isunordered' [E0685]",
7058                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
7059            ]
7060        );
7061    }
7062
7063    /// The three of the family that need a constant of the format other than an infinity.
7064    ///
7065    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
7066    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
7067    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
7068    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
7069    /// and the picking is a mask because all five are constants and neither of them can have an
7070    /// effect.
7071    #[test]
7072    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
7073        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
7074        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
7075        // of the number, since the encoding of a value whose sign bit is clear rises with the
7076        // value in every format this compiles for.
7077        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
7078        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
7079        assert!(text.contains("%3 = and %1, %2"), "{text}");
7080        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
7081        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
7082        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
7083        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
7084        assert!(text.contains("%8 = and %6, %7"), "{text}");
7085
7086        // The same question in the target's widest format, where the smallest normal has the
7087        // leading significand bit stored rather than implied, so its encoding is two bits and not
7088        // one. There is no integer that wide to compare the bits in, so it is the magnitude that
7089        // is compared, as a value.
7090        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
7091        assert!(text.contains("fconst.f80 0x18000000000000000"), "{text}");
7092        assert!(text.contains("fconst.f80 0x7fff8000000000000000"), "{text}");
7093        assert!(text.contains("fcmp oge"), "{text}");
7094        assert!(text.contains("fcmp olt"), "{text}");
7095
7096        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
7097        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
7098        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
7099        assert!(text.contains("%7 = sub %5, %6"), "{text}");
7100
7101        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
7102        assert!(text.contains("fcmp uno %0, %0"), "{text}");
7103        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
7104        // Four questions, each of them a bit widened into the type of the answer and then spread
7105        // into a mask that picks between the answer and whatever the questions after it settled
7106        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
7107        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
7108        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
7109        assert!(!text.contains("call"), "{text}");
7110
7111        // The value is evaluated once however many questions are asked of it, which is the whole
7112        // reason `fpclassify` is a node rather than the chain of tests it turns into.
7113        let text = body(concat!(
7114            "double g(void);\n",
7115            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
7116        ));
7117        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7118    }
7119
7120    /// Each of the three answers a constant where its operand is one.
7121    ///
7122    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
7123    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
7124    /// translation time or the program is refused rather than merely compiled slowly. Every
7125    /// number here is what gcc 16 gives.
7126    #[test]
7127    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
7128        let text = ir(concat!(
7129            "int a = __builtin_isnormal(1.0);\n",
7130            "int b = __builtin_isnormal(0.0);\n",
7131            "int c = __builtin_isnormal(1.0 / 0.0);\n",
7132            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
7133            "int e = __builtin_isinf_sign(1.0);\n",
7134            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
7135            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
7136            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
7137        ));
7138        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7139        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7140        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7141        assert!(text.contains("global @d : i32 = -1,"), "{text}");
7142        assert!(text.contains("global @e : i32 = 0,"), "{text}");
7143        assert!(text.contains("global @g : i32 = 4,"), "{text}");
7144        assert!(text.contains("global @h : i32 = 2,"), "{text}");
7145        assert!(text.contains("global @i : i32 = 1,"), "{text}");
7146    }
7147
7148    /// `fpclassify` refuses what gcc refuses, in gcc's words.
7149    ///
7150    /// The five answers have to be integer constant expressions, because what the builtin does is
7151    /// pick one of them and a pick between values that are not known here would be a chain of
7152    /// conditionals over expressions the call has already evaluated.
7153    #[test]
7154    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
7155        let mut opts = options();
7156        opts.emit = EmitKind::Ir;
7157        let source = concat!(
7158            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
7159            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
7160            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
7161        );
7162        let messages = run(&opts, source).messages;
7163        assert_eq!(
7164            messages,
7165            [
7166                "/main.c:1:60: error: non-const integer argument 3 in call to function \
7167                 '__builtin_fpclassify' [E0687]",
7168                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
7169                 [E0511]",
7170                "/main.c:3:23: error: non-floating-point argument in call to function \
7171                 '__builtin_fpclassify' [E0685]",
7172            ]
7173        );
7174    }
7175
7176    /// A builtin whose answer is a constant is one, and is not a call to the library.
7177    ///
7178    /// This is the reason the family is answered in the front end at all. `double x =
7179    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
7180    /// there is no point in the program at which a call could be made, and a compiler that
7181    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
7182    /// gcc 16 gives on x86-64.
7183    #[test]
7184    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
7185        let text = ir(concat!(
7186            "double a = __builtin_inf();\n",
7187            "float b = __builtin_huge_valf();\n",
7188            "long double c = __builtin_infl();\n",
7189            "double d = __builtin_huge_val();\n",
7190        ));
7191        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
7192        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
7193        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
7194        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
7195        assert!(!text.contains("call"), "{text}");
7196    }
7197
7198    /// A nan is written with the payload the program asked for.
7199    ///
7200    /// The string is read the way `strtoull` reads a number, which is what the library function
7201    /// of the same name does with it, and a string that is not one at all leaves the call for the
7202    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
7203    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
7204    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
7205    /// `long double` ones on a machine with the x87 format.
7206    #[test]
7207    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
7208        let text = ir(concat!(
7209            "double a = __builtin_nan(\"\");\n",
7210            "double b = __builtin_nan(\"0x1\");\n",
7211            // Octal, since there is a leading zero, so this is eight and not ten.
7212            "double c = __builtin_nan(\"010\");\n",
7213            "double d = __builtin_nans(\"\");\n",
7214            "double e = __builtin_nans(\"0x1\");\n",
7215            "float f = __builtin_nanf(\"0x1\");\n",
7216            "float g = __builtin_nansf(\"\");\n",
7217            "long double h = __builtin_nansl(\"\");\n",
7218        ));
7219        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
7220        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
7221        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
7222        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
7223        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
7224        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
7225        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
7226        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
7227
7228        // A payload that is not a number, and one that is not known until run time, are both
7229        // left to the library, which is the same thing gcc emits for either of them.
7230        let text = ir(concat!(
7231            "double f(const char *p) { return __builtin_nan(p); }\n",
7232            "double g(void) { return __builtin_nans(\"1x\"); }\n",
7233        ));
7234        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
7235        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
7236    }
7237
7238    /// The length and the order of a string literal are known here.
7239    ///
7240    /// A program that asks for either of them is asking about something the translation already
7241    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
7242    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
7243    /// different signature, so leaving the call behind is a name collision that gcc does not
7244    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
7245    #[test]
7246    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
7247        let text = ir(concat!(
7248            "unsigned long a = __builtin_strlen(\"hello\");\n",
7249            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
7250            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
7251            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
7252            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
7253        ));
7254        assert!(text.contains("global @a : i64 = 5,"), "{text}");
7255        assert!(text.contains("global @b : i64 = 1,"), "{text}");
7256        assert!(text.contains("global @c : i32 = 1,"), "{text}");
7257        assert!(text.contains("global @d : i32 = 0,"), "{text}");
7258        assert!(text.contains("global @e : i32 = 1,"), "{text}");
7259        assert!(!text.contains("call"), "{text}");
7260
7261        // An argument that is not a literal is the library's to answer, as it has to be.
7262        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
7263        assert!(text.contains("call @strlen("), "{text}");
7264    }
7265
7266    /// A sign builtin is a mask over the bits, and is not a call.
7267    ///
7268    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
7269    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
7270    /// would not link. Neither needs anything the library has: one clears the sign bit and the
7271    /// other takes it from the second operand, and every other bit goes through untouched.
7272    #[test]
7273    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
7274        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
7275        assert!(text.contains("bitcast.i64 %0"), "{text}");
7276        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
7277        assert!(text.contains("and %1, %2"), "{text}");
7278        assert!(text.contains("bitcast.f64 %3"), "{text}");
7279        assert!(!text.contains("call"), "{text}");
7280
7281        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
7282        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
7283        assert!(text.contains("%8 = or %4, %7"), "{text}");
7284        assert!(!text.contains("call"), "{text}");
7285
7286        // The x87 format, whose value is eighty bits sitting in an object of sixteen. There is no
7287        // integer that wide, so the mask is on the word at the top of the value, in memory.
7288        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
7289        assert!(text.contains("iconst.i16 32767"), "{text}");
7290        assert!(text.contains("load.f80"), "{text}");
7291        assert!(!text.contains("call"), "{text}");
7292
7293        // The width a name does not spell out is `double`, so a `float` argument widens first and
7294        // the answer is a `double`, which is what gcc's declaration of it says.
7295        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
7296        assert!(text.contains("fpext.f64 %0"), "{text}");
7297        assert!(text.contains("bitcast.i64 %1"), "{text}");
7298    }
7299
7300    /// A shuffle reads each lane of the answer out of a copy of its sources, at the index the mask
7301    /// lane gives with only its low bits kept, and is not a call.
7302    ///
7303    /// The copy is what makes `*v = __builtin_shuffle(*v, m)` right, since the answer is written
7304    /// over the vector it reads, and the mask is what `pr85331.c` checks: gcc keeps as many bits
7305    /// of an index as it takes to name a lane, so `10000000001` picks lane one of two.
7306    #[test]
7307    fn a_shuffle_picks_each_lane_by_the_low_bits_of_the_mask() {
7308        let text = body(concat!(
7309            "typedef int v2 __attribute__((vector_size(8)));\n",
7310            "void f(v2 *v, v2 m) { *v = __builtin_shuffle(*v, m); }\n",
7311        ));
7312        assert!(text.contains("memcpy"), "{text}");
7313        assert_eq!(text.matches("iconst.i32 1\n").count(), 2, "{text}");
7314        assert_eq!(text.matches(" = and ").count(), 2, "{text}");
7315        assert!(!text.contains("call"), "{text}");
7316
7317        // Two sources of four lanes are eight to pick from, so three bits of each index are
7318        // kept, and a mask of bytes is widened to a word before it is masked.
7319        let text = body(concat!(
7320            "typedef char v4 __attribute__((vector_size(4)));\n",
7321            "v4 f(v4 a, v4 b, v4 m) { return __builtin_shuffle(a, b, m); }\n",
7322        ));
7323        assert_eq!(text.matches("iconst.i32 7\n").count(), 4, "{text}");
7324        assert!(text.contains("zext.i32"), "{text}");
7325        assert!(!text.contains("call"), "{text}");
7326    }
7327
7328    /// A function holding `__builtin_apply_args` writes every argument register into its frame
7329    /// before anything else runs, the ones its parameters took as well as the ones they did not,
7330    /// and the answer is the address of where it wrote them.
7331    #[test]
7332    fn the_arguments_a_function_was_called_with_are_saved_on_the_way_in() {
7333        let text =
7334            mir("void *f(int a, double b) { (void)a; (void)b; return __builtin_apply_args(); }\n");
7335        // Six words and the address the arguments in memory start at, and eight vectors.
7336        assert!(text.matches("x64.mov_mr_64").count() >= 7, "{text}");
7337        assert!(text.matches("x64.movaps_mr").count() >= 8, "{text}");
7338        for reg in ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9", "$xmm0", "$xmm7"] {
7339            assert!(text.contains(reg), "{reg} is not saved in\n{text}");
7340        }
7341
7342        // And a function without one saves nothing.
7343        let text = mir("int f(int a) { return a; }\n");
7344        assert!(!text.contains("movaps_mr"), "{text}");
7345    }
7346
7347    /// `__builtin_apply` loads every argument register out of the block it is given, copies the
7348    /// bytes of arguments in memory it was told about, and calls through the address, with eight
7349    /// in `%al` since every vector register may hold an argument.
7350    #[test]
7351    fn a_call_built_from_saved_arguments_loads_every_argument_register() {
7352        let text = mir(concat!(
7353            "void *g(void *args, void (*h)()) {\n",
7354            "  return __builtin_apply(h, args, 64);\n",
7355            "}\n",
7356        ));
7357        assert!(text.matches("x64.mov_rm_64").count() >= 7, "{text}");
7358        assert!(text.matches("x64.movaps_rm").count() >= 8, "{text}");
7359        assert!(text.contains("call"), "{text}");
7360        // What came back is written out, two words and two vectors.
7361        assert!(text.matches("x64.movaps_mr").count() >= 2, "{text}");
7362
7363        // The size is a number the frame can be laid out with, and nothing else is.
7364        let mut opts = options();
7365        opts.emit = EmitKind::Ir;
7366        let result = run(
7367            &opts,
7368            "void *g(void *a, void (*h)(), int n) { return __builtin_apply(h, a, n); }\n",
7369        );
7370        assert!(result.failed(), "{:?}", result.messages);
7371        assert!(
7372            result
7373                .messages
7374                .iter()
7375                .any(|m| m.contains("the size given to '__builtin_apply' is a constant")),
7376            "{:?}",
7377            result.messages
7378        );
7379    }
7380
7381    /// A shuffle whose operands gcc would refuse is refused, in gcc's words.
7382    #[test]
7383    fn a_shuffle_refuses_what_gcc_refuses() {
7384        let mut opts = options();
7385        opts.emit = EmitKind::Ir;
7386        let source = concat!(
7387            "typedef int v4 __attribute__((vector_size(16)));\n",
7388            "typedef float f4 __attribute__((vector_size(16)));\n",
7389            "typedef short s8 __attribute__((vector_size(16)));\n",
7390            "typedef long long l4 __attribute__((vector_size(32)));\n",
7391            "void a(v4 x, f4 m) { __builtin_shuffle(x, m); }\n",
7392            "void b(int x, v4 m) { __builtin_shuffle(x, m); }\n",
7393            "void c(v4 x, f4 y, v4 m) { __builtin_shuffle(x, y, m); }\n",
7394            "void d(v4 x, s8 m) { __builtin_shuffle(x, m); }\n",
7395            "void e(f4 x, l4 m) { __builtin_shuffle(x, m); }\n",
7396            "void g(v4 x) { __builtin_shuffle(x); }\n",
7397        );
7398        let messages = run(&opts, source).messages;
7399        let wanted = [
7400            "last argument must be an integer vector [E0715]",
7401            "arguments must be vectors [E0715]",
7402            "argument vectors must be of the same type [E0715]",
7403            "number of elements of the argument vector(s) and the mask vector should be the same \
7404             [E0715]",
7405            "argument vector(s) inner type must have the same size as inner type of the mask \
7406             [E0715]",
7407            "too few arguments to function '__builtin_shuffle' [E0511]",
7408        ];
7409        assert_eq!(messages.len(), wanted.len(), "{messages:?}");
7410        for (message, wanted) in messages.iter().zip(wanted) {
7411            assert!(message.ends_with(wanted), "{message}");
7412        }
7413    }
7414
7415    /// The plain math library names are the same mask, which is what makes a program link.
7416    ///
7417    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
7418    /// every program that includes the header reaches. Recognising only the prefixed spelling
7419    /// leaves a call to the math library behind, and the math library is not on the link line
7420    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
7421    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
7422    /// build stopped. That is issue 630.
7423    #[test]
7424    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
7425        let text =
7426            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
7427        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
7428        assert!(!text.contains("call"), "{text}");
7429
7430        let text =
7431            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
7432        assert!(text.contains("bitcast.i32 %0"), "{text}");
7433        assert!(!text.contains("call"), "{text}");
7434
7435        let text = body(concat!(
7436            "double copysign(double x, double y);\n",
7437            "double f(double x, double y) { return copysign(x, y); }\n",
7438        ));
7439        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
7440        assert!(!text.contains("call"), "{text}");
7441
7442        let text = body(concat!(
7443            "float copysignf(float x, float y);\n",
7444            "float f(float x, float y) { return copysignf(x, y); }\n",
7445        ));
7446        assert!(!text.contains("call"), "{text}");
7447
7448        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
7449        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
7450        // name would trade a link error for a worse one. They go in with issue 540.
7451        let text = ir(concat!(
7452            "long double fabsl(long double x);\n",
7453            "long double f(long double x) { return fabsl(x); }\n",
7454        ));
7455        assert!(text.contains("call @fabsl"), "{text}");
7456    }
7457
7458    /// A plain math name the program took is the program's own function.
7459    ///
7460    /// The same four ways as the absolute value family next door, asked again here because these
7461    /// two go through a different path: the plain names of this family are taken after the call
7462    /// has been checked against the declaration, and the declaration is the whole reason the
7463    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
7464    /// function in every one of them.
7465    #[test]
7466    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
7467        let taken = concat!(
7468            "static double fabs(double b) { return 7; }\n",
7469            "double f(double x) { return fabs(x); }\n",
7470        );
7471        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
7472
7473        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
7474        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
7475
7476        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
7477        let mut opts = options();
7478        opts.emit = EmitKind::Ir;
7479        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
7480
7481        opts.builtins = false;
7482        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
7483
7484        opts.builtins = true;
7485        opts.no_builtin = vec!["fabs".to_owned()];
7486        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
7487        let one = concat!(
7488            "double copysign(double a, double b);\n",
7489            "double f(double x) { return copysign(x, 1.0); }\n",
7490        );
7491        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
7492
7493        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
7494        opts.no_builtin = Vec::new();
7495        opts.builtins = false;
7496        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
7497        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
7498    }
7499
7500    /// The sign builtins answer a zero and a nan the way the bits say.
7501    ///
7502    /// This is why they are described over the bits rather than written with comparisons and
7503    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
7504    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
7505    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
7506    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
7507    /// x87 format measured on a machine that has it.
7508    #[test]
7509    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
7510        let text = ir(concat!(
7511            "double a = __builtin_fabs(-3.5);\n",
7512            "double b = __builtin_copysign(1.0, -0.0);\n",
7513            "double c = __builtin_copysign(0.0, -2.0);\n",
7514            // The payload survives both, and only the sign bit moves.
7515            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
7516            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
7517            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
7518            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
7519            "long double i = __builtin_fabsl(-__builtin_infl());\n",
7520        ));
7521        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
7522        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
7523        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
7524        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
7525        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
7526        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
7527        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
7528        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
7529    }
7530
7531    /// The sign of a `long double` is read and written in the word at the top of it.
7532    ///
7533    /// The other formats have their sign tested and set on an integer as wide as the value, and
7534    /// there is no eighty bit integer for the x87 one to go to: no rule lowers it, and
7535    /// `execute/20080502-1.c` and `execute/ieee/copysign1.c` in the torture suite stopped on that.
7536    /// The value goes through memory instead, and the word holding its sign is what is looked at.
7537    #[test]
7538    fn the_sign_of_a_long_double_is_in_the_word_at_the_top_of_it() {
7539        for source in [
7540            "int f(long double x) { return __builtin_signbit(x); }\n",
7541            "long double f(long double x) { return __builtin_fabsl(x); }\n",
7542            "long double f(long double x, long double y) { return __builtin_copysignl(x, y); }\n",
7543            "int f(long double x) { return __builtin_isnormal(x); }\n",
7544        ] {
7545            let text = body(source);
7546            assert!(!text.contains("i80"), "{text}");
7547            assert!(text.contains("i16"), "{text}");
7548        }
7549    }
7550
7551    /// The complex builtins are the halves of the value, and are not a call.
7552    ///
7553    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
7554    /// gives them, so there is nothing for the math library to do that the translation cannot do
7555    /// with the object in front of it. Leaving the call behind would not link either, since all
7556    /// three are in the math library and a program that wrote one never had a reason to ask for
7557    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
7558    #[test]
7559    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
7560        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
7561        assert!(!text.contains("call"), "{text}");
7562        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
7563        assert!(!text.contains("call"), "{text}");
7564
7565        // The conjugate is the imaginary half negated and the real half as it stands, so there is
7566        // one negation in it. A complex negation is the one with two.
7567        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
7568        assert_eq!(text.matches("fneg").count(), 1, "{text}");
7569        assert!(!text.contains("call"), "{text}");
7570        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
7571        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
7572
7573        // `~` on a complex operand is the same operator, which is the spelling the language has
7574        // had all along and the one a program that never included the header writes.
7575        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
7576        assert_eq!(written, text, "the name and the operator are the same thing");
7577
7578        // The plain names, which are the ones the header declares and so the ones programs write.
7579        let text = body(concat!(
7580            "double creal(_Complex double z);\n",
7581            "double f(_Complex double z) { return creal(z); }\n",
7582        ));
7583        assert!(!text.contains("call"), "{text}");
7584        let text = body(concat!(
7585            "_Complex float conjf(_Complex float z);\n",
7586            "_Complex float f(_Complex float z) { return conjf(z); }\n",
7587        ));
7588        assert_eq!(text.matches("fneg").count(), 1, "{text}");
7589        assert!(!text.contains("call"), "{text}");
7590
7591        // A program that took the name means its own function, the same four ways the absolute
7592        // value family next door asks it.
7593        let taken = concat!(
7594            "static double creal(_Complex double z) { return 7; }\n",
7595            "double f(_Complex double z) { return creal(z); }\n",
7596        );
7597        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
7598        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
7599        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
7600        let plain = concat!(
7601            "double cimag(_Complex double z);\n",
7602            "double f(_Complex double z) { return cimag(z); }\n",
7603        );
7604        let mut opts = options();
7605        opts.emit = EmitKind::Ir;
7606        opts.builtins = false;
7607        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
7608        opts.builtins = true;
7609        opts.no_builtin = vec!["cimag".to_owned()];
7610        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
7611
7612        // A constant folds, which is what a static initializer written with one needs.
7613        let text = ir(concat!(
7614            "double a = __builtin_creal(1.5 + 2.5i);\n",
7615            "double b = __builtin_cimag(1.5 + 2.5i);\n",
7616            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
7617        ));
7618        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
7619        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
7620        assert!(
7621            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
7622            "the conjugate of a constant is the constant with the second half negated: {text}"
7623        );
7624        assert!(!text.contains("call"), "{text}");
7625    }
7626
7627    /// A math library builtin handed a constant is the answer, and is not a call.
7628    ///
7629    /// This is the reason the family is answered in the front end at all. `double x =
7630    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
7631    /// there is no point in the program at which a call could be made, and a compiler that lowered
7632    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
7633    /// gives on x86-64, read out of the object file one initializer at a time.
7634    #[test]
7635    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
7636        let text = ir(concat!(
7637            "double a = __builtin_ceil(1.5);\n",
7638            "double b = __builtin_floor(1.5);\n",
7639            "double c = __builtin_trunc(-1.5);\n",
7640            // A half goes away from zero and not to even, which is where C and the default
7641            // rounding of IEEE 754 part company.
7642            "double d = __builtin_round(2.5);\n",
7643            // The sign survives a number that rounds away to nothing, so this is a negative zero.
7644            "double e = __builtin_ceil(-0.5);\n",
7645            "double f = __builtin_fmax(1.0, 2.0);\n",
7646            "double g = __builtin_fmin(1.0, 2.0);\n",
7647            "float h = __builtin_ceilf(1.25f);\n",
7648            // The plain name is the same answer, which is what a program that included `math.h`
7649            // and never wrote a prefix reaches.
7650            "double ceil(double x);\n",
7651            "double i = ceil(2.25);\n",
7652        ));
7653        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
7654        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
7655        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
7656        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
7657        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
7658        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
7659        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
7660        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
7661        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
7662        assert!(!text.contains("call"), "{text}");
7663    }
7664
7665    /// A math library builtin handed anything else is a call to the library function it is.
7666    ///
7667    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
7668    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
7669    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
7670    /// point of the prefixed spelling: a program writing it reaches the library's function even
7671    /// where a macro or a definition of its own has taken the short name.
7672    #[test]
7673    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
7674        let text = ir(concat!(
7675            "double f(double x) { return __builtin_ceil(x); }\n",
7676            "float g(float x) { return __builtin_floorf(x); }\n",
7677            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
7678        ));
7679        assert!(text.contains("call @ceil("), "{text}");
7680        assert!(text.contains("call @floorf("), "{text}");
7681        assert!(text.contains("call @fmax("), "{text}");
7682
7683        // The two the rounding mode decides are calls even when the argument is a constant, since
7684        // what they answer is not known until the program runs. gcc refuses a static initializer
7685        // written with one for that reason, so there is nothing to fold here either.
7686        let text = ir(concat!(
7687            "double f(void) { return __builtin_rint(2.5); }\n",
7688            "double g(void) { return __builtin_nearbyint(2.5); }\n",
7689        ));
7690        assert!(text.contains("call @rint("), "{text}");
7691        assert!(text.contains("call @nearbyint("), "{text}");
7692
7693        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
7694        // answer is the other operand, and gcc will not fold that one either.
7695        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
7696        assert!(text.contains("call @fmin("), "{text}");
7697
7698        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
7699        // prefixed spelling alone, which is what writing the prefix is for.
7700        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
7701        let mut opts = options();
7702        opts.emit = EmitKind::Ir;
7703        opts.no_builtin = vec!["ceil".to_owned()];
7704        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
7705    }
7706
7707    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
7708    ///
7709    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
7710    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
7711    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
7712    /// number here is what gcc 16 gives on x86-64.
7713    #[test]
7714    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
7715        let text = ir(concat!(
7716            "constexpr int side = 4;\n",
7717            "constexpr int wider = side + 1;\n",
7718            "constexpr double half = 1.5;\n",
7719            "struct point { int x; int y; };\n",
7720            "constexpr struct point origin = { 5, 6 };\n",
7721            "int square[side * side];\n",
7722            "int rectangle[wider];\n",
7723            "int rounded[(int)half * 2];\n",
7724            "int across[origin.y];\n",
7725            "enum named { four = side };\n",
7726            "int e = four;\n",
7727        ));
7728        assert!(text.contains("global @square : bytes 64 ="), "{text}");
7729        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
7730        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
7731        assert!(text.contains("global @across : bytes 24 ="), "{text}");
7732        assert!(text.contains("global @e : i32 = 4,"), "{text}");
7733
7734        // A `const` object is not one of them, which is what makes `int a[n];` a variable
7735        // length array in C and is the distinction the keyword was added to draw.
7736        let mut opts = options();
7737        opts.emit = EmitKind::Ir;
7738        let konst = "const int n = 1;\nint a[n];\n";
7739        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
7740        assert_eq!(run(&opts, konst).messages, [message]);
7741
7742        // Nor is a subscript of one, which gcc 16 refuses in the same words.
7743        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
7744        assert_eq!(run(&opts, subscript).messages, [message]);
7745
7746        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
7747        let address = "constexpr int c = 3;\nint *p = &c;\n";
7748        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
7749             pointer target type [E0514]";
7750        assert_eq!(run(&opts, address).messages, [warning]);
7751    }
7752
7753    /// A member whose size was refused is not a flexible array member, whatever it looks like.
7754    ///
7755    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
7756    /// without the count that tells the two apart the rules about where a flexible array member
7757    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
7758    /// thing about each of these and so does this, which is what the program can act on: adding
7759    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
7760    /// the end of `struct E` does not either.
7761    #[test]
7762    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
7763        let mut opts = options();
7764        opts.emit = EmitKind::Ir;
7765
7766        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
7767        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
7768        assert_eq!(run(&opts, alone).messages, [message]);
7769
7770        // And not one in the wrong place either, which is the other half of the same rule.
7771        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
7772        assert_eq!(run(&opts, first).messages, [message]);
7773
7774        // A size that is refused for a reason of its own, to show the count is about the
7775        // refusal rather than about the one message that happens to have been found first.
7776        let negative = "struct F { int a[-1]; };\n";
7777        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
7778        assert_eq!(run(&opts, negative).messages, [refused]);
7779
7780        // The member that was written with no size at all is still a flexible array member, and
7781        // a structure with nothing else in it still has no named member to hang one off.
7782        let flexible = "struct G { int a[]; };\n";
7783        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
7784             members [E0554]";
7785        assert_eq!(run(&opts, flexible).messages, [named]);
7786    }
7787
7788    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
7789    ///
7790    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
7791    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
7792    /// then reads the element types, finds one `const` and one not, and calls the two arrays
7793    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
7794    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
7795    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
7796    /// two directions are told apart the way they are everywhere else, which is that adding a
7797    /// qualifier is silent and dropping one is worth a word.
7798    ///
7799    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
7800    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
7801    /// not compile for it.
7802    #[test]
7803    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
7804        let mut opts = options();
7805        opts.emit = EmitKind::Ir;
7806        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
7807
7808        // Adding it, which is the direction the library writes and the one nothing is owed for.
7809        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
7810        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
7811
7812        // And the same thing written out rather than through the typedef, since the typedef is a
7813        // spelling and the rule is about the array.
7814        let plain = concat!(
7815            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
7816            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
7817        );
7818        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
7819
7820        // Dropping it, which is the direction that is worth a word, and the word is the one every
7821        // other pointer target gets rather than a complaint about the types not matching.
7822        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
7823        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
7824             [E0514]";
7825        assert_eq!(run(&opts, &dropping).messages, [warning]);
7826
7827        // A pointer to an array of something else is still an incompatible pointer, because
7828        // nothing here is about the element being a different type.
7829        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
7830        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
7831             incompatible return type 'const unsigned int (*)[4]' [E0512]";
7832        assert_eq!(run(&opts, wrong).messages, [error]);
7833    }
7834
7835    /// A definition that names its parameters and then declares them under the list.
7836    ///
7837    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
7838    /// types with the default argument promotions over them, which is what a caller of an
7839    /// unprototyped function hands over. A prototype already in scope overrules the promoted
7840    /// types, since a header saying `int narrow(char);` over a definition written this way is
7841    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
7842    /// every compiler.
7843    #[test]
7844    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
7845        // C17, since the default dialect is the one that warns about the form and this is
7846        // about what it means rather than about the warning.
7847        let mut opts = options();
7848        opts.std = Std::C17;
7849        let source = concat!(
7850            "int add(a, b)\n",
7851            "int a;\n",
7852            "int b;\n",
7853            "{ return a + b; }\n",
7854            "int promoted(c)\n",
7855            "char c;\n",
7856            "{ return c; }\n",
7857            "int narrow(char);\n",
7858            "int narrow(c)\n",
7859            "char c;\n",
7860            "{ return c; }\n",
7861            "int first(a)\n",
7862            "int a[4];\n",
7863            "{ return a[0]; }\n",
7864        );
7865        let result = run(&opts, source);
7866        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
7867        let text = result.text();
7868        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
7869        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
7870        // The body still sees the `char` it was declared as, whatever the caller hands over.
7871        assert!(text.contains("c : char object automatic defined"), "{text}");
7872        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
7873        // An array parameter is a pointer here as much as it is in a prototype.
7874        assert!(text.contains("first : int(int *) function external defined"), "{text}");
7875    }
7876
7877    /// What the two halves of an old-style parameter list can disagree about.
7878    ///
7879    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
7880    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
7881    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
7882    /// left the language in C23, where gcc still takes it and warns.
7883    #[test]
7884    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
7885        let mut opts = options();
7886        opts.std = Std::C17;
7887        for (source, message) in [
7888            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
7889            (
7890                "int f(a)\nint a;\nint b;\n{ return a; }\n",
7891                "3:5: error: declaration for parameter 'b' but no such parameter",
7892            ),
7893            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
7894            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
7895            (
7896                "int f(a)\nstatic int a;\n{ return a; }\n",
7897                "2:12: error: storage class specified for parameter 'a'",
7898            ),
7899            (
7900                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
7901                "2:7: error: argument 'a' doesn't match prototype",
7902            ),
7903        ] {
7904            let result = run(&opts, source);
7905            assert!(result.failed(), "expected this to fail:\n{source}");
7906            assert!(result.messages[0].contains(message), "{:?}", result.messages);
7907        }
7908
7909        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
7910        // in that dialect, and every dialect after it made the same line a diagnostic.
7911        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
7912        let mut older = options();
7913        older.std = Std::C89;
7914        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
7915        let result = run(&opts, implicit);
7916        assert!(
7917            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
7918            "{:?}",
7919            result.messages
7920        );
7921
7922        // C23 took the form out of the language and gcc kept accepting it with a warning, and
7923        // a warning is what this is, because the code written this way is not going to be
7924        // rewritten and refusing it would put the compiler out of reach of it.
7925        let mut newer = options();
7926        newer.std = Std::C23;
7927        let plain = "int f(a)\nint a;\n{ return a; }\n";
7928        let result = run(&newer, plain);
7929        assert!(!result.failed(), "{:?}", result.messages);
7930        assert_eq!(
7931            result.messages,
7932            ["/main.c:1:5: warning: old-style function definition [E0412]"]
7933        );
7934        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
7935    }
7936
7937    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
7938    ///
7939    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
7940    /// same era's spelling for a member. Both are still in code written against a compiler of
7941    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
7942    /// is where the columns below come from as well.
7943    #[test]
7944    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
7945        let array = "int a[8] = { [3] 7 };\n";
7946        let member = "struct s { int x; } v = { x: 7 };\n";
7947        for source in [array, member] {
7948            let result = run(&options(), source);
7949            assert!(!result.failed(), "{:?}", result.messages);
7950            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
7951        }
7952
7953        let mut asked = options();
7954        asked.pedantic = true;
7955        assert_eq!(
7956            run(&asked, array).messages,
7957            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
7958        );
7959        assert_eq!(
7960            run(&asked, member).messages,
7961            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
7962        );
7963    }
7964
7965    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
7966    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
7967    ///
7968    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
7969    /// record of every byte an object may have is laid out and one byte more is refused. All
7970    /// four numbers are what gcc 16 gives on x86-64.
7971    #[test]
7972    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
7973        let text = ir(concat!(
7974            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
7975            "struct brim { char buf[9223372036854775807L]; };\n",
7976            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
7977            "unsigned long h = sizeof(struct huge_struct);\n",
7978            "unsigned long b = sizeof(struct brim);\n",
7979            "unsigned long y = sizeof(struct bitty);\n",
7980        ));
7981        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
7982        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
7983        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
7984
7985        let mut opts = options();
7986        opts.emit = EmitKind::Ir;
7987        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
7988        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
7989        assert_eq!(run(&opts, over).messages, [message]);
7990        let array = "struct wide { short buf[1L << 62]; };\n";
7991        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
7992             maximum object size '9223372036854775807' [E0537]";
7993        assert_eq!(run(&opts, array).messages[0], message);
7994    }
7995
7996    /// A byte in the source that is not part of a character, which only a literal may hold.
7997    ///
7998    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
7999    /// mostly text.
8000    fn compile_bytes(source: &[u8]) -> Compiled {
8001        let mut opts = options();
8002        opts.emit = EmitKind::Ir;
8003        let mut fs = MemoryFileSystem::new();
8004        fs.insert("/main.c", source.to_vec());
8005        compile(&opts, "/main.c", &fs)
8006    }
8007
8008    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
8009    /// the only place in a source file where a byte does not have to be part of a character.
8010    /// Replacing it would give the object three bytes rather than one, since the replacement
8011    /// character is three bytes of UTF-8, so the object would not be the one that was written
8012    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
8013    /// is where gcc draws the same line.
8014    #[test]
8015    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
8016        let mut source = b"char s[] = \"a".to_vec();
8017        source.push(0xff);
8018        source.extend_from_slice(b"b\";\nchar c = '");
8019        source.push(0xff);
8020        source.extend_from_slice(b"';\n");
8021        let result = compile_bytes(&source);
8022        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
8023        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
8024        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
8025        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
8026
8027        let mut stray = b"int a".to_vec();
8028        stray.push(0xff);
8029        stray.extend_from_slice(b" = 1;\n");
8030        let result = compile_bytes(&stray);
8031        assert!(
8032            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
8033            "{:?}",
8034            result.messages
8035        );
8036    }
8037
8038    #[test]
8039    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
8040        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
8041        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
8042        let expected = "\
8043func @add(i32, i32) -> i32, linkage(external) {
8044block0(%0: i32, %1: i32):
8045    %2 = add.nsw %0, %1
8046    return %2
8047}
8048";
8049        assert!(text.contains(expected), "{text}");
8050    }
8051
8052    #[test]
8053    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
8054        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
8055        assert!(!text.contains("alloca"), "{text}");
8056        assert!(!text.contains("load"), "{text}");
8057        assert!(!text.contains("store"), "{text}");
8058    }
8059
8060    #[test]
8061    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
8062        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
8063        let expected = "\
8064block0:
8065    %0 = alloca, size 4, align 4
8066    %1 = iconst.i32 1
8067    store %1 -> %0, align 4, tbaa !1
8068    %2 = call @g(%0) : (ptr) -> i32
8069    return %2
8070";
8071        assert_eq!(text, expected);
8072    }
8073
8074    #[test]
8075    fn a_loop_carries_what_it_changes_as_block_parameters() {
8076        // The whole point of building SSA during the walk rather than after it: `i` and
8077        // `total` are values that arrive on an edge, and neither has ever been in memory.
8078        let text = body(
8079            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
8080             return total;\n}\n",
8081        );
8082        assert!(!text.contains("alloca"), "{text}");
8083        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
8084        assert!(text.contains("jump block1("), "{text}");
8085    }
8086
8087    #[test]
8088    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
8089        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
8090        assert!(text.contains("icmp slt %0, %1"), "{text}");
8091        assert!(!text.contains("zext"), "{text}");
8092    }
8093
8094    #[test]
8095    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
8096        let text = body("int f(int a, int b) { return a && b; }\n");
8097        let expected = "\
8098block0(%0: i32, %1: i32):
8099    %2 = iconst.i32 0
8100    %3 = icmp ne %0, %2
8101    %4 = iconst.i1 0
8102    br_if %3, block1, block2(%4)
8103
8104block1:
8105    %5 = iconst.i32 0
8106    %6 = icmp ne %1, %5
8107    jump block2(%6)
8108
8109block2(%7: i1):
8110    %8 = zext.i32 %7
8111    return %8
8112";
8113        assert_eq!(text, expected);
8114    }
8115
8116    #[test]
8117    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
8118        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
8119        // Three blocks, the test and the two arms. The join the `return 3` would need is
8120        // never created, because a block nothing branches to is not a block.
8121        assert!(!text.contains("block3"), "{text}");
8122        assert!(!text.contains("iconst.i32 3"), "{text}");
8123    }
8124
8125    #[test]
8126    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
8127        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
8128        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
8129        assert!(body("int f(void) { }\n").contains("unreachable"));
8130    }
8131
8132    #[test]
8133    fn a_structure_is_copied_rather_than_held_in_a_value() {
8134        let text = body(
8135            "struct point { int x, y; };\n\
8136             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
8137        );
8138        assert!(text.contains("memcpy"), "{text}");
8139    }
8140
8141    #[test]
8142    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
8143        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
8144        assert!(text.contains("memset"), "{text}");
8145    }
8146
8147    #[test]
8148    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
8149        let text = body(
8150            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
8151             default: r = 4; } return r; }\n",
8152        );
8153        let expected = "\
8154block0(%0: i32):
8155    %1 = iconst.i32 0
8156    switch %0, block1, [1 => block2, 2 => block3(%1)]
8157
8158block1:
8159    %2 = iconst.i32 4
8160    jump block4(%2)
8161
8162block2:
8163    %3 = iconst.i32 1
8164    jump block3(%3)
8165
8166block3(%4: i32):
8167    %5 = iconst.i32 2
8168    %6 = add.nsw %4, %5
8169    jump block4(%6)
8170
8171block4(%7: i32):
8172    return %7
8173";
8174        assert_eq!(text, expected);
8175    }
8176
8177    #[test]
8178    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
8179        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
8180        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
8181        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
8182        assert!(text.contains("%2 = sub %0, %1"), "{text}");
8183        assert!(text.contains("icmp ule"), "{text}");
8184        assert!(!text.contains("switch"), "{text}");
8185    }
8186
8187    #[test]
8188    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
8189        let text = body(
8190            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
8191             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
8192        );
8193        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
8194        // which is also where the default falls out to.
8195        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
8196        assert!(text.contains("block5:\n    jump block7("), "{text}");
8197        assert!(text.contains("block6:\n    jump block8("), "{text}");
8198    }
8199
8200    #[test]
8201    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
8202        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
8203    }
8204
8205    #[test]
8206    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
8207        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
8208        // The `while` is not reached in order, so the walk starts a block nothing branches to and
8209        // builds it from there. What comes out is the loop with an edge straight into its body,
8210        // and the header that nothing arrives at is pruned.
8211        let text = body(
8212            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
8213             return n; }\n",
8214        );
8215        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
8216        // at the bottom of the loop comes back round to the body.
8217        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
8218        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
8219        assert!(text.contains("block4:\n    jump block3("), "{text}");
8220    }
8221
8222    #[test]
8223    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
8224        // The same thing through a `goto`. The first pass through the body runs whatever the
8225        // label is on, and only then does the loop reach its own test.
8226        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
8227        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
8228        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
8229        assert!(text.contains("br_if %6, block2, block3"), "{text}");
8230    }
8231
8232    #[test]
8233    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
8234        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
8235        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
8236        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
8237        // up the block list to second place.
8238        assert!(!text.contains("alloca"), "{text}");
8239        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
8240        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
8241    }
8242
8243    #[test]
8244    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
8245        let text =
8246            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
8247        assert!(!text.contains("alloca"), "{text}");
8248        assert!(text.contains("block1(%2: i32):"), "{text}");
8249        assert!(text.contains("jump block1(%5)"), "{text}");
8250    }
8251
8252    #[test]
8253    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
8254        // A block nothing branches to is not a legal function, and which labels are dead is not
8255        // known until the last statement has been walked, since the `goto` is allowed to be it.
8256        assert_eq!(
8257            body("int f(int x) { return x; spare: return 0; }\n"),
8258            "block0(%0: i32):\n    return %0\n"
8259        );
8260    }
8261
8262    #[test]
8263    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
8264        let text = body(
8265            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
8266        );
8267        // One byte holds both fields, and the signed one needs no mask: shifting it down
8268        // arithmetically is what says its top bit is a sign.
8269        assert_eq!(
8270            text,
8271            "\
8272block0(%0: ptr):
8273    %1 = load.i8 %0, align 1
8274    %2 = iconst.i8 3
8275    %3 = ashr %1, %2
8276    %4 = sext.i32 %3
8277    return %4
8278"
8279        );
8280    }
8281
8282    #[test]
8283    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
8284        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
8285        // the four byte store this would take is a data race in a program that has none. The
8286        // three bytes of `a` go in as two and one, and `c` is not touched.
8287        let text =
8288            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
8289        assert_eq!(
8290            text,
8291            "\
8292block0(%0: ptr, %1: i32):
8293    %2 = iconst.i32 16777215
8294    %3 = and %1, %2
8295    %4 = trunc.i16 %3
8296    store %4 -> %0, align 2
8297    %5 = iconst.i32 16
8298    %6 = lshr %3, %5
8299    %7 = trunc.i8 %6
8300    %8 = iconst.i64 2
8301    %9 = ptr_add %0, %8
8302    store %7 -> %9, align 1
8303    return
8304"
8305        );
8306    }
8307
8308    #[test]
8309    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
8310        let text =
8311            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
8312        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
8313        // assignment is worth.
8314        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
8315        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
8316    }
8317
8318    #[test]
8319    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
8320        // The value of an assignment to a bit-field takes a shift to build, and a statement
8321        // has no use for it. Nothing here reads back what was stored.
8322        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
8323        assert_eq!(text.matches("ashr").count(), 0, "{text}");
8324        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
8325    }
8326
8327    #[test]
8328    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
8329        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
8330        // to be zero before it goes in or what the initializer did not name is whatever the
8331        // stack held.
8332        let text = body(
8333            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
8334        );
8335        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
8336    }
8337
8338    #[test]
8339    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
8340        // Two fields in one byte are not two entries in the image, because an image is written
8341        // in bytes: they are the byte they are both in.
8342        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
8343        assert!(
8344            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
8345            "{text}"
8346        );
8347    }
8348
8349    #[test]
8350    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
8351        // `sizeof` answers without the array and the definition has to hold what was written, so
8352        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
8353        // so does this. The image used to be written at the size the type had, which left the
8354        // verifier looking at twenty bytes going into four.
8355        let text = ir(concat!(
8356            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
8357            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
8358            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
8359            "char s[2] = \"hi\";\n",
8360        ));
8361        assert!(
8362            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
8363            "{text}"
8364        );
8365        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
8366        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
8367        // The array with a length of its own still cuts the literal down to it, which is the
8368        // one case in C where a string initializer drops its terminator.
8369        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
8370    }
8371
8372    #[test]
8373    fn a_definition_takes_a_parameter_it_left_unnamed() {
8374        // The entry block's parameters are the definition's, and one the front end dropped for
8375        // having no name left the two lists different lengths, which the walk read as an
8376        // old-style definition and refused. gcc has taken these for far longer than C23 has.
8377        let text = ir("int f(int a, int) { return a; }\n");
8378        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
8379        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
8380
8381        // The unnamed one first, so that the named one is the second parameter of the entry
8382        // block and not the first: the list says the order and not only how many there are.
8383        let text = ir("int g(int, int n) { return n; }\n");
8384        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
8385    }
8386
8387    #[test]
8388    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
8389        // `d = e = c` used to be refused, because the middle assignment is a value of structure
8390        // type and the walk had nowhere to read one from. What an assignment is worth is the
8391        // value it stored, so the object it stored into is the answer and the chain is three
8392        // copies out of the one source with no temporary in it.
8393        let text = body(concat!(
8394            "struct s { int f; int g; };\n",
8395            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
8396            "{ *d = *e = a[0] = *c; }\n",
8397        ));
8398        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
8399        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
8400        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
8401        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
8402    }
8403
8404    #[test]
8405    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
8406        // The excess used to be laid into the object anyway, so the row after was written over
8407        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
8408        // in only if there is room for it, and gcc discards the rest of a literal that is longer
8409        // still, which is what the first of these is and why it warns.
8410        let mut opts = options();
8411        opts.emit = EmitKind::Ir;
8412        let result = run(
8413            &opts,
8414            concat!(
8415                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
8416                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
8417                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
8418                "const union u c = { { \"1234\", \"567\" } };\n",
8419            ),
8420        );
8421        let text = result.text();
8422        assert_eq!(
8423            result.messages,
8424            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
8425              (5 chars into 3 available) [E0637]"]
8426        );
8427        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
8428        assert!(
8429            text.contains(
8430                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
8431                 bytes \"9\\00\", zero 3 }"
8432            ),
8433            "{text}"
8434        );
8435        // The eight bytes are four, three and a terminator, and then the byte the shorter
8436        // literal left for the string in the other member of the union to end at.
8437        assert!(
8438            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
8439            "{text}"
8440        );
8441    }
8442
8443    #[test]
8444    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
8445        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
8446        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
8447        // refused with E0519. It is one copy out of the object named, not two.
8448        let text = body(concat!(
8449            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
8450            "void g(struct v *);\n",
8451            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
8452        ));
8453        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
8454    }
8455
8456    #[test]
8457    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
8458        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
8459        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
8460        // it a non constant because reading it is a node of its own and the read was what it
8461        // looked at, and lowering had no way to put an object where it wanted a number.
8462        let text = ir(concat!(
8463            "struct s { int x; };\n",
8464            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
8465            "int n = (int){ 7 };\n",
8466            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
8467        ));
8468        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
8469        assert!(text.contains("global @n : i32 = 7,"), "{text}");
8470        // The second literal names nothing, so what it puts in is the zeros of its own size and
8471        // not the tail of the object it went in, which would have been the same bytes by luck.
8472        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
8473    }
8474
8475    #[test]
8476    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
8477        // Nothing declares a compound literal, so the reference is the only thing that can ask
8478        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
8479        // symbol, which the link would have been the first to find out.
8480        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
8481        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
8482        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
8483    }
8484
8485    #[test]
8486    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
8487        // A zero length array, which gcc allows and real code uses as the tail of a structure.
8488        // The image is there and holds nothing, which is not the global that has no image at
8489        // all, and the IR reader used to stop on the empty one.
8490        let text = ir("unsigned char foo[1][0];\n");
8491        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
8492    }
8493
8494    #[test]
8495    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
8496        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
8497        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
8498        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
8499        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
8500        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
8501    }
8502
8503    #[test]
8504    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
8505        // Which the verifier used to refuse, having read a declaration as a definition with
8506        // nothing in it. `extern const` is how a program names something in the library's read
8507        // only data, and glibc and Darwin both have one in a header a real program includes.
8508        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
8509        assert!(
8510            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
8511            "{text}"
8512        );
8513    }
8514
8515    #[test]
8516    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
8517        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
8518        // addresses can, and the answer is the address of whichever arm was taken rather than
8519        // a copy of it into a third place: both arms outlive the expression, so a copy would
8520        // be one nothing could observe. SQLite's parser writes one of these.
8521        let text = body(
8522            "\
8523struct s { int a, b; };
8524struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
8525",
8526        );
8527        // The join takes an address, each arm hands it the one it has, and nothing is copied.
8528        assert!(text.contains("block3(%7: ptr)"), "{text}");
8529        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
8530        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
8531    }
8532
8533    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
8534    ///
8535    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
8536    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
8537    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
8538    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
8539    /// increments once.
8540    #[test]
8541    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
8542        let text = body("int f(int i) { return ++i ?: 10; }\n");
8543        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
8544        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
8545
8546        // The arm still converts, since what the whole expression is worth is a `long` here and
8547        // the node under it is an `int`. What it converts is the value in hand.
8548        let text = body("long f(int i) { return ++i ?: 10L; }\n");
8549        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
8550        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
8551
8552        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
8553        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
8554        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
8555
8556        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
8557        // operand being absent is the whole of the difference.
8558        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
8559        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
8560    }
8561
8562    #[test]
8563    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
8564        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
8565        // one `i64` in each direction and the body takes the object apart and puts it back
8566        // together around the call.
8567        let text = ir("\
8568struct pair { int a, b; };
8569struct pair make(int a, int b);
8570struct pair twice(struct pair p) { return make(p.a, p.b); }
8571");
8572        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
8573        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
8574    }
8575
8576    #[test]
8577    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
8578        // Over two eightbytes the caller passes the bytes in the argument area, which is
8579        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
8580        // a parameter the program wrote and both are parameters the function has.
8581        let text = ir("\
8582struct big { double v[8]; };
8583struct big grow(struct big b);
8584struct big twice(struct big b) { return grow(grow(b)); }
8585");
8586        assert!(
8587            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
8588            "{text}"
8589        );
8590        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
8591        // The inner call writes into a slot and the outer one reads the same slot, so the
8592        // object between the two calls is never copied anywhere.
8593        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
8594    }
8595
8596    #[test]
8597    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
8598        // The bytes travel in the argument area the same way they would for a parameter, and
8599        // `printf` has no parameter there to say it on, so the call says it instead. The one
8600        // that fits in registers says nothing, because travelling as the registers it fits in
8601        // is what an argument does when nothing says otherwise.
8602        let text = ir("\
8603struct big { double v[8]; };
8604struct pair { int a, b; };
8605int p(const char *, ...);
8606int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
8607");
8608        assert!(
8609            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
8610            "{text}"
8611        );
8612    }
8613
8614    #[test]
8615    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
8616        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
8617        // is a slot the returned registers are written to.
8618        let body = body(
8619            "\
8620struct pair { int a, b; };
8621struct pair make(int a, int b);
8622int second(void) { return make(1, 2).b; }
8623",
8624        );
8625        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
8626        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
8627    }
8628
8629    #[test]
8630    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
8631        // The same declaration, classified by a different ABI: three `float` members are an
8632        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
8633        // registers on AAPCS64.
8634        let source = "\
8635struct hfa { float x, y, z; };
8636int take(struct hfa h);
8637int give(struct hfa h) { return take(h); }
8638";
8639        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
8640        let mut opts = options();
8641        opts.emit = EmitKind::Ir;
8642        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
8643        let result = run(&opts, source);
8644        assert_eq!(result.messages, Vec::<String>::new());
8645        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
8646    }
8647
8648    #[test]
8649    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
8650        // The size is a multiplication rather than a number, the slot is taken from the stack
8651        // where the declaration is, and the scope it was declared in gives it back.
8652        let source = "\
8653int use(int *);
8654void f(int n) {
8655  {
8656    int a[n];
8657    use(a);
8658  }
8659  use(0);
8660}
8661";
8662        let body = body(source);
8663        assert!(body.contains("mul.nsw"), "{body}");
8664        assert!(body.contains("stacksave"), "{body}");
8665        assert!(body.contains("alloca %"), "{body}");
8666        assert!(body.contains("stackrestore"), "{body}");
8667    }
8668
8669    #[test]
8670    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
8671        // The label is outside the block the array is in, so arriving there means the array is
8672        // gone, and the restore that says so goes in front of the branch. The `goto` is written
8673        // before the walk knows where the label is, which is why the restore is put there at
8674        // the end rather than built where the branch was.
8675        let source = "\
8676int use(int *);
8677int f(int n) {
8678  {
8679    int a[n];
8680    if (use(a)) goto out;
8681    use(0);
8682  }
8683out:
8684  return 0;
8685}
8686";
8687        let body = body(source);
8688        // Two ways out of the block and a restore on each: the jump and the end of the block.
8689        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
8690        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
8691        assert!(after.starts_with(" %4\n    jump block"), "{body}");
8692    }
8693
8694    #[test]
8695    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
8696        // The label is after the declaration and in the same block, so control that arrives
8697        // there arrives somewhere the array exists. Giving it back would be giving back an
8698        // object the next statement reads.
8699        let source = "\
8700int use(int *);
8701int f(int n) {
8702  int a[n];
8703again:
8704  if (use(a)) goto again;
8705  return 0;
8706}
8707";
8708        let body = body(source);
8709        assert!(body.contains("stacksave"), "{body}");
8710        assert!(!body.contains("stackrestore"), "{body}");
8711    }
8712
8713    #[test]
8714    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
8715        // A loop written out of a `goto`, with the array made inside it. The label is in the
8716        // same block as the declaration and before it, which is a place where the array does
8717        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
8718        // compiler that skips this restore grows the stack once per iteration.
8719        let source = "\
8720int use(int *);
8721int f(int n) {
8722again:
8723  {
8724    int a[n];
8725    if (use(a)) goto again;
8726  }
8727  return 0;
8728}
8729";
8730        let body = body(source);
8731        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
8732        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
8733        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
8734    }
8735
8736    #[test]
8737    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
8738        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
8739        // not one mark nobody reads. The marks are a stack, so the next close took this one
8740        // instead of its own, and the body of the loop gave back nothing while the block after
8741        // the loop restored a pointer saved inside it. The verifier refused that, which is how
8742        // it was found.
8743        let source = "\
8744int f(void);
8745void t(void) {
8746  int count = 10;
8747  for (; count--;) {
8748    int b[f()];
8749    int i;
8750    for (i = 0; i < f(); i++) {
8751      b[i] = count;
8752    }
8753  }
8754}
8755";
8756        let body = body(source);
8757        // One save, in the body, and one restore for it, also in the body: the block the
8758        // restore is in is the one the inner loop leaves through, and it goes back round the
8759        // outer loop rather than out of it.
8760        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
8761        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
8762        // The rest of the block the restore is in, which is the last block here, so there is not
8763        // always another one after it to split on.
8764        let next = after.split("\n\n").next().expect("the block the restore is in");
8765        assert!(next.contains("jump block1("), "{body}");
8766    }
8767
8768    #[test]
8769    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
8770        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
8771        // still as long as the array is, which is what `n` was when the array came into being.
8772        let source = "\
8773unsigned long f(int n) {
8774  int a[n];
8775  n = 0;
8776  return sizeof a;
8777}
8778";
8779        let body = body(source);
8780        // One read of the parameter, at the declaration, and the answer is built out of it.
8781        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
8782    }
8783
8784    #[test]
8785    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
8786        // GNU's statement expression: the statements happen where they are written and the last
8787        // one is the value, so the temporary in it never becomes a slot and never is copied.
8788        let source = "\
8789int use(int);
8790int f(int x) {
8791  return ({
8792    int t = use(x);
8793    t * t;
8794  });
8795}
8796";
8797        let expected = "\
8798block0(%0: i32):
8799    %1 = call @use(%0) : (i32) -> i32
8800    %2 = mul.nsw %1, %1
8801    return %2
8802";
8803        assert_eq!(body(source), expected);
8804    }
8805
8806    #[test]
8807    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
8808        // What janet writes, which is a call that does not return and then a value after it so
8809        // that the arm is worth something. The left side happens for what it did and the answer
8810        // is where the right side is, so there is nothing to copy and no temporary for a copy.
8811        let source = "\
8812struct pair { int a, b; };
8813void bail(void);
8814int f(struct pair p) {
8815  return (bail(), p).b;
8816}
8817";
8818        let expected = "\
8819block0(%0: i64):
8820    %1 = alloca, size 8, align 4
8821    store %0 -> %1, align 4
8822    call @bail() : ()
8823    %2 = iconst.i64 4
8824    %3 = ptr_add %1, %2
8825    %4 = load.i32 %3, align 4, tbaa !1
8826    return %4
8827";
8828        assert_eq!(body(source), expected);
8829    }
8830
8831    #[test]
8832    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
8833        // A macro that always jumps, which is what this shape is in real code. The value is
8834        // never taken, and the block the rest of the expression would have been built in is
8835        // one nothing branches to, so it goes with the other unreachable blocks.
8836        let source = "int f(int x) { return ({ return x; 0; }); }\n";
8837        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
8838    }
8839
8840    #[test]
8841    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
8842        // What it becomes is the target's answer, and this is not where the target's answers
8843        // are, so the walk writes down which list and which type and leaves it at that. Two of
8844        // them are two instructions, since each moves the list on.
8845        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
8846        let expected = "\
8847block0(%0: ptr):
8848    %1 = va_arg.f64 %0
8849    %2 = va_arg.f64 %0
8850    %3 = fadd %1, %2
8851    return %3
8852";
8853        assert_eq!(body(source), expected);
8854    }
8855
8856    #[test]
8857    fn one_that_reads_a_structure_answers_where_the_object_is() {
8858        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
8859        // the object form is a second instruction. What it answers is an address, so it is a
8860        // place already and the walk copies nothing out of it: the copy here is the one the
8861        // initializer asks for, into the variable being declared. The size and the alignment
8862        // travel with it because they are what steps the list on and what a target that has to
8863        // put registers somewhere needs to know. So does the classification, which says the two
8864        // halves of this one arrived in general purpose registers: that is an answer about a C
8865        // type, and this is the last place that still has one.
8866        //
8867        // The slot is aligned to sixteen and the copy into it to eight, which is not a
8868        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
8869        // members ask for, and eight is what the type asks for and so what the copy may assume
8870        // about the object it is reading from.
8871        let source = "\
8872struct s { int a; long b; };
8873long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
8874";
8875        let expected = "\
8876block0(%0: ptr):
8877    %1 = alloca, size 16, align 16
8878    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
8879    memcpy %1, %2, size 16, align 8
8880    %3 = iconst.i64 8
8881    %4 = ptr_add %1, %3
8882    %5 = load.i64 %4, align 8, tbaa !1
8883    return %5
8884";
8885        assert_eq!(body(source), expected);
8886    }
8887
8888    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
8889    /// and an object with no slots at all is one it sent to the caller's argument area, which is
8890    /// what everything over two eightbytes is whatever its members are.
8891    #[test]
8892    fn the_classification_says_which_registers_the_object_arrived_in() {
8893        let source = "\
8894struct s { double a; double b; };
8895double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
8896";
8897        assert!(
8898            body(source)
8899                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
8900            "{}",
8901            body(source)
8902        );
8903
8904        let big = "\
8905struct s { long a[4]; };
8906long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
8907";
8908        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
8909    }
8910
8911    #[test]
8912    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
8913        // GNU's computed goto. Which label the address holds is not known here, so all of them
8914        // are listed, and the values arriving at one are passed on every edge the same way they
8915        // are on an ordinary branch.
8916        let source = "\
8917int f(int c) {
8918  void *p = c ? &&one : &&two;
8919  goto *p;
8920one:
8921  return 1;
8922two:
8923  return 2;
8924}
8925";
8926        let expected = "\
8927block0(%0: i32):
8928    %1 = iconst.i32 0
8929    %2 = icmp ne %0, %1
8930    br_if %2, block1, block2
8931
8932block1:
8933    %3 = block_addr block3
8934    jump block4(%3)
8935
8936block2:
8937    %4 = block_addr block5
8938    jump block4(%4)
8939
8940block3:
8941    %5 = iconst.i32 1
8942    return %5
8943
8944block4(%6: ptr):
8945    indirect_br %6, block3, block5
8946
8947block5:
8948    %7 = iconst.i32 2
8949    return %7
8950";
8951        assert_eq!(body(source), expected);
8952    }
8953
8954    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
8955    /// loop keeps in hand, and a jump through the table at the end of every one of them.
8956    fn dispatch(labels: usize) -> String {
8957        let mask = labels - 1;
8958        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
8959        for index in 0..labels {
8960            source.push_str(&format!(" &&a{index},"));
8961        }
8962        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
8963        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
8964        for index in 0..labels {
8965            let step = match index % 4 {
8966                0 => "w += x;",
8967                1 => "x += y;",
8968                2 => "y += z;",
8969                _ => "z += w;",
8970            };
8971            source.push_str(&format!("a{index}:\n\t{step}\n"));
8972            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
8973            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
8974        }
8975        source.push_str("}\n");
8976        source
8977    }
8978
8979    /// How many moves are written in front of the first jump through a register.
8980    fn in_front_of_the_jump(text: &str) -> usize {
8981        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
8982        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
8983    }
8984
8985    /// What a branch writes in front of its jump is what it carries, not what every label it can
8986    /// reach would like to be handed.
8987    ///
8988    /// A label an indirect branch reaches is given its values in registers the branch writes
8989    /// before it goes, because the moves cannot go after a jump and cannot go across the register
8990    /// the jump reads. Writing a register for each parameter of each label costs the table's
8991    /// length on every dispatch, which is a few moves in a program with two labels and five
8992    /// hundred in an interpreter with seventy. The values are the same values, so the registers
8993    /// are the same registers, and the cost stays where the number of values puts it.
8994    #[test]
8995    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
8996        let small = in_front_of_the_jump(&asm(&dispatch(4)));
8997        let large = in_front_of_the_jump(&asm(&dispatch(32)));
8998        assert_eq!(small, large, "eight times the labels and the same values in hand");
8999        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
9000    }
9001
9002    /// The same interpreter with more values in hand than there are registers, which is what makes
9003    /// the allocator send some of them to the stack at every label.
9004    fn crowded(labels: usize) -> String {
9005        const VALUES: usize = 24;
9006        let mask = labels - 1;
9007        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
9008        for index in 0..labels {
9009            source.push_str(&format!(" &&a{index},"));
9010        }
9011        source.push_str(" };\n\t");
9012        for value in 0..VALUES {
9013            source.push_str(&format!("int v{value} = n + {value}; "));
9014        }
9015        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
9016        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
9017        for index in 0..labels {
9018            let (to, from) = (index % VALUES, (index + 1) % VALUES);
9019            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
9020            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
9021            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
9022        }
9023        source.push_str("}\n");
9024        source
9025    }
9026
9027    /// How many bytes of frame the first function in a listing opens.
9028    fn the_frame(text: &str) -> u64 {
9029        text.lines()
9030            .find_map(|line| {
9031                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
9032                size.parse().ok()
9033            })
9034            .expect("a function that opens a frame")
9035    }
9036
9037    /// A frame holds what a function wants at once, and an interpreter does not want the whole
9038    /// table at once.
9039    ///
9040    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
9041    /// allocator has no register for goes on the stack. They are the same few values one label at
9042    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
9043    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
9044    /// which is tamnd/rucc#1630.
9045    #[test]
9046    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
9047        let small = the_frame(&asm(&crowded(16)));
9048        let large = the_frame(&asm(&crowded(64)));
9049        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
9050    }
9051
9052    /// A template that saves the callee-saved registers by name, which is micropython's non local
9053    /// return and is tamnd/rucc#1583.
9054    ///
9055    /// Every register in it is one the template named rather than one the statement handed over,
9056    /// because the buffer is defined as holding those registers and there is no constraint letter
9057    /// that means `%rsp`. The instructions come out naming what the program named, and the
9058    /// allocator, which was told about the writes rather than left to find out, saves the ones the
9059    /// calling convention says belong to whoever called.
9060    #[test]
9061    fn a_template_that_names_its_own_registers_gets_the_ones_it_named() {
9062        let source = "void save(void *nlr) {
9063    __asm volatile (
9064        \"movq   %%rsp, 32(%%rdi)   \\n\"
9065        \"movq   %%rbx, 40(%%rdi)   \\n\"
9066        \"movq   %%r12, 48(%%rdi)   \\n\"
9067        : : \"D\" (nlr) : \"memory\");
9068}
9069";
9070        let text = asm(source);
9071        assert!(text.contains("\tmovq\t%rsp, 32(%rdi)\n"), "{text}");
9072        assert!(text.contains("\tmovq\t%rbx, 40(%rdi)\n"), "{text}");
9073        assert!(text.contains("\tmovq\t%r12, 48(%rdi)\n"), "{text}");
9074    }
9075
9076    #[test]
9077    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
9078        // The address came from outside the function, and a jump to a label in another function
9079        // is undefined. The expression is still evaluated, since a call in it has to happen.
9080        let source = "void **next(void);
9081void f(void) { goto *next(); }
9082";
9083        let expected = "\
9084block0:
9085    %0 = call @next() : () -> ptr
9086    unreachable
9087";
9088        assert_eq!(body(source), expected);
9089    }
9090
9091    #[test]
9092    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
9093        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
9094        // a basic asm implies.
9095        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
9096        let expected = "\
9097block0:
9098    inline_asm.volatile \"mfence\", \"\", \"memory\"()
9099    return
9100";
9101        assert_eq!(body(source), expected);
9102    }
9103
9104    #[test]
9105    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
9106        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
9107        // output in a register is a result, and one that is read as well is an argument too.
9108        let source = "\
9109int f(int x, int y) {
9110  int r;
9111  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
9112  return r + y;
9113}
9114";
9115        let expected = "\
9116block0(%0: i32, %1: i32):
9117    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
9118    %4 = add.nsw %2, %3
9119    return %4
9120";
9121        assert_eq!(body(source), expected);
9122    }
9123
9124    #[test]
9125    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
9126        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
9127        // that runs before the walk has to have known that or there would be nothing to point
9128        // at. A structure travels this way whatever else its constraint allows, since there is
9129        // no register that holds one.
9130        let source = "\
9131struct pair { int a, b; };
9132int f(int x) {
9133  int slot = x;
9134  struct pair p = { x, x };
9135  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
9136  return slot + p.a;
9137}
9138";
9139        let text = body(source);
9140        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
9141        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
9142        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
9143    }
9144
9145    #[test]
9146    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
9147        // The output is only in scope where the instruction dominates, which is the fall through
9148        // block, so the edge to the label carries the value the object had before the assembly
9149        // ran. That is what document 11 asks for and it is what putting the fall through first
9150        // buys.
9151        let source = "\
9152int f(int x) {
9153  int r = 7;
9154  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
9155  return r;
9156away:
9157  return r;
9158}
9159";
9160        let expected = "\
9161block0(%0: i32):
9162    %1 = iconst.i32 7
9163    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
9164
9165block1:
9166    return %2
9167
9168block2:
9169    return %1
9170";
9171        assert_eq!(body(source), expected);
9172    }
9173
9174    #[test]
9175    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
9176        // The operands are checked here rather than by the assembler, because by the time the
9177        // assembler sees the template the operands have become registers and it has nothing left
9178        // to say about the C that named them.
9179        let mut opts = options();
9180        opts.emit = EmitKind::Ir;
9181        for (source, expected) in [
9182            (
9183                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
9184                "output operand constraint lacks '='",
9185            ),
9186            (
9187                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
9188                "lvalue required in 'asm' statement",
9189            ),
9190            (
9191                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
9192                "read-only variable 'g' used as 'asm' output",
9193            ),
9194            (
9195                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
9196                "input operand constraint contains '='",
9197            ),
9198            (
9199                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
9200                "memory input 0 is not directly addressable",
9201            ),
9202            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
9203            (
9204                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
9205                "duplicate asm operand name 'a'",
9206            ),
9207            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
9208        ] {
9209            let result = run(&opts, source);
9210            assert!(result.failed(), "expected this to be reported:\n{source}");
9211            assert!(
9212                result.messages.iter().any(|m| m.contains(expected)),
9213                "{expected}\n{:?}",
9214                result.messages
9215            );
9216        }
9217    }
9218
9219    /// An `asm` at file scope whose template is directives is the whole of what the incbin
9220    /// header, an alias table and a hand written jump table each write, and what it says is a
9221    /// section holding named bytes. So it becomes the globals it names, in the order it names
9222    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
9223    #[test]
9224    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
9225        let text = ir(concat!(
9226            "__asm__(\n",
9227            "  \".section .rodata\\n\"\n",
9228            "  \".globl first\\n\"\n",
9229            "  \".balign 8\\n\"\n",
9230            "  \"first:\\n\"\n",
9231            "  \".long 1\\n\"\n",
9232            "  \".long 2\\n\"\n",
9233            "  \".globl last\\n\"\n",
9234            "  \"last:\\n\"\n",
9235            "  \".quad last - first\\n\");\n",
9236            "extern const int first[];\n",
9237            "extern const long last;\n",
9238        ));
9239        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
9240        assert!(text.contains("global @last : i64 = 8"), "{text}");
9241    }
9242
9243    /// The distance between two labels is what the incbin header hands a program as the size of
9244    /// the data, so a declaration of one of the names has to find the definition the template
9245    /// made rather than turn it back into something the linker is asked for.
9246    #[test]
9247    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
9248        let text = ir(concat!(
9249            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
9250            "extern int counter;\n",
9251            "int read(void) { return counter; }\n",
9252        ));
9253        assert!(text.contains("global @counter : i32 = 7"), "{text}");
9254    }
9255
9256    /// Bytes written before any label are a global with a name minted for them, in front of the
9257    /// label written under them, which is what makes the first byte of the name the one written
9258    /// under it. The block is the one tcc's test file writes, without the line of it that measures
9259    /// from one section to another.
9260    #[test]
9261    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
9262        let text = ir(concat!(
9263            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
9264            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
9265            "extern unsigned char stuff[];\n",
9266            "int read(void) { return stuff[0]; }\n",
9267        ));
9268        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
9269        let named = text.find("global @stuff : i8 = 42").expect(&text);
9270        assert!(under < named, "the bytes under no label come first: {text}");
9271        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
9272        // The byte after the pop is a run of its own, because coming back to a section finishes
9273        // what was being written to it the way a label does. It is the next global of that
9274        // section all the same, so the byte lands where the template put it, which is the one
9275        // after the byte under `stuff`.
9276        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
9277        assert!(named < after, "{text}");
9278    }
9279
9280    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
9281    /// last and what the alternative instruction tables in a kernel header are made of. It is the
9282    /// linker's answer rather than the compiler's, because the two sections are placed by the
9283    /// linker, so the image holds a hole and a name for it.
9284    #[test]
9285    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
9286        let text = ir(concat!(
9287            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
9288            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
9289            "extern unsigned char stuff[];\n",
9290            "int read(void) { return stuff[0]; }\n",
9291        ));
9292        // The label the template measured to is a local one and no symbol, so what the hole names
9293        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
9294        // since it is the first byte of it.
9295        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
9296    }
9297
9298    /// A `.set` says one name stands for another, which is a second symbol at the first one's
9299    /// address and is an alias and nothing else. What the directives around it said about the
9300    /// name is what the name gets, and a name the file defines itself keeps its own definition,
9301    /// which is what gcc's symbol table shows for the block tcc's test file writes.
9302    #[test]
9303    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
9304        let text = ir(concat!(
9305            "void base(void) {}\n",
9306            "__asm__(\".weak one\\n.set one, base\");\n",
9307            "__asm__(\".globl two\\n.set two, base\");\n",
9308            "__asm__(\".set three, base\");\n",
9309            "void three(void) {}\n",
9310        ));
9311        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
9312        assert!(text.contains("alias @two = @base"), "{text}");
9313        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
9314        assert!(text.contains("func @three"), "{text}");
9315    }
9316
9317    /// The target has to be something this file defines, because an alias is a symbol at an
9318    /// address in this object and a name only declared here has none to be at. The same rule and
9319    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
9320    /// way.
9321    #[test]
9322    fn a_set_of_a_name_this_file_does_not_define_says_so() {
9323        let messages = errors("__asm__(\".set here, elsewhere\");\n");
9324        assert!(
9325            messages
9326                .iter()
9327                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
9328                    && m.contains("E0697")),
9329            "{messages:?}"
9330        );
9331    }
9332
9333    /// `.incbin` is the one directive that reads something, and what it reads comes through the
9334    /// same file system the sources did.
9335    #[test]
9336    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
9337        let mut opts = options();
9338        opts.emit = EmitKind::Ir;
9339        let mut fs = MemoryFileSystem::new();
9340        fs.insert(
9341            "/main.c",
9342            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
9343        );
9344        fs.insert("seed", b"hi".to_vec());
9345        let result = compile(&opts, "/main.c", &fs);
9346        assert_eq!(result.messages, Vec::<String>::new());
9347        let text = result.text();
9348        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
9349    }
9350
9351    /// A file that is not there is the mistake a build makes when it runs the compiler from the
9352    /// wrong directory, and it is worth saying which file rather than saying the template failed.
9353    #[test]
9354    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
9355        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
9356        assert!(
9357            messages
9358                .iter()
9359                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
9360            "{messages:?}"
9361        );
9362    }
9363
9364    /// The line drawn is the same one the `asm` inside a function draws: directives are read and
9365    /// an instruction waits for an assembler. Refusing by name is what makes the wait visible.
9366    #[test]
9367    fn an_instruction_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
9368        for source in [
9369            "__asm__(\".text\\n.globl f\\nf:\\n  ret\\n\");\n",
9370            "__asm__(\".data\\n.set alias, 4\\n\");\n",
9371        ] {
9372            let messages = errors(source);
9373            assert!(
9374                messages
9375                    .iter()
9376                    .any(|m| m.contains("not supported yet")
9377                        && m.contains("in an `asm` at file scope")),
9378                "{source}\n{messages:?}"
9379            );
9380        }
9381    }
9382
9383    /// micropython's `nlr_push`, which is the program that asks for all of this. The body is the
9384    /// whole of the function: the return address is read out of `(%rsp)` where the call left it,
9385    /// the registers the convention preserves are saved by hand, and the frame that was just built
9386    /// is handed to a function written in C that never comes back.
9387    ///
9388    /// What is checked is what gcc writes for the same file. No prologue in front of the saves,
9389    /// since a push would move the return address the first of them reads. No epilogue and no
9390    /// `ret`, since the jump is where the function ends. And a `ud2` behind the jump, which is
9391    /// where control arrives if the jump is ever not taken and is exactly what gcc puts there.
9392    #[test]
9393    fn a_naked_function_is_its_own_prologue_and_its_own_ending() {
9394        let text = asm(concat!(
9395            "unsigned nlr_push_tail(void *nlr);\n",
9396            "__attribute__((naked)) unsigned nlr_push(void *nlr) {\n",
9397            "  __asm volatile(\n",
9398            "    \"movq (%rsp), %rax\\n\"\n",
9399            "    \"movq %rax, 16(%rdi)\\n\"\n",
9400            "    \"movq %rbx, 40(%rdi)\\n\"\n",
9401            "    \"jmp nlr_push_tail\\n\");\n",
9402            "}\n",
9403        ));
9404        assert!(text.contains("\tmovq\t(%rsp), %rax\n"), "{text}");
9405        assert!(text.contains("\tjmp\tnlr_push_tail\n"), "{text}");
9406        assert!(text.contains("\tud2\n"), "{text}");
9407        assert!(!text.contains("\tpushq\t"), "nothing is saved in front of it: {text}");
9408        assert!(!text.contains("\tret\n"), "the jump is where it ends: {text}");
9409    }
9410
9411    /// The three things a naked function may not ask for, each of which is a frame nothing sets up
9412    /// or a jump over an epilogue there is one of.
9413    #[test]
9414    fn what_a_function_without_a_prologue_cannot_be_given_is_refused() {
9415        let mut opts = options();
9416        opts.emit = EmitKind::Asm;
9417        for (source, why) in [
9418            (
9419                "__attribute__((naked)) void f(void) { volatile long a[8]; a[0] = 1; }\n",
9420                "bytes of frame",
9421            ),
9422            (
9423                "__attribute__((naked)) void f(int n) { char a[n]; __asm(\"nop\" ::\"r\"(a)); }\n",
9424                "has no prologue to point a frame pointer at it with",
9425            ),
9426            ("void elsewhere(void); void f(void) { __asm(\"jmp elsewhere\"); }\n", "jumps out of"),
9427        ] {
9428            let result = run(&opts, source);
9429            assert!(result.failed(), "expected this to be refused:\n{source}");
9430            assert!(
9431                result.messages.iter().any(|message| message.contains(why)),
9432                "{:?}",
9433                result.messages
9434            );
9435        }
9436    }
9437
9438    #[test]
9439    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
9440        let mut opts = options();
9441        opts.emit = EmitKind::Ir;
9442        for source in [
9443            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
9444            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
9445        ] {
9446            let result = run(&opts, source);
9447            assert!(result.failed(), "expected this to be reported:\n{source}");
9448            assert!(
9449                result.messages.iter().any(|m| m.contains("not supported yet")),
9450                "{:?}",
9451                result.messages
9452            );
9453        }
9454    }
9455
9456    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
9457    fn round_trip(source: &str) -> (String, String) {
9458        let printed = ir(source);
9459        let mut opts = options();
9460        opts.emit = EmitKind::Ir;
9461        let mut fs = MemoryFileSystem::new();
9462        fs.insert("/main.ir", printed.clone().into_bytes());
9463        let result = compile_ir(&opts, "/main.ir", &fs);
9464        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
9465        (printed, result.text().to_owned())
9466    }
9467
9468    #[test]
9469    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
9470        // The other half of the round trip test below, through the driver rather than through
9471        // the library, which is what makes the property something to run over a real program
9472        // rather than over the modules a test builds.
9473        let (printed, again) = round_trip(
9474            "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",
9475        );
9476        assert_eq!(printed, again);
9477    }
9478
9479    #[test]
9480    fn ir_that_is_not_ir_says_which_line_stopped_it() {
9481        let mut opts = options();
9482        opts.emit = EmitKind::Ir;
9483        let mut fs = MemoryFileSystem::new();
9484        let text = "\
9485; ModuleID = 'a.c'
9486; format 0
9487target triple = \"x86_64-unknown-linux-gnu\"
9488target datalayout = \"e-p:64:64-i64:64-S128\"
9489
9490func @f(), linkage(external) {
9491block0:
9492    frobnicate
9493}
9494";
9495        fs.insert("/main.ir", text.as_bytes().to_vec());
9496        let result = compile_ir(&opts, "/main.ir", &fs);
9497        assert!(result.failed());
9498        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
9499    }
9500
9501    #[test]
9502    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
9503        // A module that a person edited has not been through the verifier, and the return of
9504        // an `i32` from a function that returns nothing is the kind of thing editing produces.
9505        let mut opts = options();
9506        opts.emit = EmitKind::Ir;
9507        let mut fs = MemoryFileSystem::new();
9508        let text = "\
9509; ModuleID = 'a.c'
9510; format 0
9511target triple = \"x86_64-unknown-linux-gnu\"
9512target datalayout = \"e-p:64:64-i64:64-S128\"
9513
9514func @f(), linkage(external) {
9515block0:
9516    %0 = iconst.i32 1
9517    return %0
9518}
9519";
9520        fs.insert("/main.ir", text.as_bytes().to_vec());
9521        let result = compile_ir(&opts, "/main.ir", &fs);
9522        assert!(result.failed());
9523        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
9524    }
9525
9526    #[test]
9527    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
9528        // The C that became this is not here any more, so there is nothing to print a tree of.
9529        let mut fs = MemoryFileSystem::new();
9530        fs.insert("/main.ir", Vec::new());
9531        let result = compile_ir(&options(), "/main.ir", &fs);
9532        assert!(result.failed());
9533        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
9534    }
9535
9536    #[test]
9537    fn the_printed_ir_reads_back_as_the_same_module() {
9538        // The M2 exit criterion: the text is the module and nothing about it is lost by
9539        // writing it down. Anything the printer invents or the parser drops shows up here.
9540        let text = ir("\
9541struct point { int x, y; };
9542static const char greeting[] = \"hi\";
9543int table[4] = { 1, 2, 3 };
9544int puts(const char *);
9545double half(double x) { return x / 2.0; }
9546int f(int n) {
9547  int total = 0;
9548  for (int i = 0; i < n; i++) {
9549    if (i == 3) continue;
9550    total += table[i];
9551  }
9552  switch (n) {
9553    case 0: total = 1;
9554    case 1: total++; break;
9555    default: total = -total;
9556  }
9557  struct point p = { total, 1 };
9558  int *q = &p.y;
9559  puts(greeting);
9560  return p.x + *q;
9561}
9562int dispatch(int c) {
9563  void *p = c ? &&one : &&two;
9564  goto *p;
9565one:
9566  return 1;
9567two:
9568  return 2;
9569}
9570int assembly(int x, int *p) {
9571  int r;
9572  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
9573  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
9574  return r;
9575away:
9576  return 0;
9577}
9578");
9579        let mut names = Interner::new();
9580        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
9581        assert_eq!(rucc_ir::print(&module, &names), text);
9582    }
9583
9584    #[test]
9585    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
9586        // The point of the flag is that these two are the compilation rather than a description
9587        // of one, so both come out of the run that produced the object rather than out of a
9588        // second run under different flags.
9589        let mut opts = options();
9590        opts.emit = EmitKind::Object;
9591        opts.save_temps = rucc_session::SaveTemps::Object;
9592        let result = run(&opts, "#define N 2\nint a[N];\n");
9593        assert_eq!(result.messages, Vec::<String>::new());
9594        let text = result.temps.preprocessed.expect("the preprocessed text");
9595        assert!(text.contains("int a[2];"), "{text}");
9596        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
9597        let asm = result.temps.assembly.expect("the assembly");
9598        assert!(asm.contains("a:"), "{asm}");
9599        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
9600    }
9601
9602    #[test]
9603    fn nothing_is_kept_unless_the_flag_asked_for_it() {
9604        // A compilation that was not asked to keep anything must not pay for printing text
9605        // nobody will read, and the empty value is what says so.
9606        let mut opts = options();
9607        opts.emit = EmitKind::Object;
9608        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
9609    }
9610
9611    #[test]
9612    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
9613        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
9614        // what a report about the file being read wrongly has to have in it.
9615        let mut opts = options();
9616        opts.emit = EmitKind::Ir;
9617        opts.save_temps = rucc_session::SaveTemps::Cwd;
9618        let result = run(&opts, "int a;\n");
9619        assert!(result.temps.preprocessed.is_some());
9620        assert_eq!(result.temps.assembly, None);
9621    }
9622
9623    /// A stretch of a local's life, written short because these tests are about nothing else.
9624    fn span(from: u64, len: u64, held: rucc_debug::Held) -> rucc_debug::Span {
9625        rucc_debug::Span { from, len, held }
9626    }
9627
9628    #[test]
9629    fn two_stretches_that_meet_and_agree_come_out_as_one() {
9630        let one = span(0, 4, rucc_debug::Held::Reg(3));
9631        let two = span(4, 4, rucc_debug::Held::Reg(3));
9632        assert_eq!(settle(vec![two, one]), vec![span(0, 8, rucc_debug::Held::Reg(3))]);
9633    }
9634
9635    #[test]
9636    fn a_stretch_another_starts_inside_and_disagrees_with_ends_where_the_other_starts() {
9637        let one = span(0, 8, rucc_debug::Held::Reg(3));
9638        let two = span(4, 8, rucc_debug::Held::Reg(4));
9639        // The second starts where the declaration was given its value, so from there it is the
9640        // second and not the first.
9641        let settled = settle(vec![one, two]);
9642        assert_eq!(
9643            settled,
9644            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 8, rucc_debug::Held::Reg(4))]
9645        );
9646    }
9647
9648    #[test]
9649    fn a_stretch_cut_by_one_that_ends_first_does_not_come_back_after_it() {
9650        // The old value is still live after the new one is done with, because something else
9651        // reads it, but the declaration stopped holding it where the new one started.
9652        let one = span(0, 16, rucc_debug::Held::Reg(3));
9653        let two = span(4, 4, rucc_debug::Held::Reg(4));
9654        assert_eq!(
9655            settle(vec![one, two]),
9656            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 4, rucc_debug::Held::Reg(4))]
9657        );
9658    }
9659
9660    #[test]
9661    fn a_stretch_inside_another_that_agrees_with_it_cuts_nothing() {
9662        let one = span(0, 16, rucc_debug::Held::Reg(3));
9663        let two = span(4, 4, rucc_debug::Held::Reg(3));
9664        assert_eq!(settle(vec![one, two]), vec![span(0, 16, rucc_debug::Held::Reg(3))]);
9665    }
9666
9667    #[test]
9668    fn a_stretch_two_others_disagree_over_the_whole_of_says_nothing_at_all() {
9669        let one = span(0, 8, rucc_debug::Held::Reg(3));
9670        let two = span(0, 8, rucc_debug::Held::Frame(-16));
9671        assert_eq!(settle(vec![one, two]), Vec::new());
9672    }
9673
9674    #[test]
9675    fn stretches_with_a_gap_between_them_keep_the_gap() {
9676        let one = span(0, 4, rucc_debug::Held::Reg(3));
9677        let two = span(16, 4, rucc_debug::Held::Reg(3));
9678        assert_eq!(settle(vec![one, two]), vec![one, two]);
9679    }
9680
9681    /// A function of `len` bytes, since that is the only thing about one these tests look at.
9682    fn extent(len: usize) -> rucc_object::Extent {
9683        rucc_object::Extent {
9684            name: "f".to_owned(),
9685            start: 0,
9686            len,
9687            align: 1,
9688            binding: rucc_object::Binding::Global,
9689            visibility: rucc_object::Visibility::Default,
9690            patch: None,
9691        }
9692    }
9693
9694    /// A line table row at `at` built for the source bytes `lo` to `hi`.
9695    fn row(at: usize, lo: u32, hi: u32) -> rucc_asm::Row {
9696        let span = Span::new(lo, hi);
9697        rucc_asm::Row { at, span, inst: None }
9698    }
9699
9700    #[test]
9701    fn a_row_ends_where_the_next_address_begins() {
9702        let rows = [row(0, 0, 1), row(4, 1, 2), row(10, 2, 3)];
9703        assert_eq!(ends(&extent(16), &rows), vec![4, 10, 16]);
9704    }
9705
9706    #[test]
9707    fn rows_sharing_an_address_all_end_where_the_next_address_begins() {
9708        // Two instructions that encoded to nothing sit on the address of the one after them, and
9709        // none of the three ends in front of that one.
9710        let rows = [row(0, 0, 1), row(4, 1, 2), row(4, 2, 3), row(4, 3, 4)];
9711        assert_eq!(ends(&extent(12), &rows), vec![4, 12, 12, 12]);
9712    }
9713
9714    #[test]
9715    fn the_rows_of_a_scope_that_are_next_to_each_other_come_out_as_one_stretch() {
9716        let rows = [row(0, 0, 4), row(4, 10, 14), row(8, 14, 18), row(12, 40, 44)];
9717        let ends = ends(&extent(16), &rows);
9718        let scope = Span::new(8, 20);
9719        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 8 }]);
9720    }
9721
9722    #[test]
9723    fn a_scope_the_back_end_split_in_two_comes_out_as_two_stretches() {
9724        let rows = [row(0, 10, 14), row(4, 40, 44), row(8, 14, 18)];
9725        let ends = ends(&extent(12), &rows);
9726        let scope = Span::new(8, 20);
9727        let over = spread(scope, &ends, &rows);
9728        assert_eq!(
9729            over,
9730            vec![rucc_debug::Reach { from: 0, len: 4 }, rucc_debug::Reach { from: 8, len: 4 }]
9731        );
9732    }
9733
9734    #[test]
9735    fn a_row_with_no_source_of_its_own_belongs_to_no_scope() {
9736        // The prologue is the one of these every function has, and it is not inside any block.
9737        let rows = [rucc_asm::Row { at: 0, span: Span::DUMMY, inst: None }, row(4, 10, 14)];
9738        let ends = ends(&extent(8), &rows);
9739        let scope = Span::new(0, 20);
9740        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 4 }]);
9741    }
9742
9743    /// A scope of the unit, written short because these tests are about nothing else.
9744    fn scope(parent: Option<usize>, lo: u32, hi: u32) -> crate::shapes::Scope {
9745        let span = Span::new(lo, hi);
9746        crate::shapes::Scope { parent, span }
9747    }
9748
9749    #[test]
9750    fn a_function_gets_the_scopes_its_own_locals_are_in_and_nothing_else() {
9751        // Two functions' worth of scopes in one table, and this one is in the second pair.
9752        let scopes = [scope(None, 0, 10), scope(None, 20, 30), scope(Some(1), 22, 26)];
9753        let rows = [row(0, 22, 24), row(4, 26, 28)];
9754        let (out, at) = nests(&[Some(2)], &scopes, &extent(8), &rows);
9755        // The one the local is in and the one that is inside, numbered from zero for this
9756        // function, with the parent named by the entry it became rather than by where it was.
9757        assert_eq!(at.get(&1), Some(&0));
9758        assert_eq!(at.get(&2), Some(&1));
9759        assert_eq!(at.get(&0), None);
9760        assert_eq!(out.len(), 2);
9761        assert_eq!(out[0].parent, None);
9762        assert_eq!(out[1].parent, Some(0));
9763        assert_eq!(out[0].over, vec![rucc_debug::Reach { from: 0, len: 8 }]);
9764        assert_eq!(out[1].over, vec![rucc_debug::Reach { from: 0, len: 4 }]);
9765    }
9766
9767    #[test]
9768    fn a_local_written_straight_into_the_body_pulls_no_scope_in() {
9769        let scopes = [scope(None, 20, 30)];
9770        let rows = [row(0, 22, 24)];
9771        let (out, at) = nests(&[None], &scopes, &extent(4), &rows);
9772        assert_eq!(out, Vec::new());
9773        assert!(at.is_empty());
9774    }
9775
9776    #[test]
9777    fn a_scope_whose_code_all_went_away_is_still_one_of_the_functions_scopes() {
9778        // Nothing was built for the bytes it covers, so there is nowhere to say its names were
9779        // live. The entry is written anyway, since dropping it would move a local up into the
9780        // function and make it answer to a name it was not declared under.
9781        let scopes = [scope(None, 20, 30)];
9782        let rows = [row(0, 40, 44)];
9783        let (out, at) = nests(&[Some(0)], &scopes, &extent(4), &rows);
9784        assert_eq!(at.get(&0), Some(&0));
9785        assert_eq!(out.len(), 1);
9786        assert_eq!(out[0].over, Vec::new());
9787    }
9788}