Skip to main content

rucc_session/
lib.rs

1//! The `Session`: the options, the interner and the diagnostic sink that every stage of a
2//! single compilation is handed.
3//!
4//! Design: `spec/03-architecture.md` and `spec/04-driver-and-cli.md`. Layer rank 4, see
5//! `spec/18-package-layout.md`.
6//!
7//! Everything below the driver reaches the outside world through this type and not through
8//! `std::fs`, `std::env` or `println!`. That is the whole reason the compiler can be used as
9//! a library and tested without spawning a process, and it is enforced by the layer rule
10//! rather than by discipline.
11//!
12//! # Status
13//!
14//! Options, optimisation levels, emit kinds, diagnostic counting, the source map every span
15//! is resolved against, the file system the compiler reads through, the include search path
16//! and the headers the compiler itself ships are real. The parallel job model is still a
17//! placeholder.
18//!
19//! This crate is tier 3 in `spec/18-package-layout.md` section 18.5: its Rust API is
20//! explicitly unstable and will change without a major version bump.
21
22#![doc(html_root_url = "https://docs.rs/rucc-session/0.12.2")]
23
24mod fs;
25pub mod runtime;
26
27pub use crate::fs::{Dir, FileSystem, Found, IncludeForm, MemoryFileSystem, SearchPath, path_key};
28
29use std::borrow::Cow;
30use std::fmt;
31use std::str::FromStr;
32
33use rucc_base::Interner;
34use rucc_diag::{Diagnostic, Severity, SourceMap};
35use rucc_target::{Arch, Isa, Os, TargetInfo, Triple};
36
37/// An optimisation level.
38///
39/// `spec/16-performance.md` section 16.4 gives each level a throughput budget and a code
40/// quality budget, and the levels exist to make that tradeoff explicit rather than to be a
41/// dial. There is no `-O4`, because a level nobody can state the contract for is a level
42/// nobody can test.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
44pub enum OptLevel {
45    /// `-O0`. Compile as fast as possible and keep every variable inspectable.
46    #[default]
47    O0,
48    /// `-O1`. The cheap wins, at roughly the cost of `-O0`.
49    O1,
50    /// `-O2`. The full pipeline. This is the level the code quality claim is about.
51    O2,
52    /// `-O3`. `-O2` plus the transformations that trade size for speed.
53    O3,
54    /// `-Os`. Optimise for size, at roughly `-O2` compile time.
55    Os,
56    /// `-Oz`. Optimise for size, aggressively.
57    Oz,
58}
59
60impl OptLevel {
61    /// The flag that selects this level.
62    pub const fn as_flag(self) -> &'static str {
63        match self {
64            OptLevel::O0 => "-O0",
65            OptLevel::O1 => "-O1",
66            OptLevel::O2 => "-O2",
67            OptLevel::O3 => "-O3",
68            OptLevel::Os => "-Os",
69            OptLevel::Oz => "-Oz",
70        }
71    }
72
73    /// Whether this level optimises for size rather than speed.
74    pub const fn is_size(self) -> bool {
75        matches!(self, OptLevel::Os | OptLevel::Oz)
76    }
77
78    /// Whether the middle end runs at all.
79    pub const fn runs_optimizer(self) -> bool {
80        !matches!(self, OptLevel::O0)
81    }
82
83    /// Whether the instructions of a block are put in the order the machine finishes soonest.
84    ///
85    /// `spec/optimizer/38-scheduling-and-layout.md` section 38.6 says `-O2` and above, and gcc
86    /// turns `-fschedule-insns2` on at the two size levels as well, which costs nothing: a
87    /// schedule is a permutation of instructions that were all going to be written anyway, so it
88    /// is the one optimization here that cannot make a function larger.
89    pub const fn schedules(self) -> bool {
90        !matches!(self, OptLevel::O0 | OptLevel::O1)
91    }
92
93    /// Whether a call in tail position becomes a jump.
94    ///
95    /// `-O2` and above and the two size levels, which is where gcc turns
96    /// `-foptimize-sibling-calls` on. Not at `-O1`, where gcc leaves it off so that a backtrace
97    /// still shows every function the program went through.
98    pub const fn sibling_calls(self) -> bool {
99        !matches!(self, OptLevel::O0 | OptLevel::O1)
100    }
101
102    /// Whether every function keeps a frame pointer when the command line did not say.
103    ///
104    /// Only at `-O0`, which is where gcc keeps one. Code built without optimization is code
105    /// somebody is going to step through, and an asm statement written for that build may walk
106    /// the frame through `%rbp` itself, the way chibicc's own test of asm returns early.
107    pub const fn frame_pointer(self) -> bool {
108        matches!(self, OptLevel::O0)
109    }
110}
111
112impl fmt::Display for OptLevel {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        f.write_str(self.as_flag())
115    }
116}
117
118impl FromStr for OptLevel {
119    type Err = ();
120
121    /// Parses the part after `-O`, so `""` is `-O` which GCC treats as `-O1`.
122    fn from_str(s: &str) -> Result<Self, ()> {
123        Ok(match s {
124            "0" => OptLevel::O0,
125            "" | "1" => OptLevel::O1,
126            "2" => OptLevel::O2,
127            // GCC accepts `-O4` and above and treats them as `-O3`. Build systems in the
128            // wild do pass them, so matching that is cheaper than being right.
129            "3" | "4" | "5" | "6" | "7" | "8" | "9" => OptLevel::O3,
130            "s" => OptLevel::Os,
131            "z" => OptLevel::Oz,
132            _ => return Err(()),
133        })
134    }
135}
136
137/// How much of the memory safety monitor is on, from `-fsafety=`.
138///
139/// Design: `spec/safe-memory/15-integration.md` section 15.4. One flag rather than a plane at a
140/// time, because the tiers of `spec/safe-memory/02-threat-model.md` are the product and the
141/// modifiers are how somebody who has read that document departs from one.
142///
143/// The tiers agree about which accesses are checked and disagree about what happens when a check
144/// says no and about how much of the boundary is covered. That is why they are one value here and
145/// not three booleans: a build asks for a tier, and everything else follows from it.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
147pub enum Safety {
148    /// `-fsafety=off`. No checks and no runtime. The default, and what every existing build gets.
149    #[default]
150    Off,
151    /// `-fsafety=detect`. Tier D: report and carry on, for a test run or a fuzzer.
152    Detect,
153    /// `-fsafety=enforce`. Tier E: report and stop, for a program that faces the network.
154    Enforce,
155    /// `-fsafety=kernel`. Tier K: what a kernel can afford, with the allocator and the libc
156    /// wrappers taken out because a kernel has neither.
157    Kernel,
158}
159
160impl Safety {
161    /// The spelling this tier is asked for by, without the flag in front of it.
162    pub const fn as_str(self) -> &'static str {
163        match self {
164            Safety::Off => "off",
165            Safety::Detect => "detect",
166            Safety::Enforce => "enforce",
167            Safety::Kernel => "kernel",
168        }
169    }
170
171    /// Whether checks are inserted at all.
172    ///
173    /// The three tiers that are not `off` all insert the same checks at this milestone. What
174    /// separates them is the reporter and the boundary, which are milestones S2 and S3 in
175    /// `spec/safe-memory/16-milestones.md`.
176    pub const fn instruments(self) -> bool {
177        !matches!(self, Safety::Off)
178    }
179}
180
181impl fmt::Display for Safety {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        f.write_str(self.as_str())
184    }
185}
186
187impl FromStr for Safety {
188    type Err = ();
189
190    /// Parses the part after `-fsafety=`.
191    fn from_str(s: &str) -> Result<Self, ()> {
192        Ok(match s {
193            "off" => Safety::Off,
194            "detect" => Safety::Detect,
195            "enforce" => Safety::Enforce,
196            "kernel" => Safety::Kernel,
197            _ => return Err(()),
198        })
199    }
200}
201
202/// Whether padding participates in the init plane, from `-fsafety-init=`.
203///
204/// Design: `spec/safe-memory/09-type-init-and-races.md` section 9.3.
205///
206/// The correct rule is that a store which writes an object as a whole initializes it as a whole,
207/// padding included, and that a fill done a member at a time leaves the padding alone. That rule
208/// reports a structure filled member by member and then hashed, compared or written to a file,
209/// and it is right to: that is CWE-200 and it is the kernel infoleak KMSAN was built to find.
210///
211/// It is also every third program in a userspace corpus, where the bytes never leave the process
212/// and nobody is hunting an infoleak. So section 9.3 makes it a flag and splits the default:
213/// padding participates for the kernel profile, where the leak is the thing being looked for, and
214/// does not for library code, where it would be a torrent of reports about programs nobody is
215/// worried about. Document 12's scoreboard reports the two configurations separately for the same
216/// reason.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
218pub enum Padding {
219    /// `-fsafety-init=nopadding`. A store through a member says the padding after it holds
220    /// something too, so a record filled a member at a time comes out entirely written.
221    #[default]
222    Ignored,
223    /// `-fsafety-init=padding`. A store through a member says only what it wrote, which is
224    /// section 9.3's rule and is what makes the infoleak visible.
225    Tracked,
226}
227
228impl Padding {
229    /// The spelling this is asked for by, without the flag in front of it.
230    pub const fn as_str(self) -> &'static str {
231        match self {
232            Padding::Ignored => "nopadding",
233            Padding::Tracked => "padding",
234        }
235    }
236}
237
238impl fmt::Display for Padding {
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        f.write_str(self.as_str())
241    }
242}
243
244impl FromStr for Padding {
245    type Err = ();
246
247    /// Parses the part after `-fsafety-init=`.
248    fn from_str(s: &str) -> Result<Self, ()> {
249        Ok(match s {
250            "nopadding" => Padding::Ignored,
251            "padding" => Padding::Tracked,
252            _ => return Err(()),
253        })
254    }
255}
256
257/// Whether an access has to stay inside the member it names, from `-fsafety-subobject`.
258///
259/// Design: `spec/safe-memory/09-type-init-and-races.md` section 9.4, which is row S4 of document
260/// 03 and is the class Fil-C, CHERI by default and ARM MTE all miss. Their metadata is per
261/// allocation and a member is not an allocation, so an overflow from one member of a structure
262/// into the next is invisible to all three. The type plane is byte granular, so it is not
263/// invisible here.
264///
265/// A flag rather than a default because of what a store means. C 6.5 says a store to allocated
266/// storage sets that storage's effective type, so a write that leaves one member and lands in the
267/// next is, read literally, a program retyping bytes it owns. Every buffer that gets reused for a
268/// second kind of value does the same thing on purpose. So the question a store asks is only asked
269/// when somebody has said they want it asked, and what they get in return is the write half of
270/// S4 that nothing else catches.
271///
272/// The read half is not behind this and never was: a read that disagrees with the plane is
273/// judgement J1 at every tier, because reading bytes back through a type they were not stored
274/// through is undefined however the pointer got there.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
276pub enum Subobject {
277    /// No `-fsafety-subobject`. A store records what it wrote and is asked nothing.
278    #[default]
279    Off,
280    /// `-fsafety-subobject`. A store asks the plane whether the bytes it is about to write agree
281    /// with the type it writes them through, which catches an overflow out of a member into a
282    /// member of a different type.
283    ///
284    /// Two adjacent members of the same type are indistinguishable to this, which section 9.4
285    /// states plainly: `struct { int a; int b; }` overflowing from `a` into `b` writes `int` over
286    /// `int` and there is nothing for the plane to disagree with. That is what
287    /// `-fsafety-subobject=strict` is for and it is not here yet.
288    Members,
289}
290
291impl Subobject {
292    /// The spelling this is asked for by, without the flag in front of it.
293    pub const fn as_str(self) -> &'static str {
294        match self {
295            Subobject::Off => "off",
296            Subobject::Members => "members",
297        }
298    }
299
300    /// Whether a store asks the type plane anything.
301    pub const fn asks(self) -> bool {
302        matches!(self, Subobject::Members)
303    }
304}
305
306impl fmt::Display for Subobject {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        f.write_str(self.as_str())
309    }
310}
311
312/// Whether pointer races are watched, from `-fsafety-races=`.
313///
314/// Design: `spec/safe-memory/09-type-init-and-races.md` section 9.5, which is document 03's C1
315/// through C4 and is judgement J9 of document 04 section 4.4. A thread counts its own metadata
316/// stores, a store through a pointer shaped slot leaves that count in the epoch plane, and an
317/// access that finds a count from another thread which nothing it has done orders is a race that
318/// really happened in the interleaving that really ran.
319///
320/// A flag rather than a default, and the reason is not cost. It is that this is the one plane in
321/// the compiler where instrumentation nobody wrote costs a false report instead of a missed one.
322/// Every ordering the monitor has was carried by a synchronization edge somebody interposed, so two
323/// threads that an edge nobody saw really did join look exactly like two threads nothing joined.
324/// The edges that are calls are interposed already, and the ordering that is not a call at all is
325/// emitted by this pass beside the checks: an atomic that publishes gets a `meta_release` in front
326/// of it and one that takes gets a `meta_acquire` after it. A bare `atomic_thread_fence` gets the
327/// same pair with no key, since it orders against every thread rather than against an object and so
328/// has no address an edge could be keyed on, and the runtime holds one clock for every fence in the
329/// program rather than a table.
330///
331/// Which is also why the default stays [`Races::Off`] after the flag works. Turning it on is a
332/// decision about a program, not about a build.
333#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
334pub enum Races {
335    /// `-fsafety-races=off`. Nothing records into the epoch plane and nothing asks it anything.
336    #[default]
337    Off,
338    /// `-fsafety-races=metadata`. The classes that produce a wrong pointer rather than a wrong
339    /// number, which section 9.5 lists as C1, C3 and C4, and which Tier E carries.
340    Metadata,
341    /// `-fsafety-races=pointer`. The same, and C2 as well, which is a race on a pointer word
342    /// reported in its own right rather than only used to decide one of the other three.
343    Pointer,
344}
345
346impl Races {
347    /// The spelling this is asked for by, without the flag in front of it.
348    pub const fn as_str(self) -> &'static str {
349        match self {
350            Races::Off => "off",
351            Races::Metadata => "metadata",
352            Races::Pointer => "pointer",
353        }
354    }
355
356    /// Whether a store through a pointer shaped slot records which thread made it, and asks first
357    /// whether another thread got there with nothing in between.
358    ///
359    /// Both of the modes that are not off. Every class section 9.5 lists is decided by comparing
360    /// against a stamp a store left behind, so both of them record, and the question a store puts
361    /// is C3, the metadata race, which both of them report.
362    pub const fn records(self) -> bool {
363        !matches!(self, Races::Off)
364    }
365
366    /// Whether a load of a pointer asks the same question, which is where the two modes differ.
367    ///
368    /// C2 of section 9.5, the general pointer word race, which the section lists apart from the
369    /// other three because it is the class reported in its own right rather than used to decide one
370    /// of them. Tier E carries `metadata` and not this, so a build that wants every race a load can
371    /// see has to ask for it by name.
372    pub const fn reads(self) -> bool {
373        matches!(self, Races::Pointer)
374    }
375}
376
377impl fmt::Display for Races {
378    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379        f.write_str(self.as_str())
380    }
381}
382
383impl FromStr for Races {
384    type Err = ();
385
386    /// Parses the part after `-fsafety-races=`.
387    fn from_str(s: &str) -> Result<Self, ()> {
388        Ok(match s {
389            "off" => Races::Off,
390            "metadata" => Races::Metadata,
391            "pointer" => Races::Pointer,
392            _ => return Err(()),
393        })
394    }
395}
396
397/// Whether the `restrict` contract is checked, from `-fsafety-restrict`.
398///
399/// Design: `spec/safe-memory/09-type-init-and-races.md` section 9.6, which is row Y8 of document
400/// 03 and is judgement J8. C 6.7.3.1 says that if an object reachable through a `restrict` pointer
401/// declared in a block is modified anywhere in that block, every access to that object in that
402/// block goes through that pointer. Nothing about one access decides it, which is why document 04
403/// section 4.6 keeps it out of J1.
404///
405/// A flag rather than a default for two reasons, and neither of them is the one
406/// [`Subobject`] has. The first is cost, and it is a bad distribution rather than a large number:
407/// an access inside a block that declares `restrict` pointers pays a scan of that block's record,
408/// and blocks that declare them are the numeric kernels and the `mem` functions, which is exactly
409/// where the hot loops are. Code with no `restrict` in it pays nothing at all. The second is that
410/// the record is the union of what each pointer reached, so two pointers striding through one array
411/// without ever landing on the same byte are reported, and by the letter of the standard those are
412/// different objects and that is not a violation.
413///
414/// The second one is not an imprecision to apologise for. This check exists because a violated
415/// `restrict` is a miscompilation, and what the optimizer acts on is that the ranges are disjoint,
416/// so a program the union rule reports is a program the optimizer is entitled to break. It is
417/// still a report about a program the standard permits, which is a decision that belongs to the
418/// build rather than to this compiler.
419#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
420pub enum Promise {
421    /// No `-fsafety-restrict`. An access says which `restrict` pointer it went through, because
422    /// the alias analysis reads that, and nothing asks whether two of them met.
423    #[default]
424    Off,
425    /// `-fsafety-restrict`. Every block that declares `restrict` pointers keeps a record of what
426    /// each of them reached, and every access through one asks whether another got there first.
427    Blocks,
428}
429
430impl Promise {
431    /// The spelling this is asked for by, without the flag in front of it.
432    pub const fn as_str(self) -> &'static str {
433        match self {
434            Promise::Off => "off",
435            Promise::Blocks => "blocks",
436        }
437    }
438
439    /// Whether a block keeps a record and an access asks about it.
440    pub const fn checks(self) -> bool {
441        matches!(self, Promise::Blocks)
442    }
443}
444
445impl fmt::Display for Promise {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        f.write_str(self.as_str())
448    }
449}
450
451/// How far a name reaches outside a shared library when nothing in the source said.
452///
453/// `-fvisibility=`, which is written on every cmake project that cares about its exports and is
454/// the way a library ships a small documented interface instead of every name it happens to
455/// define. The attribute in the source wins wherever one was written, which is what makes the
456/// flag a default rather than an override and what lets `-fvisibility=hidden` be put on a whole
457/// tree and the dozen exported names marked one at a time.
458///
459/// Three answers to four spellings. `internal` is `hidden` plus a promise about never taking the
460/// address across a component boundary, and nothing here derives anything from that promise, so
461/// what it gets is the same symbol with a weaker claim on it.
462#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
463pub enum Visibility {
464    /// `-fvisibility=default`. Exported and interposable, which is what a name gets when the flag
465    /// is not written at all and what gcc does by default too.
466    #[default]
467    Default,
468    /// `-fvisibility=hidden` and `-fvisibility=internal`. Not in the dynamic symbol table.
469    Hidden,
470    /// `-fvisibility=protected`. In the dynamic symbol table, and a reference from inside the
471    /// library binds to the definition inside it.
472    Protected,
473}
474
475impl Visibility {
476    /// The spelling this is asked for by, without the flag in front of it.
477    ///
478    /// One spelling each, so `internal` is not here: it is a way of asking for `hidden` rather
479    /// than an answer of its own.
480    pub const fn as_str(self) -> &'static str {
481        match self {
482            Visibility::Default => "default",
483            Visibility::Hidden => "hidden",
484            Visibility::Protected => "protected",
485        }
486    }
487}
488
489impl fmt::Display for Visibility {
490    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491        f.write_str(self.as_str())
492    }
493}
494
495impl FromStr for Visibility {
496    type Err = ();
497
498    /// Parses the part after `-fvisibility=`.
499    fn from_str(s: &str) -> Result<Self, ()> {
500        Ok(match s {
501            "default" => Visibility::Default,
502            "hidden" | "internal" => Visibility::Hidden,
503            "protected" => Visibility::Protected,
504            _ => return Err(()),
505        })
506    }
507}
508
509/// How the debug sections are compressed, which is what `-gz` asks.
510///
511/// Debug information is much larger than the code it describes and almost never read, so an ELF
512/// section holding it may be stored compressed: the section keeps its name, gains the
513/// `SHF_COMPRESSED` flag and starts with a header saying what it decompresses to, and every reader
514/// that understands the flag unpacks it on the way in. A distribution that ships debug symbols for
515/// everything it builds saves more from this than from anything else it passes.
516///
517/// Nothing here compresses one yet, so every answer produces the same bytes and an object built
518/// with `-gz=zstd` is identical to one built without the flag. There are sections to compress now,
519/// which makes this a flag waiting on a compressor rather than one waiting on a producer, and
520/// `crates/rucc-debug` is where that is written down.
521#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
522pub enum Compress {
523    /// `-gz=none`, and what a command line that says nothing gets. gcc's default is the same.
524    #[default]
525    None,
526    /// `-gz` and `-gz=zlib`. The ELF way, with the `SHF_COMPRESSED` flag and an `Elf64_Chdr` in
527    /// front of the data. Bare `-gz` means this one, which is worth knowing because the manual
528    /// describes the flag without saying so.
529    Zlib,
530    /// `-gz=zlib-gnu`. The older way, where the section is renamed from `.debug_info` to
531    /// `.zdebug_info` and carries `ZLIB` and a length instead of a real header. Kept because
532    /// binutils still reads it and some build systems still ask for it by name.
533    ZlibGnu,
534    /// `-gz=zstd`. The same arrangement as `Zlib` with a different algorithm in the header, which
535    /// packs debug information smaller and unpacks it faster.
536    Zstd,
537}
538
539impl Compress {
540    /// The spelling this is asked for by, without the `-gz=` in front of it.
541    pub const fn as_str(self) -> &'static str {
542        match self {
543            Compress::None => "none",
544            Compress::Zlib => "zlib",
545            Compress::ZlibGnu => "zlib-gnu",
546            Compress::Zstd => "zstd",
547        }
548    }
549}
550
551impl fmt::Display for Compress {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        f.write_str(self.as_str())
554    }
555}
556
557impl FromStr for Compress {
558    type Err = ();
559
560    /// Parses the part after `-gz=`. Bare `-gz` is not this function's business because there is
561    /// nothing after the flag to hand it.
562    fn from_str(s: &str) -> Result<Self, ()> {
563        Ok(match s {
564            "none" => Compress::None,
565            "zlib" => Compress::Zlib,
566            "zlib-gnu" => Compress::ZlibGnu,
567            "zstd" => Compress::Zstd,
568            _ => return Err(()),
569        })
570    }
571}
572
573/// How many processes the link time work is spread over, which is what `-flto=` takes.
574///
575/// Named for the flag rather than for what it counts, because `Jobs` in the driver is already the
576/// answer to how many files are compiled at once and the two numbers are not the same number.
577///
578/// The link time half of link time optimization is where all of the time goes, because it is the
579/// half that has the whole program in front of it, and gcc's answer is to cut the program into
580/// pieces and generate code for the pieces at once. This says how many at once.
581#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
582pub enum LtoJobs {
583    /// Bare `-flto`, and `-flto=1`. One process, which is what gcc does when the flag is written
584    /// without a number after it.
585    #[default]
586    One,
587    /// `-flto=auto`. As many as the machine has, worked out when the link runs.
588    Auto,
589    /// `-flto=jobserver`. As many as `make` is willing to hand out, asked for through the
590    /// jobserver pipe it puts in the environment, which is the only answer that does not fight
591    /// with the rest of a parallel build for the same cores.
592    Jobserver,
593    /// `-flto=<n>`. Exactly that many. gcc refuses a zero, so this is never one.
594    Count(u32),
595}
596
597impl FromStr for LtoJobs {
598    type Err = ();
599
600    /// Parses the part after `-flto=`. A number has to be positive, which is gcc's rule: `-flto=0`
601    /// is refused rather than read as `-fno-lto`.
602    fn from_str(s: &str) -> Result<Self, ()> {
603        Ok(match s {
604            "auto" => LtoJobs::Auto,
605            "jobserver" => LtoJobs::Jobserver,
606            _ => match s.parse::<u32>() {
607                Ok(1) => LtoJobs::One,
608                Ok(n) if n > 1 => LtoJobs::Count(n),
609                _ => return Err(()),
610            },
611        })
612    }
613}
614
615impl fmt::Display for LtoJobs {
616    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617        match self {
618            LtoJobs::One => f.write_str("1"),
619            LtoJobs::Auto => f.write_str("auto"),
620            LtoJobs::Jobserver => f.write_str("jobserver"),
621            LtoJobs::Count(n) => write!(f, "{n}"),
622        }
623    }
624}
625
626/// How the program is cut up before the link time work is spread over it, from `-flto-partition=`.
627///
628/// A partition is a set of functions that are generated together, and where the cuts fall decides
629/// both how well the work spreads and how much is visible from inside one piece. The names are
630/// gcc's and so are the shapes: one piece per input file, pieces balanced by size, one piece for
631/// the whole program, a piece per function, or no partitioning at all.
632#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
633pub enum Partition {
634    /// `-flto-partition=balanced`, and what gcc does when nothing asks. Pieces of roughly equal
635    /// size, which is the answer that spreads the work best and is why it is the default.
636    #[default]
637    Balanced,
638    /// `-flto-partition=1to1`. One piece per input file, which keeps the generated code in the
639    /// same order the inputs were in and is what a build comparing two outputs wants.
640    OneToOne,
641    /// `-flto-partition=one`. The whole program in one piece, which is the most the optimizer can
642    /// see at once and the least the work can be spread over.
643    One,
644    /// `-flto-partition=max`. A piece per function, which is the other end of the same trade.
645    Max,
646    /// `-flto-partition=none`. No partitioning, and no streaming back out to be generated in
647    /// pieces either.
648    None,
649}
650
651impl Partition {
652    /// The spelling this is asked for by, without the `-flto-partition=` in front of it.
653    pub const fn as_str(self) -> &'static str {
654        match self {
655            Partition::Balanced => "balanced",
656            Partition::OneToOne => "1to1",
657            Partition::One => "one",
658            Partition::Max => "max",
659            Partition::None => "none",
660        }
661    }
662}
663
664impl fmt::Display for Partition {
665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666        f.write_str(self.as_str())
667    }
668}
669
670impl FromStr for Partition {
671    type Err = ();
672
673    /// Parses the part after `-flto-partition=`.
674    fn from_str(s: &str) -> Result<Self, ()> {
675        Ok(match s {
676            "balanced" => Partition::Balanced,
677            "1to1" => Partition::OneToOne,
678            "one" => Partition::One,
679            "max" => Partition::Max,
680            "none" => Partition::None,
681            _ => return Err(()),
682        })
683    }
684}
685
686/// What the `-flto` family asked for, which is a whole optimization this compiler does not do yet.
687///
688/// Link time optimization is the optimizer run once over the whole program instead of once per
689/// translation unit, which is the only way an inliner ever sees across a file boundary and is
690/// where most of what is left on the table after `-O2` is. `spec/09-optimizer.md` says how it will
691/// work here: the IR goes into a section of the object, the driver finds those sections at link
692/// time, merges them into one module and generates code with everything visible.
693///
694/// None of that exists, so the whole family is read, checked and recorded rather than acted on.
695/// That is a different answer from the one `-gsplit-dwarf` gets in the same specification, and the
696/// difference is what ignoring each of them does. Ignoring `-gsplit-dwarf` means a file a build
697/// asked for never appears. Ignoring this means a program that is correct and slower than it could
698/// have been, which is what section 4.1 means by a hint about speed, and which is also what every
699/// compilation at `-O0` already is.
700///
701/// The other half of the argument is about the object. gcc's `-flto` object holds the bytecode and
702/// no machine code at all, so it is only useful to a link that knows about it; the objects here
703/// always hold the code, which is what `-ffat-lto-objects` asks gcc for. So a build that passes
704/// `-flto` to this compiler gets objects that are strictly more usable than the ones it would have
705/// got, rather than different ones.
706#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
707pub struct Lto {
708    /// Whether the last of `-flto` and `-fno-lto` on the command line was the first of the two.
709    pub requested: bool,
710    /// How many processes to spread the link time work over.
711    pub jobs: LtoJobs,
712    /// How the program is cut up before the work is spread.
713    pub partition: Partition,
714    /// How hard to compress the IR on its way into the object, from `-flto-compression-level=`,
715    /// where `None` means whatever the compressor does when nobody says. Between 0 and 19, which
716    /// is zstd's range and is the range gcc checks against.
717    pub compression: Option<u8>,
718}
719
720/// What the profile reading half of the `-fprofile` family asked for.
721///
722/// A profile is a count per edge, gathered by running a build of the program that was instrumented
723/// to count, and read back on a second compilation so that the optimizer knows which way each
724/// branch actually went. It is worth more than any single optimization, because almost everything
725/// the optimizer decides is a guess about a frequency that the counts simply state.
726///
727/// Nothing here reads one yet, so this is recorded rather than acted on, and the family splits in
728/// two rather than being taken or refused as a whole. The half recorded here is the half that only
729/// costs speed when it is ignored: a build that asks to read a profile and is not read one gets the
730/// program it would have got anyway, which is what section 4.1 means by a hint about speed. The
731/// other half writes files, and that half is refused by the driver rather than landing here, on the
732/// same reading `-gsplit-dwarf` gets: a program instrumented by `-fprofile-generate` writes a
733/// `.gcda` when it runs and `-ftest-coverage` writes a `.gcno` beside the object, and ignoring
734/// either means a build waits for a file that never arrives and then quietly optimizes against no
735/// counts at all.
736///
737/// gcc's own measurement is the argument for the split. `-fprofile-use` on a file with no counts
738/// beside it produces an object byte for byte identical to the one no flag produces, and warns; the
739/// same file under `-fprofile-generate` grows from 71 bytes of code to 375 with 296 bytes of
740/// counters beside it. So one half of the family is already a no-op in gcc when there is nothing to
741/// read, and the other half is never one.
742#[derive(Debug, Clone, PartialEq, Eq, Default)]
743pub struct Profile {
744    /// Whether the last of `-fprofile-use` and `-fno-profile-use` on the command line was the
745    /// first of the two.
746    pub requested: bool,
747    /// Where to read the counts from, from `-fprofile-use=<path>`, where `None` means beside the
748    /// object the way gcc looks when nobody says. A directory or a file, which is gcc's rule and
749    /// is not something this can tell apart without looking at the filesystem.
750    pub path: Option<String>,
751    /// Where the whole family's files live, from `-fprofile-dir=`. Separate from `path` because
752    /// gcc keeps them separate: this one moves the counts for the generating half as well.
753    pub dir: Option<String>,
754    /// Whether the path recorded in those files is made absolute, from `-fprofile-abs-path`. It is
755    /// what a build with several object directories under one source tree needs so that two files
756    /// of the same name do not land on one set of counts.
757    pub absolute: bool,
758    /// Whether counts that do not add up are repaired rather than refused, from
759    /// `-fprofile-correction`. A program that forked or was killed while it ran leaves counts that
760    /// no single execution could have produced, and this says to make the best of them.
761    pub correction: bool,
762    /// Whether the parts of the program the training run never reached are optimized as if they
763    /// were cold rather than as if nothing were known about them, from `-fprofile-partial-training`.
764    pub partial_training: bool,
765}
766
767/// Which functions get a stack protector, which is what the `-fstack-protector` family asks.
768///
769/// A canary is a word the prologue copies into the frame above everything a local can be written
770/// through, and the epilogue compares it against the copy the runtime still holds before it
771/// returns. A write that runs off the end of a local and keeps going passes the canary on its way
772/// to the return address, so a function that returns with the word changed calls
773/// `__stack_chk_fail` instead of returning at all.
774///
775/// Which functions are worth the slot and the comparison is what the three levels disagree about,
776/// and the middle one is the one that matters: every distribution has built its packages with
777/// `-fstack-protector-strong` for a decade, so a compiler that cannot take the flag cannot be the
778/// `CC` of a package build whatever else it can do.
779#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
780pub enum Protector {
781    /// `-fno-stack-protector`, and what a command line that says nothing gets. gcc's own default
782    /// is the same, and it is the distributions rather than the compiler that turn it on.
783    #[default]
784    None,
785    /// `-fstack-protector`. A function with a local array of at least eight bytes, or one whose
786    /// stack grows while it runs.
787    Buffers,
788    /// `-fstack-protector-strong`. Any of those, and any function with a local array at all, a
789    /// local holding one, or a local whose address is taken.
790    Strong,
791    /// `-fstack-protector-all`. Every function that has a frame.
792    All,
793}
794
795/// What overflows rather than being undefined, from `-fwrapv` and its relatives.
796///
797/// C says a signed addition that overflows and a pointer that walks off the end of the object it
798/// points into are both undefined, and an optimizer that believes it reads a great deal into every
799/// loop: that a counter going up one at a time never turns round, that an index widened to an
800/// address may be widened before the arithmetic rather than after, that a bound is reached. These
801/// flags withdraw exactly that. They do not make the program mean something else, they make it mean
802/// less, and the code that asks for them is code that overflows on purpose and wants the answer the
803/// machine gives rather than the answer the standard declines to give.
804///
805/// Two of them because gcc has two, and a build that wants one usually wants the other. Signed
806/// arithmetic and pointer arithmetic are separate assumptions and a kernel turns both off.
807///
808/// `-ftrapv` is the third answer to the first question and is here for that reason. Undefined,
809/// wrapping and stopping are the three things a signed overflow can be, and a command line picks
810/// one of them: the last of `-fwrapv` and `-ftrapv` wins, which is gcc's behaviour and what makes
811/// them one field rather than two that can both be set.
812#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
813pub struct Wrapping {
814    /// Whether signed arithmetic wraps, from `-fwrapv`.
815    pub signed: bool,
816    /// Whether pointer arithmetic wraps, from `-fwrapv-pointer`.
817    pub pointer: bool,
818    /// Whether a signed overflow stops the program instead, from `-ftrapv`.
819    ///
820    /// Never set at the same time as [`Wrapping::signed`], since a program cannot both wrap and
821    /// stop, and the driver is what keeps that true by clearing each when the other is asked for.
822    pub trap: bool,
823}
824
825impl Wrapping {
826    /// Both of them, which is what `-fno-strict-overflow` asks for.
827    ///
828    /// gcc says so itself: its help text for `-fstrict-overflow` reads "negated as `-fwrapv`
829    /// `-fwrapv-pointer`", so the older flag is a name for the pair rather than a third knob. And
830    /// asking for wrapping is asking for not stopping, so this is the whole answer and not two
831    /// thirds of one.
832    pub const ALL: Self = Self { signed: true, pointer: true, trap: false };
833
834    /// Neither, which is the default and what a command line that says nothing about any of this
835    /// gets.
836    pub const NONE: Self = Self { signed: false, pointer: false, trap: false };
837}
838
839/// A list of `old=new` rewrites to apply to a path before it is written into the output, which is
840/// what the `-f*-prefix-map=` family asks for.
841///
842/// The point of them is a build whose output does not depend on where it was built. A path is the
843/// last thing in an object that a second machine cannot reproduce: two people who check out the
844/// same commit and run the same compiler get the same instructions and different `__FILE__`
845/// strings, and a distribution that wants to prove its binaries came from its sources has to make
846/// that difference go away. So the build says what its root is called, and every path that would
847/// name the real one names that instead.
848///
849/// The rule is a plain string prefix and nothing more, which is worth saying because it looks like
850/// it ought to be about directories. gcc compares the characters, so `s=B` turns `sub/h.h` into
851/// `Bub/h.h`, and an empty `old` matches everything and puts `new` in front of it. The path
852/// compared against is the one the search found, so a header reached through a relative `-I` is
853/// mapped as a relative path and the same header reached through an absolute one is mapped as an
854/// absolute path.
855#[derive(Debug, Clone, Default, PartialEq, Eq)]
856pub struct PrefixMap {
857    /// The rewrites, in the order the command line gave them.
858    entries: Vec<(String, String)>,
859}
860
861impl PrefixMap {
862    /// No rewrites, which is what a command line that says nothing about this gets.
863    #[must_use]
864    pub fn new() -> Self {
865        Self::default()
866    }
867
868    /// Whether nothing was asked for, which is the case worth not spending anything on.
869    #[must_use]
870    pub fn is_empty(&self) -> bool {
871        self.entries.is_empty()
872    }
873
874    /// Adds a rewrite, which is what one flag on the command line is.
875    pub fn push(&mut self, old: impl Into<String>, new: impl Into<String>) {
876        self.entries.push((old.into(), new.into()));
877    }
878
879    /// The two halves of one flag's argument, split at the last `=` rather than the first.
880    ///
881    /// That is where gcc splits it, and it is the answer that makes a path containing an `=`
882    /// mappable: `-ffile-prefix-map=/home/a=b=/src` maps the directory `/home/a=b`. The cost is
883    /// that a replacement cannot contain one, which is the rarer thing to want. `None` when there
884    /// is no `=` at all, which gcc refuses rather than reading as a mapping to nothing.
885    #[must_use]
886    pub fn split(arg: &str) -> Option<(&str, &str)> {
887        arg.rsplit_once('=')
888    }
889
890    /// `path` with the last rewrite that matches it applied, or `path` where none does.
891    ///
892    /// The last rather than the first, because that is gcc's answer and because it is the one a
893    /// build relies on: a mapping set for the whole project and a narrower one set for one
894    /// directory is a command line where the second is meant to win.
895    #[must_use]
896    pub fn apply<'a>(&self, path: &'a str) -> Cow<'a, str> {
897        for (old, new) in self.entries.iter().rev() {
898            if let Some(rest) = path.strip_prefix(old.as_str()) {
899                return Cow::Owned(format!("{new}{rest}"));
900            }
901        }
902        Cow::Borrowed(path)
903    }
904}
905
906/// The three answers to the question the `-f*-prefix-map=` family asks, which is one question
907/// asked about three kinds of output.
908///
909/// They are separate because gcc's flags are separate and a build uses that: a distribution maps
910/// its debug paths to something a debugger can find the sources under and leaves `__FILE__` alone,
911/// or maps `__FILE__` so that an assertion message does not name a build directory and leaves the
912/// debug info pointing at the real tree. `-ffile-prefix-map=` is the shorthand for all three and is
913/// what a build that simply wants to be reproducible writes.
914#[derive(Debug, Clone, Default, PartialEq, Eq)]
915pub struct PrefixMaps {
916    /// What `__FILE__` and `__BASE_FILE__` are rewritten by, from `-fmacro-prefix-map=`.
917    ///
918    /// The only one of the three this compiler acts on today, because it is the only one whose
919    /// output exists: `__FILE__` is a string literal in the binary and an assertion message a user
920    /// reads.
921    pub macros: PrefixMap,
922    /// What a path in the debug info is rewritten by, from `-fdebug-prefix-map=`.
923    ///
924    /// Read by the driver rather than by `rucc-debug`, because the driver is the layer where a path
925    /// is still a path and by the time one reaches the DWARF writer it is a string in a table that
926    /// nothing is allowed to reinterpret. Every path that reaches the line table goes through it,
927    /// the unit's own name and the directory it was compiled in among them.
928    pub debug: PrefixMap,
929    /// What a path in the profile data is rewritten by, from `-fprofile-prefix-map=`.
930    ///
931    /// Nothing reads this yet either, and for the same reason: there is no profile data.
932    pub profile: PrefixMap,
933}
934
935/// How far a multiply and an addition may be fused into one rounding, from `-ffp-contract=`.
936///
937/// A fused multiply add computes `a * b + c` with one rounding instead of two, which is both
938/// faster and closer to the exact answer, and is therefore a different answer. C lets an
939/// implementation do it within one expression and lets a program turn it off with the
940/// `FP_CONTRACT` pragma, gcc does it across a whole function by default, and code that cares about
941/// reproducing a result bit for bit turns it off everywhere.
942///
943/// This is the command line's answer to that question, and it is carried into the IR as an
944/// attribute on each function so that the code generator still has it by the time it would matter.
945/// It is a separate question from the flag on one instruction: a licence granted to an expression
946/// the optimizer has since taken apart is a licence about operations that no longer sit together,
947/// and only the function level answer survives that.
948#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
949pub enum Contract {
950    /// `-ffp-contract=off`. Never, so every rounding the source asked for happens.
951    ///
952    /// The default here, which is not gcc's. gcc defaults to `fast` under its own dialects and to
953    /// `off` under a strict `-std=`, and the reason the default is this one anyway is that nothing
954    /// in this compiler fuses anything: the two settings are the same program today, and of the two
955    /// this is the one that does not write a licence nobody reads onto every function in the file.
956    /// The day the code generator learns to fuse, the default moves to gcc's, and that is a change
957    /// to the code generator rather than to this flag.
958    #[default]
959    Off,
960    /// `-ffp-contract=on`. Within one expression, which is what C allows an implementation to do
961    /// without being asked.
962    On,
963    /// `-ffp-contract=fast`. Anywhere in the function, across statements and across whatever the
964    /// optimizer has rearranged, which is what gcc does under its own dialects.
965    Fast,
966}
967
968impl Contract {
969    /// The spelling after the `=`.
970    pub const fn as_str(self) -> &'static str {
971        match self {
972            Contract::Off => "off",
973            Contract::On => "on",
974            Contract::Fast => "fast",
975        }
976    }
977}
978
979impl fmt::Display for Contract {
980    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981        f.write_str(self.as_str())
982    }
983}
984
985impl FromStr for Contract {
986    type Err = ();
987
988    fn from_str(s: &str) -> Result<Self, ()> {
989        Ok(match s {
990            "off" => Contract::Off,
991            "on" => Contract::On,
992            "fast" => Contract::Fast,
993            _ => return Err(()),
994        })
995    }
996}
997
998impl Protector {
999    /// The spelling this is asked for by, which is the whole flag rather than a part of one,
1000    /// because these are four flags and not one flag with an argument.
1001    pub const fn as_str(self) -> &'static str {
1002        match self {
1003            Protector::None => "-fno-stack-protector",
1004            Protector::Buffers => "-fstack-protector",
1005            Protector::Strong => "-fstack-protector-strong",
1006            Protector::All => "-fstack-protector-all",
1007        }
1008    }
1009}
1010
1011impl fmt::Display for Protector {
1012    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1013        f.write_str(self.as_str())
1014    }
1015}
1016
1017/// Which control flow transfers are checked, which is what `-fcf-protection=` asks.
1018///
1019/// Two mechanisms and one flag, because the hardware turns them on together and a program built
1020/// for one and not the other is a program with a hole in whichever half was left out. The forward
1021/// edge is an indirect call or jump, and it is checked by a landing pad at every address one is
1022/// allowed to arrive at, so a corrupted function pointer reaches somewhere somebody meant rather
1023/// than any byte of the program. The backward edge is a return, and it is checked against a second
1024/// copy of the return address the program cannot write to, which needs no instructions at all: the
1025/// machine keeps the copy and the loader turns it on.
1026///
1027/// Which is why the marker matters as much as the code. An object says in a note which halves it
1028/// was built for, the linker takes the intersection over every input, and the loader turns on what
1029/// survives. One object built without the note is enough to turn the whole program's protection
1030/// off, so the note goes in even for a mode that changes no instruction.
1031#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1032pub enum Control {
1033    /// `-fcf-protection=none` and `-fno-cf-protection`, and what a command line that says nothing
1034    /// gets. gcc's own default is the same on the targets this compiler has a back end for.
1035    #[default]
1036    None,
1037    /// `-fcf-protection=branch`. The forward edge alone: a landing pad at every function, and a
1038    /// note that asks for the check on indirect transfers and not on returns.
1039    Branch,
1040    /// `-fcf-protection=return`. The backward edge alone, which is the note and nothing else,
1041    /// since the copy of the return address is the machine's own and no instruction maintains it.
1042    Return,
1043    /// `-fcf-protection=full`, and what the bare `-fcf-protection` means. Both halves.
1044    Full,
1045    /// `-fcf-protection=check`. Asks that the compilation be checked for compatibility with the
1046    /// mode rather than built in it, so nothing is instrumented and no note is written, which is
1047    /// exactly what gcc emits for it.
1048    Check,
1049}
1050
1051impl Control {
1052    /// Whether a landing pad goes at the top of every function.
1053    #[must_use]
1054    pub const fn branch(self) -> bool {
1055        matches!(self, Control::Branch | Control::Full)
1056    }
1057
1058    /// Whether returns are asked to be checked against the machine's own copy.
1059    #[must_use]
1060    pub const fn ret(self) -> bool {
1061        matches!(self, Control::Return | Control::Full)
1062    }
1063
1064    /// Whether anything at all is asked for, which is what decides whether the file says what it
1065    /// was built for.
1066    ///
1067    /// False for the two modes that build nothing. [`Control::None`] asks for nothing and
1068    /// [`Control::Check`] asks that the compilation be looked at rather than changed, and gcc
1069    /// writes no note for either.
1070    #[must_use]
1071    pub const fn any(self) -> bool {
1072        self.branch() || self.ret()
1073    }
1074
1075    /// What the argument was spelled as, which is the part after the equals sign.
1076    pub const fn as_str(self) -> &'static str {
1077        match self {
1078            Control::None => "none",
1079            Control::Branch => "branch",
1080            Control::Return => "return",
1081            Control::Full => "full",
1082            Control::Check => "check",
1083        }
1084    }
1085}
1086
1087impl fmt::Display for Control {
1088    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1089        f.write_str(self.as_str())
1090    }
1091}
1092
1093impl FromStr for Control {
1094    type Err = ();
1095
1096    /// Parses the part after `-fcf-protection=`.
1097    fn from_str(s: &str) -> Result<Self, ()> {
1098        Ok(match s {
1099            "none" => Control::None,
1100            "branch" => Control::Branch,
1101            "return" => Control::Return,
1102            "full" => Control::Full,
1103            "check" => Control::Check,
1104            _ => return Err(()),
1105        })
1106    }
1107}
1108
1109/// Where the call `-pg` puts at the top of every function goes, which `-mfentry` chooses.
1110///
1111/// Two conventions for one job, and the difference is what the hook can see when it runs. See
1112/// [`rucc_target::Trace`] for what each of them is and why a kernel needs the earlier one.
1113///
1114/// A third answer, because a command line that named neither has not asked a question: the
1115/// platform's own answer is the one it gets, and that is a fact about the target rather than about
1116/// the flags, so it is settled where the target is known and not here.
1117#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1118pub enum Hook {
1119    /// Whichever the platform puts first, which is what a command line that said neither gets.
1120    #[default]
1121    Platform,
1122    /// `-mfentry`. In front of the prologue, so the return address is the top thing on the stack
1123    /// and the arguments are still where the call left them.
1124    Early,
1125    /// `-mno-fentry`. Once the frame is taken, so the hook can walk back through the frame pointer,
1126    /// which is why a function that has this one is given a frame pointer whatever else was said.
1127    Late,
1128}
1129
1130impl Hook {
1131    /// That answer as it is written on a command line, which is what `--print-config` reports.
1132    #[must_use]
1133    pub const fn as_str(self) -> &'static str {
1134        match self {
1135            Hook::Platform => "platform",
1136            Hook::Early => "fentry",
1137            Hook::Late => "mcount",
1138        }
1139    }
1140
1141    /// Whether the call goes in front of the prologue, given what the platform puts first.
1142    #[must_use]
1143    pub const fn early(self, fentry: bool) -> bool {
1144        match self {
1145            Hook::Platform => fentry,
1146            Hook::Early => true,
1147            Hook::Late => false,
1148        }
1149    }
1150}
1151
1152impl fmt::Display for Hook {
1153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1154        f.write_str(self.as_str())
1155    }
1156}
1157
1158/// How much room at the top of every function is reserved for somebody to write over later, which
1159/// `-fpatchable-function-entry=` asks for.
1160///
1161/// Room rather than instructions. What goes there is a run of the shortest instruction the machine
1162/// has that does nothing, and the point of them is that they are never executed for long: a tracer
1163/// or a live patcher overwrites them with a jump or a call once the program is running, and what it
1164/// needs from the compiler is a known address, a known number of bytes, and a promise that nothing
1165/// in the function jumps into the middle of them.
1166///
1167/// Two numbers because the room can be on either side of the function's own label, and the two
1168/// sides are not the same thing. Room after the label is room inside the function, which is what a
1169/// patcher that redirects a call into the function wants. Room in front of the label is outside it,
1170/// so what goes there is reached only by something that already knows the address, and a patcher
1171/// that wants somewhere to put a whole instruction it can reach from the first one needs it.
1172///
1173/// The address recorded for the function is the start of the room, which is the front of the part
1174/// before the label when there is one and the front of the part after it when there is not.
1175#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1176pub struct Patchable {
1177    /// How many bytes in total, which is the first number and the one a command line must give.
1178    pub total: u32,
1179    /// How many of them go in front of the function's own label, which is the second number and is
1180    /// zero on a command line that gave one number.
1181    pub before: u32,
1182}
1183
1184impl Patchable {
1185    /// Whether any room at all was asked for, which is what decides whether a function gets a
1186    /// record.
1187    ///
1188    /// `=0` is a command line that asked for none, and gcc accepts it and writes nothing, so the
1189    /// question is about the number rather than about whether the flag was written.
1190    #[must_use]
1191    pub const fn any(self) -> bool {
1192        self.total > 0
1193    }
1194
1195    /// How many bytes go after the function's own label, which is the rest of them.
1196    #[must_use]
1197    pub const fn after(self) -> u32 {
1198        self.total - self.before
1199    }
1200}
1201
1202impl FromStr for Patchable {
1203    type Err = ();
1204
1205    /// Parses the part after `-fpatchable-function-entry=`, which is a number or two of them.
1206    ///
1207    /// A second number larger than the first is refused rather than clamped, because it asks for
1208    /// more room in front of the label than there is room at all and there is no reading of that a
1209    /// caller meant. So is a third, and so is anything that is not a number, which is what gcc does
1210    /// with each of them.
1211    fn from_str(s: &str) -> Result<Self, ()> {
1212        let (total, before) = match s.split_once(',') {
1213            Some((total, before)) => (total, before),
1214            None => (s, "0"),
1215        };
1216        let total: u32 = total.parse().map_err(|_| ())?;
1217        let before: u32 = before.parse().map_err(|_| ())?;
1218        if before > total {
1219            return Err(());
1220        }
1221        Ok(Patchable { total, before })
1222    }
1223}
1224
1225impl fmt::Display for Patchable {
1226    /// Written the way it was asked for, which is one number when the second is zero.
1227    ///
1228    /// Not because the two forms mean different things, they do not, but because that is the form
1229    /// a command line reaching for this feature writes and reading back what was written is what
1230    /// `--print-config` is for.
1231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232        match self.before {
1233            0 => write!(f, "{}", self.total),
1234            before => write!(f, "{},{before}", self.total),
1235        }
1236    }
1237}
1238
1239/// Which of the two position independent questions the output is answering.
1240///
1241/// Everything this compiler writes is position independent, so this is not about whether there are
1242/// absolute addresses in the text. It is about whether the link that reads the object is one that
1243/// puts every name in the same program. An executable is such a link and a shared library is not,
1244/// and the difference decides how a name is reached: from the instruction pointer where the
1245/// distance is a number the linker has, and out of the global offset table where it is not.
1246///
1247/// The expensive answer is the one that has to be asked for, which is gcc's arrangement and is why
1248/// `-fPIC` is on the compile line of every library and nowhere else. A name is only reached the
1249/// expensive way when it is one another object may define or replace, so `-fPIC -fvisibility=hidden`
1250/// costs no more than an executable does.
1251#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1252pub enum Pic {
1253    /// `-fPIE`, `-fpie` and nothing at all. The link puts every name in one program, so a name this
1254    /// file defines is at a distance from the instruction asking, and a name it declares ends up at
1255    /// one too, because the linker answers a reference to a variable defined in a library by making
1256    /// room for it here and copying it. That is what a distribution's default build is.
1257    #[default]
1258    Executable,
1259    /// `-fPIC` and `-fpic`. The output may end up in a shared library, where a name the file
1260    /// exports is one something loaded earlier may define too, and where a name defined elsewhere
1261    /// is not copied in. Both are reached through the global offset table.
1262    Library,
1263}
1264
1265impl Pic {
1266    /// The spelling this is asked for by, which is the one gcc's manual leads with.
1267    pub const fn as_str(self) -> &'static str {
1268        match self {
1269            Pic::Executable => "-fPIE",
1270            Pic::Library => "-fPIC",
1271        }
1272    }
1273}
1274
1275impl fmt::Display for Pic {
1276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1277        f.write_str(self.as_str())
1278    }
1279}
1280
1281/// What the compiler should produce.
1282///
1283/// The intermediate forms are not a debugging convenience bolted on later. Every one of them
1284/// is a documented textual form that round-trips, which is what makes the per-stage testing
1285/// in `spec/15-testing.md` section 15.2 possible.
1286#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1287// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
1288// match that needs to change, in this workspace and in anyone else's code. That is
1289// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
1290// target is a data change: the compiler tells you every place the data is read.
1291pub enum EmitKind {
1292    /// A linked executable. The default.
1293    #[default]
1294    Executable,
1295    /// An object file, `-c`.
1296    Object,
1297    /// A static library holding the objects of every input, `--emit=archive`.
1298    ///
1299    /// Not a GCC mode, because GCC has `ar` beside it and we have said we ship a toolchain rather
1300    /// than half of one. What needs it first is `cargo xtask builtins`, which has to turn a
1301    /// directory of C files into the `librucc_builtins.a` a cross link looks for, for a target
1302    /// whose machine may have no `ar` that knows the format.
1303    ///
1304    /// It is a mode of the compiler rather than a second program because of the symbol index. A
1305    /// static link resolves through it, so writing one means knowing what each member defines, and
1306    /// the compiler has just finished compiling them. An `ar` would have to read the objects back
1307    /// to find out the same thing.
1308    Archive,
1309    /// Assembly text, `-S`.
1310    Asm,
1311    /// Preprocessed source, `-E`.
1312    Preprocessed,
1313    /// The typed AST, `--emit=tast`.
1314    Tast,
1315    /// The IR, `--emit=ir`.
1316    Ir,
1317    /// The machine IR after register allocation, `--emit=mir-final`.
1318    MirFinal,
1319    /// The safety summary, `--emit=safety-summary`.
1320    ///
1321    /// Not an intermediate form of the program the way the three above are. It is the answer to
1322    /// "what does this build's guarantee actually rest on", which
1323    /// `spec/safe-memory/07-check-elimination.md` section 7.8 asks for and
1324    /// `spec/safe-memory/10-boundaries.md` section 10.2 says why.
1325    SafetySummary,
1326    /// How the bytes of the translation unit's records fall into granules,
1327    /// `--emit=type-granules`.
1328    ///
1329    /// Not an intermediate form either. It is the measurement
1330    /// `spec/safe-memory/17-open-questions.md` question 6 asks for, which decides whether the
1331    /// type plane fits inside Tier D's memory budget, and it needs nothing past the type
1332    /// checker because it is a question about layouts rather than about code.
1333    TypeGranules,
1334    /// Nothing at all, `-fsyntax-only`.
1335    ///
1336    /// The front end runs through the type checker and the diagnostics come out, and then the
1337    /// compile stops. Build systems use it to ask whether a file compiles without paying for code
1338    /// generation: meson runs its `has_header_symbol` and `has_function` probes this way, and
1339    /// editors run it on every save.
1340    SyntaxOnly,
1341}
1342
1343impl EmitKind {
1344    /// The name used by `--emit=` and by `--print-config`.
1345    pub const fn as_str(self) -> &'static str {
1346        match self {
1347            EmitKind::Executable => "exe",
1348            EmitKind::Object => "obj",
1349            EmitKind::Archive => "archive",
1350            EmitKind::Asm => "asm",
1351            EmitKind::Preprocessed => "preprocessed",
1352            EmitKind::Tast => "tast",
1353            EmitKind::Ir => "ir",
1354            EmitKind::MirFinal => "mir-final",
1355            EmitKind::SafetySummary => "safety-summary",
1356            EmitKind::TypeGranules => "type-granules",
1357            EmitKind::SyntaxOnly => "syntax-only",
1358        }
1359    }
1360}
1361
1362impl FromStr for EmitKind {
1363    type Err = ();
1364
1365    fn from_str(s: &str) -> Result<Self, ()> {
1366        Ok(match s {
1367            "exe" => EmitKind::Executable,
1368            "obj" => EmitKind::Object,
1369            "archive" => EmitKind::Archive,
1370            "asm" => EmitKind::Asm,
1371            "preprocessed" => EmitKind::Preprocessed,
1372            "tast" => EmitKind::Tast,
1373            "ir" => EmitKind::Ir,
1374            "mir-final" => EmitKind::MirFinal,
1375            "safety-summary" => EmitKind::SafetySummary,
1376            "type-granules" => EmitKind::TypeGranules,
1377            "syntax-only" => EmitKind::SyntaxOnly,
1378            _ => return Err(()),
1379        })
1380    }
1381}
1382
1383/// Which C the source is written in.
1384///
1385/// The GNU variants are the same language with `__STRICT_ANSI__` left undefined, so the
1386/// dialect and the extension question are two fields rather than ten variants.
1387#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1388pub enum Std {
1389    /// `-std=c89`, and `-ansi`.
1390    C89,
1391    /// `-std=c99`.
1392    C99,
1393    /// `-std=c11`.
1394    C11,
1395    /// `-std=c17`, which is C11 with the defect reports applied.
1396    C17,
1397    /// `-std=c23`. The default, matching current GCC.
1398    #[default]
1399    C23,
1400    /// `-std=c2y`, the draft after C23, which gcc 16 takes and reports as `202500L`. It is C23
1401    /// with whatever the next standard has added so far, and the one addition anything here reads
1402    /// yet is the four unsigned absolute value functions under their plain names.
1403    C2y,
1404}
1405
1406impl Std {
1407    /// What `__STDC_VERSION__` says, which C89 does not define at all.
1408    pub const fn stdc_version(self) -> Option<&'static str> {
1409        match self {
1410            Std::C89 => None,
1411            Std::C99 => Some("199901L"),
1412            Std::C11 => Some("201112L"),
1413            Std::C17 => Some("201710L"),
1414            Std::C23 => Some("202311L"),
1415            Std::C2y => Some("202500L"),
1416        }
1417    }
1418
1419    /// The name in `-std=`.
1420    pub const fn as_str(self) -> &'static str {
1421        match self {
1422            Std::C89 => "c89",
1423            Std::C99 => "c99",
1424            Std::C11 => "c11",
1425            Std::C17 => "c17",
1426            Std::C23 => "c23",
1427            Std::C2y => "c2y",
1428        }
1429    }
1430
1431    /// Whether this dialect has `_Atomic`, `_Thread_local` and the rest of C11.
1432    pub const fn has_c11(self) -> bool {
1433        matches!(self, Std::C11 | Std::C17 | Std::C23 | Std::C2y)
1434    }
1435
1436    /// Reads a `-std=` argument, and says whether the GNU extensions came with it.
1437    ///
1438    /// Every alias GCC takes is here, including the `iso9899` spellings and the year based
1439    /// ones, because a build system that passes `-std=iso9899:1999` is passing what its
1440    /// author tested against and rejecting it helps nobody. An unknown dialect is `None`
1441    /// rather than a guess, since guessing means compiling a different language than the one
1442    /// asked for.
1443    #[must_use]
1444    pub fn from_flag(name: &str) -> Option<(Std, bool)> {
1445        let gnu = name.starts_with("gnu");
1446        let std = match name {
1447            "c89" | "c90" | "gnu89" | "gnu90" | "iso9899:1990" | "iso9899:199409" => Std::C89,
1448            "c99" | "c9x" | "gnu99" | "gnu9x" | "iso9899:1999" | "iso9899:199x" => Std::C99,
1449            "c11" | "c1x" | "gnu11" | "gnu1x" | "iso9899:2011" => Std::C11,
1450            "c17" | "c18" | "gnu17" | "gnu18" | "iso9899:2017" | "iso9899:2018" => Std::C17,
1451            "c23" | "c2x" | "gnu23" | "gnu2x" => Std::C23,
1452            "c2y" | "gnu2y" => Std::C2y,
1453            _ => return None,
1454        };
1455        Some((std, gnu))
1456    }
1457}
1458
1459/// The GCC release the compiler claims to be, as `__GNUC__`, `__GNUC_MINOR__` and
1460/// `__GNUC_PATCHLEVEL__`.
1461///
1462/// Design: `spec/04-driver-and-cli.md` section 4.5, which makes this a knob rather than a
1463/// constant and says to start conservative and raise it as the matrix in `rucc-gnu` fills in.
1464///
1465/// The default is sixteen, which is the release this compiler is written against. glibc gates
1466/// most of what it hands a caller on `__GNUC_PREREQ`, so the claim decides which half of
1467/// `sys/cdefs.h` we get, and a project does the same thing to itself: it asks what compiler this
1468/// is and writes different code depending on the answer. The claim is therefore not a boast, it
1469/// is the sentence that selects which of a program's own branches gets compiled, and claiming an
1470/// old release means compiling the code that release needed rather than the code this compiler
1471/// wants.
1472///
1473/// It stood at seven for a long time, and seven was the right number then. Below seven
1474/// `bits/floatn-common.h` writes `typedef float _Float32;` over a keyword this compiler already
1475/// has and every header that reaches it stops there, so moving from 4.2.1 to seven took Ubuntu
1476/// 24.04's glibc 2.39 from 180 of 214 headers to 202 and took the amalgamated sqlite from four
1477/// errors to none. What kept it at seven after that was that nothing needed more, and claiming a
1478/// version whose promises have not been kept means being handed syntax the compiler cannot parse.
1479///
1480/// What needed more was micropython. Its `py/nlrx64.c` asks for `__GNUC__ >= 8` before it writes
1481/// `__attribute__((naked))`, and at seven it took the gcc 7 path instead and handed this compiler
1482/// an ordinary function ending in a bare `jmp`, which is refused and ought to be. The naked
1483/// function it writes at eight and above compiles here, byte for byte what gcc 16 emits, and had
1484/// compiled for a week without micropython ever reaching it.
1485///
1486/// The measurement that moved it is the real corpus at the rung the claim could break: fifty six
1487/// projects across rungs zero through three, built at `-O2` with the claim at seven and again at
1488/// sixteen, on gcc 16.0.1 and glibc. Fifty of fifty six passed both times, and it was the same
1489/// fifty both times, with the same four not passing for the same four reasons. Nothing regressed
1490/// and nothing started working by accident. Thirteen and sixteen had already been measured
1491/// identical to seven on glibc, on the macOS SDK and on sqlite when seven was chosen, so this
1492/// confirms on real builds what the header sweep said.
1493///
1494/// Sixteen point zero rather than the point release on any particular machine, because
1495/// `__GNUC_PREREQ(16, 1)` is a promise about a specific release and the honest claim is the
1496/// earliest one in the series whose promises this compiler means to keep.
1497#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1498pub struct GnucVersion {
1499    /// `__GNUC__`.
1500    pub major: u32,
1501    /// `__GNUC_MINOR__`.
1502    pub minor: u32,
1503    /// `__GNUC_PATCHLEVEL__`.
1504    pub patch: u32,
1505}
1506
1507impl Default for GnucVersion {
1508    fn default() -> GnucVersion {
1509        GnucVersion { major: 16, minor: 0, patch: 0 }
1510    }
1511}
1512
1513impl FromStr for GnucVersion {
1514    type Err = String;
1515
1516    /// Reads `-fgnuc-version=`, which is `15`, `15.1` or `15.1.0`.
1517    ///
1518    /// The short forms are not a convenience, they are what people write. A missing component
1519    /// is zero, the same way GCC treats a release with no patchlevel.
1520    fn from_str(text: &str) -> Result<GnucVersion, String> {
1521        let mut parts = text.split('.');
1522        let mut next = |what: &str| -> Result<u32, String> {
1523            match parts.next() {
1524                None => Ok(0),
1525                Some(field) => {
1526                    field.parse().map_err(|_| format!("`{text}` has a {what} that is not a number"))
1527                }
1528            }
1529        };
1530        let major = next("major")?;
1531        let minor = next("minor")?;
1532        let patch = next("patchlevel")?;
1533        if parts.next().is_some() {
1534            return Err(format!("`{text}` has more than three components"));
1535        }
1536        Ok(GnucVersion { major, minor, patch })
1537    }
1538}
1539
1540/// What the `-d` family asks to be dumped alongside, or instead of, the preprocessed output.
1541///
1542/// Design: `spec/04-driver-and-cli.md` section 4.4.
1543///
1544/// GCC spells these as letters packed into one flag, so `-dDI` is two of them, and a letter it
1545/// does not know is ignored rather than rejected. That last part is deliberate on GCC's side
1546/// and worth copying: the family is a debugging aid and a build that passes `-dumpbase` should
1547/// not die on the `-d`.
1548#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1549pub struct Dumps {
1550    /// `-dM`. Print the macros that are defined at the end, and nothing else.
1551    pub macros: bool,
1552}
1553
1554impl Dumps {
1555    /// The letters GCC's preprocessor takes after `-d`.
1556    ///
1557    /// `M` is the macros, `D` is the macros in place, `N` is their names only, `I` is the
1558    /// `#include` lines and `U` is the macros as they are used. Only `M` does anything so far.
1559    const LETTERS: &'static str = "MDNIU";
1560
1561    /// Whether `arg` is a flag from this family rather than something else beginning with
1562    /// `-d`.
1563    ///
1564    /// The check is here rather than in the driver so that the set of letters and the set of
1565    /// flags accepted cannot drift apart. It matters because `-dumpversion` also begins with
1566    /// `-d`, and a family that swallowed every such flag would turn a flag we have not written
1567    /// into a dump of nothing.
1568    #[must_use]
1569    pub fn is_family(arg: &str) -> bool {
1570        match arg.strip_prefix("-d") {
1571            Some("") | None => false,
1572            Some(letters) => letters.chars().all(|c| Dumps::LETTERS.contains(c)),
1573        }
1574    }
1575
1576    /// Reads the letters after `-d`, ignoring the ones we do not implement yet.
1577    pub fn add(&mut self, letters: &str) {
1578        for letter in letters.chars() {
1579            if letter == 'M' {
1580                self.macros = true;
1581            }
1582        }
1583    }
1584
1585    /// Whether anything at all was asked for.
1586    #[must_use]
1587    pub const fn any(self) -> bool {
1588        self.macros
1589    }
1590}
1591
1592/// A file `-imacros` or `-include` named, read before the source file.
1593///
1594/// Design: `spec/04-driver-and-cli.md` section 4.4.
1595///
1596/// The flag a build reaches for when a whole tree has to see a definition that is not in any of
1597/// its files. The kernel builds every object with `-include` of its own configuration header, and
1598/// a configure script that has produced a `config.h` gets it into a third party source tree the
1599/// same way, without a patch.
1600#[derive(Debug, Clone, PartialEq, Eq)]
1601pub struct Preinclude {
1602    /// The name as it was written, which is looked for the way a quoted include is looked for.
1603    pub name: String,
1604    /// Whether only the definitions it makes are wanted, which is what `-imacros` asks for.
1605    ///
1606    /// The text of an `-imacros` file is read and thrown away, so a header full of declarations
1607    /// contributes its macros and nothing else. That is what makes it usable on a file that has
1608    /// already been included by the source: the definitions arrive early and the declarations do
1609    /// not arrive twice.
1610    pub macros_only: bool,
1611}
1612
1613/// What the `-M` family asks for, which is a make rule saying what a source file was built from.
1614///
1615/// Design: `spec/04-driver-and-cli.md` section 4.4.
1616///
1617/// This is a compiler flag rather than a separate tool because the answer is the set of files the
1618/// preprocessor opened, and nothing outside the preprocessor knows what that was. A build system
1619/// that generates its own makefiles asks for it on every compilation, which is why section 4.4
1620/// calls the family required rather than convenient.
1621#[derive(Debug, Clone, PartialEq, Eq)]
1622pub struct Deps {
1623    /// Whether a rule is produced at all, which is any of `-M`, `-MM`, `-MD` and `-MMD`.
1624    pub emit: bool,
1625    /// Whether the rule is produced instead of compiling, which is `-M` and `-MM` and not the
1626    /// two that end in `D`.
1627    ///
1628    /// The split is GCC's and it is about who reads the answer. The two that stop after the rule
1629    /// write it to standard output for a person, and the two that do not write it to a file
1630    /// beside the object for `make` to include on the next run.
1631    pub instead_of_compiling: bool,
1632    /// Whether a header found in a system directory is listed, which `-MM` and `-MMD` turn off.
1633    ///
1634    /// A build that lists them is a build that rebuilds the world when the C library is updated,
1635    /// which is either what somebody wanted or the reason they reached for the other spelling.
1636    ///
1637    /// On unless a flag turned it off, and nothing turns it back on. That is GCC's behaviour and
1638    /// not an oversight: `-MM -M` leaves the system headers out, because the flag that asks for
1639    /// fewer of them is read as the answer to a question the other one never asked.
1640    pub system_headers: bool,
1641    /// Where the rule is written, from `-MF`, with `-` meaning standard output.
1642    ///
1643    /// `None` is the default, which is standard output when the rule replaces the compilation and
1644    /// the output file with a `.d` suffix when it does not.
1645    pub file: Option<String>,
1646    /// What the rule's targets are, from `-MT` and `-MQ`, in the order they were given.
1647    ///
1648    /// Already escaped, because that is the whole of the difference between the two flags: `-MQ`
1649    /// escapes what it is given and `-MT` writes it through untouched. Empty means the target is
1650    /// worked out from the output file, which is what a build that passes neither expects.
1651    pub targets: Vec<String>,
1652    /// Whether every prerequisite except the source gets a target of its own with no recipe,
1653    /// from `-MP`.
1654    ///
1655    /// This is what stops `make` failing outright when a header is deleted. Without it the old
1656    /// rule names a file that is gone and no rule makes it, and the build stops on a header that
1657    /// nothing needs any more.
1658    pub phony: bool,
1659}
1660
1661impl Default for Deps {
1662    fn default() -> Deps {
1663        Deps {
1664            emit: false,
1665            instead_of_compiling: false,
1666            system_headers: true,
1667            file: None,
1668            targets: Vec::new(),
1669            phony: false,
1670        }
1671    }
1672}
1673
1674/// Whether `-save-temps` was given and where it puts the files it keeps.
1675///
1676/// Design: `spec/04-driver-and-cli.md` section 4.10.
1677///
1678/// The flag is how a build gets at the preprocessed source of the file that failed without running
1679/// the compiler a second time under different flags, which is the one way to be sure the text being
1680/// read is the text that was compiled. A bug report against a compiler is usually a preprocessed
1681/// file and nothing else, and this is where that file comes from.
1682#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1683pub enum SaveTemps {
1684    /// Not asked for, and nothing is kept.
1685    #[default]
1686    No,
1687    /// Beside the file the compilation produced, which is `-save-temps=obj`.
1688    ///
1689    /// This is what the bare `-save-temps` does as well. GCC's manual says the bare spelling is
1690    /// `-save-temps=cwd`, and gcc 16 does not do that: `-save-temps -c a.c -o out/a.o` leaves
1691    /// `out/a.i` and `out/a.s` rather than `a.i` and `a.s`. The measurement is what is followed
1692    /// here, because a build that reads the manual and a build that reads the compiler both end up
1693    /// looking for the files where the compiler put them.
1694    Object,
1695    /// In the working directory, which is `-save-temps=cwd`.
1696    Cwd,
1697}
1698
1699impl SaveTemps {
1700    /// Whether anything is kept at all.
1701    #[must_use]
1702    pub const fn wanted(self) -> bool {
1703        !matches!(self, SaveTemps::No)
1704    }
1705}
1706
1707impl FromStr for SaveTemps {
1708    type Err = String;
1709
1710    /// Reads what came after the `=`, which is the only part that varies.
1711    ///
1712    /// # Errors
1713    ///
1714    /// Returns the offending word. GCC treats an unknown one as fatal rather than ignoring it,
1715    /// which is right: a misspelled keyword here means the files a person went looking for are not
1716    /// written and nothing said so.
1717    fn from_str(s: &str) -> Result<SaveTemps, String> {
1718        match s {
1719            "obj" => Ok(SaveTemps::Object),
1720            "cwd" => Ok(SaveTemps::Cwd),
1721            _ => Err(format!("`{s}` is not a -save-temps option; accepted: cwd, obj")),
1722        }
1723    }
1724}
1725
1726/// The members of `-ffast-math` that are licences about what an arithmetic may answer, one field
1727/// each, so that a build writing `-ffast-math -fno-finite-math-only` gets what gcc gives it.
1728///
1729/// Nothing here folds floating point arithmetic in a function body at any level, so none of these
1730/// changes the code this compiler writes. What each one does change is the predefined set: gcc
1731/// names each licence it was given with a macro of its own, `<math.h>` and a numerics library read
1732/// those, and a header that configured itself for a licence the other objects were built without is
1733/// a program answering two ways. `-ftrapping-math` is the sixth member and lives in
1734/// [`Options::trapping_math`], because it was taken before the rest and it is the one that does
1735/// change an answer here.
1736#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1737pub struct Math {
1738    /// Whether a function in the maths library is taken to set `errno`, from `-fmath-errno`. On,
1739    /// which is gcc's default on a target whose library does it.
1740    pub errno: bool,
1741    /// Whether the program promises there are no NaNs and no infinities, from
1742    /// `-ffinite-math-only`.
1743    pub finite_only: bool,
1744    /// Whether the sign of a zero is kept, from `-fsigned-zeros`. On by default.
1745    pub signed_zeros: bool,
1746    /// Whether a division may become a multiplication by the reciprocal, from
1747    /// `-freciprocal-math`.
1748    pub reciprocal: bool,
1749    /// Whether an addition may be regrouped, from `-fassociative-math`. gcc drops this with a
1750    /// warning unless signed zeros and trapping are both off, so what counts is
1751    /// [`Math::associative`] rather than the field.
1752    pub associative: bool,
1753    /// `-funsafe-math-optimizations`, which is its own flag as well as the three it turns on.
1754    pub unsafe_math: bool,
1755}
1756
1757impl Default for Math {
1758    fn default() -> Math {
1759        Math {
1760            errno: true,
1761            finite_only: false,
1762            signed_zeros: true,
1763            reciprocal: false,
1764            associative: false,
1765            unsafe_math: false,
1766        }
1767    }
1768}
1769
1770impl Math {
1771    /// `-funsafe-math-optimizations` and its negative, which set or clear the members gcc's
1772    /// `set_unsafe_math_optimizations_flags` does. Trapping is one of them, so it is handed back
1773    /// for the caller to store where it lives.
1774    pub fn set_unsafe(&mut self, on: bool) -> bool {
1775        self.unsafe_math = on;
1776        self.associative = on;
1777        self.reciprocal = on;
1778        self.signed_zeros = !on;
1779        !on
1780    }
1781
1782    /// `-ffast-math` and `-fno-fast-math`, which are `set_fast_math_flags` in gcc: the unsafe
1783    /// group, the maths library's `errno` and the promise about NaNs. The value handed back is
1784    /// trapping, as above.
1785    pub fn set_fast(&mut self, on: bool) -> bool {
1786        self.errno = !on;
1787        self.finite_only = on;
1788        self.set_unsafe(on)
1789    }
1790
1791    /// Whether regrouping survives, which it does only where nothing could tell: a regrouped sum
1792    /// can move a zero's sign and can raise an exception the original order did not.
1793    #[must_use]
1794    pub fn associative(&self, trapping: bool) -> bool {
1795        self.associative && !self.signed_zeros && !trapping
1796    }
1797
1798    /// Whether every member is in the permissive position, which is what `__FAST_MATH__` says.
1799    /// Written out rather than remembered from the flag, because `-ffast-math -ftrapping-math` is
1800    /// not fast math and gcc does not define the macro for it.
1801    #[must_use]
1802    pub fn fast(&self, trapping: bool) -> bool {
1803        !trapping && self.unsafe_math && self.finite_only && !self.signed_zeros && !self.errno
1804    }
1805
1806    /// Whether the arithmetic is still IEC 60559's, which is what `__GCC_IEC_559` answers and what
1807    /// glibc writes `__STDC_IEC_559__` from. Any of these licences is an answer the standard does
1808    /// not give.
1809    #[must_use]
1810    pub fn iec_559(&self, trapping: bool) -> bool {
1811        !(self.unsafe_math
1812            || self.associative(trapping)
1813            || self.reciprocal
1814            || self.finite_only
1815            || !self.signed_zeros)
1816    }
1817}
1818
1819/// Everything a compilation was asked to do.
1820///
1821/// Options are a plain value with no interior mutability, so a caller can build one, clone
1822/// it, tweak one field and run a second compilation, which is exactly what the differential
1823/// testing in `spec/15-testing.md` needs.
1824#[derive(Debug, Clone, PartialEq, Eq)]
1825#[non_exhaustive]
1826pub struct Options {
1827    /// The target to generate code for.
1828    pub target: Triple,
1829    /// The optimisation level.
1830    pub opt_level: OptLevel,
1831    /// How much of the memory safety monitor is on, from `-fsafety=`.
1832    ///
1833    /// Off unless it was asked for. A program built without the flag is compiled by exactly the
1834    /// pipeline it was compiled by before the monitor existed, which is the only way the feature
1835    /// can be developed in the open without every build paying for it.
1836    pub safety: Safety,
1837    /// Whether padding participates in the init plane, from `-fsafety-init=`.
1838    ///
1839    /// Means nothing unless `safety` asked for a tier. The default is the one section 9.3 gives
1840    /// library code, which is that it does not, so a record filled a member at a time is not
1841    /// reported when something later reads it whole.
1842    pub padding: Padding,
1843    /// Whether an access has to stay inside the member it names, from `-fsafety-subobject`.
1844    ///
1845    /// Means nothing unless `safety` asked for a tier. Off by default, which section 9.4 argues
1846    /// for: this is the row most likely to fire on code that is doing what its author meant.
1847    pub subobject: Subobject,
1848    /// Whether the `restrict` contract is checked, from `-fsafety-restrict`.
1849    ///
1850    /// Means nothing unless `safety` asked for a tier. Off by default, which section 9.6 argues
1851    /// for: the cost lands entirely inside the loops `restrict` is written for.
1852    pub promise: Promise,
1853    /// Whether pointer races are watched, from `-fsafety-races=`.
1854    ///
1855    /// Means nothing unless `safety` asked for a tier. Off by default, and [`Races`] says why that
1856    /// one is not a cost argument like the others.
1857    pub races: Races,
1858    /// What to produce.
1859    pub emit: EmitKind,
1860    /// Whether to emit debug information.
1861    pub debug_info: bool,
1862    /// The directory the compiler ran in, which is what `DW_AT_comp_dir` says.
1863    ///
1864    /// A debugger joins it onto every file name in the line table that is relative, and the names
1865    /// in there are the ones the command line gave, so a build invoked as `rucc -g a/b.c` produces
1866    /// nothing a debugger can open without it. It is asked of the process by the driver rather than
1867    /// read here, so that a caller that is not a command line gets to say what it was and so that a
1868    /// test does not depend on where it was run from. [`None`] when the process could not say, which
1869    /// is written out as a single dot.
1870    pub working_dir: Option<String>,
1871    /// How the debug sections are compressed, from `-gz`.
1872    ///
1873    /// Nothing reads this yet, because nothing compresses a debug section yet. There are sections
1874    /// to compress now, so what this is waiting on is the compressor rather than the producer.
1875    pub compress: Compress,
1876    /// What the `-flto` family asked for, which nothing does yet.
1877    pub lto: Lto,
1878    /// What the profile reading half of the `-fprofile` family asked for, which nothing reads yet.
1879    ///
1880    /// Named for the data rather than for the flag, because `profile` next door is already the
1881    /// answer to whether `-pg` asked for a call to a profiler on the way into every function, and
1882    /// the two are different questions about the same word.
1883    pub profile_data: Profile,
1884    /// Whether every function keeps a frame pointer, from `-fno-omit-frame-pointer` and
1885    /// `-fomit-frame-pointer`.
1886    ///
1887    /// `None` is a command line that said neither, and then the level decides: kept at `-O0` and
1888    /// left out above it, which is what gcc does and what leaves the register free for the
1889    /// allocator once there is an allocator worth leaving it to. A profiler that walks the stack
1890    /// by following saved frame pointers needs it on, and so does any code a debugger has to
1891    /// unwind without unwind tables. Read it through `keeps_frame_pointer`.
1892    pub frame_pointer: Option<bool>,
1893    /// Whether the red zone may be used, from `-mno-red-zone` turned around.
1894    ///
1895    /// The 128 bytes below the stack pointer that the System V psABI promises no signal handler
1896    /// will touch, which lets a small leaf function keep its locals without moving the stack
1897    /// pointer at all. A kernel turns this off, because an interrupt taken on the kernel stack
1898    /// makes the promise false, and every kernel build in the wild passes `-mno-red-zone` for
1899    /// exactly that reason. A convention without a red zone ignores this.
1900    pub red_zone: bool,
1901    /// Which extensions of the instruction set the unit is built for, from `-march=` and the `-m`
1902    /// flags that name one, such as `-msse4.2`.
1903    ///
1904    /// What it decides today is the macros, `__SSE4_2__` and the rest, which is how a header or a
1905    /// configure probe finds out, and which functions a function built for less may call without
1906    /// saying so. The code generator emits the baseline whatever this says, so a unit built for
1907    /// more runs anywhere and is slower than it could be rather than wrong. Only x86-64 has any,
1908    /// and every other target's is [`Isa::NONE`]. See `rucc_target::isa`.
1909    pub isa: Isa,
1910    /// Whether the blocks of a function are put in the order their weights say rather than in the
1911    /// order the shape of the graph gives, from `-freorder-blocks` and `-fno-reorder-blocks`.
1912    ///
1913    /// `None` is a command line that said neither, which is nearly every one, and then the level
1914    /// decides: on above `-O0`, which is where gcc turns it on. It is a three way answer rather
1915    /// than a `bool` because `-O2 -fno-reorder-blocks` and `-O0` have to be different things and
1916    /// a `bool` set from the level could not tell them apart.
1917    pub reorder_blocks: Option<bool>,
1918    /// Whether the instructions of a block are put in the order the machine finishes soonest, from
1919    /// `-fschedule-insns2` and `-fno-schedule-insns2`.
1920    ///
1921    /// `None` is a command line that said neither, and then the level decides: on from `-O2`,
1922    /// which is where gcc turns it on. Three way rather than a `bool` for the reason
1923    /// `reorder_blocks` above is.
1924    ///
1925    /// gcc's name, and gcc's `2` in it, which is the one that runs after the registers are handed
1926    /// out. `-fschedule-insns` without it is the pass before allocation, which rucc does not have:
1927    /// `spec/optimizer/38-scheduling-and-layout.md` section 38.6 decides on one scheduler and puts
1928    /// it after allocation, and section 38.8 owes the measurement that would justify a second.
1929    pub schedule_insns: Option<bool>,
1930    /// Whether a call in tail position becomes a jump, from `-foptimize-sibling-calls` and
1931    /// `-fno-optimize-sibling-calls`.
1932    ///
1933    /// `None` is a command line that said neither, and then the level decides: on from `-O2` and at
1934    /// `-Os`, which is where gcc turns it on. Three way rather than a `bool` for the reason
1935    /// `reorder_blocks` above is.
1936    pub sibling_calls: Option<bool>,
1937    /// Whether a hot loop that fits in a 64 byte line is padded so that it does not cross one,
1938    /// when that costs at most 31 bytes, from `-falign-loops` and `-fno-align-loops`.
1939    ///
1940    /// `None` is a command line that said neither, and then it is off at every level. gcc turns its
1941    /// own rule on at `-O2` and `-O3`, and rucc does not, because what it buys is a machine's and
1942    /// not every machine's: tamnd/rucc#1838 measured 18% on a loop on AMD EPYC and nothing on the
1943    /// same loop on an Intel Core, for about half a percent of text. Three way rather than a `bool` for the reason
1944    /// `reorder_blocks` above is, so that the day a level turns it on, `-fno-align-loops` still
1945    /// means something.
1946    pub align_loops: Option<bool>,
1947    /// Whether the target's timing model is believed about the machine's units as well as about
1948    /// its latencies, from `-Zcycle-accurate-model=`.
1949    ///
1950    /// `None` is a command line that said neither, and then the model's own answer decides. gcc
1951    /// spells this `--param=cycle-accurate-model=`, `Init(1)`, and describes it as whether the
1952    /// scheduling description "is mostly a cycle-accurate model of the target processor". No model
1953    /// in this compiler is, every one of them says so, and this is how a person measuring the cost
1954    /// of that can compile the same program both ways.
1955    pub cycle_accurate_model: Option<bool>,
1956    /// Whether the backtracking register allocator decides where the values go rather than the
1957    /// single pass one, from `-Zregalloc=backtracking` and `-Zregalloc=single`.
1958    ///
1959    /// `None` is a command line that said neither, and then the level decides.
1960    pub backtracking: Option<bool>,
1961    /// Whether two things in a frame that are never both wanted may be the same bytes, from
1962    /// `-fstack-reuse=`.
1963    ///
1964    /// `None` is a command line that did not write the flag, and then the level decides: on above
1965    /// `-O0`, off at it, so that a person stepping through unoptimized code sees every local in a
1966    /// place of its own. Three way rather than a `bool` for the reason `reorder_blocks` above is,
1967    /// which is that `-O2 -fstack-reuse=none` and `-O0` have to be different things.
1968    ///
1969    /// gcc's flag takes `all`, `named_vars` or `none`. The first two are the same answer here: what
1970    /// rucc shares is a local whose address provably stays inside the function, which is narrower
1971    /// than either of gcc's and is contained in both.
1972    pub stack_reuse: Option<bool>,
1973    /// Which functions get a stack protector, from the `-fstack-protector` family.
1974    pub protector: Protector,
1975    /// Whether a prologue takes its frame a page at a time, from `-fstack-clash-protection`.
1976    ///
1977    /// An operating system leaves one page unmapped below every stack so that a stack growing
1978    /// into it faults. A function whose frame is larger than that page moves the stack pointer
1979    /// clean over it in one subtraction and can then write below it, into whatever the program
1980    /// mapped next, which is a way of reaching one allocation from another that costs an attacker
1981    /// nothing but a large local array. A prologue that takes the frame a page at a time and
1982    /// writes to each page as it arrives faults on the first one that is not there.
1983    ///
1984    /// Off by default, which is gcc's default. Distributions that build with it build everything
1985    /// with it, because the hole is in whichever function was left out.
1986    pub stack_clash: bool,
1987    /// Which control flow transfers are checked, from `-fcf-protection=`.
1988    ///
1989    /// See [`Control`]. Off by default, which is gcc's default on these targets, and on again in
1990    /// every distribution's global flags for the same reason the stack protector is.
1991    pub control: Control,
1992    /// Whether every function calls a profiler's hook on the way in, from `-pg` and `-p`.
1993    ///
1994    /// A profiler wants a count of which function called which, and the moment a function is
1995    /// entered is the only place a compiler can hand it one. It changes the link as well as the
1996    /// code, since the counts have to be started before `main` and written out after it, and the
1997    /// start file that does that is a different one.
1998    ///
1999    /// A tracer wants the same call for a different reason. The hook is one instruction the kernel
2000    /// can overwrite while the program runs, which is what makes a function traceable without
2001    /// rebuilding it, and it is why Linux is built this way rather than to be profiled.
2002    pub profile: bool,
2003    /// Where that call goes, from `-mfentry` and `-mno-fentry`.
2004    ///
2005    /// See [`Hook`]. Read even on a command line that did not ask for the call, since gcc accepts
2006    /// the flag on its own and does nothing with it.
2007    pub hook: Hook,
2008    /// How much room every function opens with for somebody to write over later, from
2009    /// `-fpatchable-function-entry=`.
2010    ///
2011    /// See [`Patchable`]. A kernel asks for this so that a function can be traced without being
2012    /// rebuilt: the room is a known number of bytes at a known address, and the addresses are
2013    /// collected into a section of their own so that whatever does the patching can find every one
2014    /// of them without reading the symbol table.
2015    pub patchable: Patchable,
2016    /// What happens rather than nothing being defined when arithmetic overflows, from `-fwrapv`,
2017    /// `-fwrapv-pointer`, `-fno-strict-overflow` and `-ftrapv`.
2018    ///
2019    /// See [`Wrapping`]. Nothing wraps and nothing stops by default, which is what C says and what
2020    /// lets the optimizer read a loop counter as a number rather than as a number that may turn
2021    /// round.
2022    pub wrapping: Wrapping,
2023    /// What a plain `char` is, from `-fsigned-char` and `-funsigned-char`, with nothing meaning
2024    /// the answer the target's ABI gives.
2025    ///
2026    /// Plain `char` is a third type either way, distinct from both `signed char` and
2027    /// `unsigned char` in every place a type is compared, and this says which of the two it has
2028    /// the range of. Changing it changes the ABI, so it is a decision about the whole program
2029    /// rather than about one file, and `__CHAR_UNSIGNED__` is defined when the answer is unsigned
2030    /// so that a header can see what was decided.
2031    pub char_signed: Option<bool>,
2032    /// Whether an enumeration nothing wrote an underlying type for is represented in the smallest
2033    /// integer type that holds its enumerators, from `-fshort-enums`.
2034    ///
2035    /// The default is `int` or wider, which is what C says and what every psABI in the table
2036    /// expects. This makes it `char` or wider instead, so `enum { A }` is one byte, and that
2037    /// changes the size and the alignment of anything holding one. It is here because a great deal
2038    /// of embedded C and every ARM EABI object is built with it, and mixing the two answers in one
2039    /// program is a silent disagreement about layout rather than a link error.
2040    pub short_enums: bool,
2041    /// Whether Microsoft's reading of an anonymous member is taken, from `-fms-extensions` and
2042    /// `-fno-ms-extensions`, with the target deciding when neither was written.
2043    ///
2044    /// C takes a `struct` or a `union` member with neither a tag nor a name as anonymous, and
2045    /// Microsoft's rule takes one written with a tag or named through a typedef as well. The
2046    /// default is on for a Windows target and off everywhere else, which is what gcc does: its
2047    /// mingw build has the flag on without being asked and its Linux build has it off. The
2048    /// Windows headers need it, because `<objidl.h>` and the rest close a nameless union with
2049    /// `} DUMMYUNIONNAME;` and the macro expands to nothing.
2050    pub ms_extensions: Option<bool>,
2051    /// Whether an access names the type it goes through, from `-fstrict-aliasing` and
2052    /// `-fno-strict-aliasing`.
2053    ///
2054    /// On, which is gcc's answer at every level above `-O0` and is what C 6.5 paragraph 7 already
2055    /// says. Clearing it makes the front end leave the type off every load and every store, and an
2056    /// access with no type on it is one the alias analysis has no type based reason to separate
2057    /// from any other, which is what the flag asks for.
2058    pub strict_aliasing: bool,
2059    /// How far a multiply and an addition may be fused into one rounding, from `-ffp-contract=`.
2060    ///
2061    /// See [`Contract`]. Most of the floating point flags have nowhere to be kept, because they
2062    /// withdraw licences that nothing here takes in the first place: no arithmetic in a function
2063    /// body is folded at any level, so a flag saying the rounding mode may have changed describes
2064    /// what already happens. This one and [`Options::trapping_math`] are the two that have
2065    /// somewhere to go.
2066    pub fp_contract: Contract,
2067    /// Whether an operation may raise an exception the program then looks at, from
2068    /// `-ftrapping-math` and `-fno-trapping-math`.
2069    ///
2070    /// On, which is gcc's default. What clearing it licenses here is one thing: the conversion of
2071    /// a constant floating value to an integer type it does not fit in. Left to the hardware that
2072    /// conversion is one instruction and the answer is the integer indefinite value, which is what
2073    /// both compilers give by default. gcc folds it under this flag instead, to the nearest end of
2074    /// the integer's range, and the difference is visible because the conversion is undefined
2075    /// behaviour rather than a value, so neither answer is wrong and the one a program was written
2076    /// against is gcc's.
2077    pub trapping_math: bool,
2078    /// The rest of the `-ffast-math` family, from the flag itself and from each member spelled on
2079    /// its own. See [`Math`].
2080    pub math: Math,
2081    /// Whether an exception may unwind through the code this unit produces, from `-fexceptions`
2082    /// and `-fno-exceptions`, and from `-fnon-call-exceptions` when neither of those was written.
2083    ///
2084    /// Off, which is gcc's default for C. What it changes in C is small: `__EXCEPTIONS` is defined,
2085    /// which is what glibc's `pthread_cleanup_push` reads to choose a `cleanup` attribute over its
2086    /// `setjmp` spelling, and a `cleanup` handler is owed a call when an unwind passes through its
2087    /// scope as well as when the scope is left the ordinary way. The tables an unwinder reads to get
2088    /// through a frame at all are [`Options::unwind_tables`] and are there either way.
2089    pub exceptions: bool,
2090    /// Whether an instruction that is not a call may raise an exception, from
2091    /// `-fnon-call-exceptions`. It turns [`Options::exceptions`] on unless `-fno-exceptions` was
2092    /// written, which is gcc's rule, and it is kept apart because it is a second promise about
2093    /// which instructions a handler covers rather than a second way of saying the first one.
2094    pub non_call_exceptions: bool,
2095    /// What a path is rewritten by before it is written into the output, from the
2096    /// `-f*-prefix-map=` family.
2097    ///
2098    /// See [`PrefixMaps`]. This is what makes a build reproducible from a different directory, and
2099    /// it is three lists rather than one because gcc has three flags and a build uses them apart.
2100    pub prefix_map: PrefixMaps,
2101    /// Whether warnings are errors.
2102    pub warnings_are_errors: bool,
2103    /// Whether a warning is raised at all, which is `-w` turned around.
2104    ///
2105    /// A build that passes this has decided it does not want to hear about anything that is not
2106    /// fatal, and the flag is dropped at the one place every diagnostic goes through rather than
2107    /// tested at each site that raises one. `-w` beats `-Werror` where both are given, because a
2108    /// warning that was never raised cannot be promoted.
2109    pub warnings: bool,
2110    /// Whether a warning about something in a header that came with the machine is printed, which
2111    /// is `-Wsystem-headers` and is off the way gcc has it off.
2112    ///
2113    /// The person compiling did not write the file and cannot change it, so a warning about it is
2114    /// noise, and under `-Werror` it is a build that stops on a line nobody in the project typed.
2115    /// It is worth having the flag rather than nothing at all, because somebody porting a header
2116    /// or reading what a new compiler thinks of one does want to hear all of it.
2117    pub system_header_warnings: bool,
2118    /// How many diagnostics to print before giving up. Past a certain point the output is
2119    /// noise from a single earlier mistake, and GCC's default of no limit is not a kindness.
2120    pub error_limit: u32,
2121    /// The dialect, from `-std=`.
2122    pub std: Std,
2123    /// Whether the GNU extensions are on, which is `-std=gnu23` rather than `-std=c23`.
2124    pub gnu_extensions: bool,
2125    /// Whether `-pedantic` was given, which is what turns a use of an extension from silence
2126    /// into a diagnostic. It is not the same knob as the dialect: `-std=c17 -pedantic` warns
2127    /// about a construct that `-std=c17` alone accepts without a word.
2128    pub pedantic: bool,
2129    /// Whether `-fpermissive` was given, which turns the rules gcc 14 promoted from errors back
2130    /// into warnings.
2131    ///
2132    /// Six of them, all about code written before the language settled: a declaration with no
2133    /// type in it, a call to a function nothing declared, a parameter in an old style definition
2134    /// with no type, a pointer made from an integer, a pointer assigned from a pointer to
2135    /// something else, and a `return` whose value disagrees with what was promised. The flag says
2136    /// nothing about any other diagnostic, and it does not say to compile something different: a
2137    /// program it accepts is compiled the way the rule it broke says it means.
2138    pub permissive: bool,
2139    /// Whether the whole unit is under GNU's reading of `inline` rather than C's, which is
2140    /// `-fgnu89-inline`.
2141    ///
2142    /// Under C's reading a definition every file-scope declaration wrote `inline` for and none
2143    /// wrote `extern` for emits nothing, and under GNU's it is the definition alone that decides
2144    /// and `extern inline` is the one that emits nothing. The C89 dialects are under GNU's
2145    /// whatever this says, since that is where the older reading came from, so this is the flag a
2146    /// program written against it reaches for when it is being compiled under a later dialect.
2147    pub gnu89_inline: bool,
2148    /// What a name that nothing in the source said anything about reaches, from `-fvisibility=`.
2149    pub visibility: Visibility,
2150    /// What every function is aligned to unless it asked for more itself, from
2151    /// `-falign-functions` and `-fno-align-functions`.
2152    ///
2153    /// `None` is the target's own answer, which is `rucc_object::FUNC_ALIGN`, and it is what a
2154    /// bare `-falign-functions` asks for as well, since gcc's bare form means the default and the
2155    /// default on this target is the same sixteen bytes. A number is a floor rather than a
2156    /// setting: a function carrying `__attribute__((aligned(N)))` keeps the larger of the two,
2157    /// because the attribute is a statement about that function and this is a preference about
2158    /// the unit.
2159    pub align_functions: Option<u32>,
2160    /// Whether every function calls `__cyg_profile_func_enter` on the way in and
2161    /// `__cyg_profile_func_exit` on the way out, from `-finstrument-functions`.
2162    ///
2163    /// Off unless asked for. A function declared `no_instrument_function` is left alone whatever
2164    /// this says, which is how the two hooks avoid calling themselves.
2165    pub instrument_functions: bool,
2166    /// Whether the object may end up in a shared library, from `-fPIC` and `-fPIE`.
2167    pub pic: Pic,
2168    /// Whether a definition in this unit may be replaced at load time by one in another object,
2169    /// from `-fsemantic-interposition` and `-fno-semantic-interposition`.
2170    ///
2171    /// True is the honest answer and is gcc's default, because that is what an exported name in a
2172    /// shared library means: the dynamic linker takes the first definition it finds in load order,
2173    /// so a function this unit defines and calls may not be the one that runs. Everything the
2174    /// optimizer reads off a body has to stop at a name like that.
2175    ///
2176    /// False is a promise the build makes, and every distribution makes it, because otherwise a
2177    /// library cannot inline its own functions into each other. It is a promise rather than a
2178    /// deduction: nothing checks it, and a program that then interposes one of those names gets a
2179    /// mixture of the two definitions. It says nothing about `-fPIE`, where no name is replaceable
2180    /// to begin with, and it says nothing about how an address is reached, which is the separate
2181    /// question `-fPIC` decides.
2182    pub interposition: bool,
2183    /// Whether a function is described to an unwinder at every instruction, from
2184    /// `-fasynchronous-unwind-tables` and `-fno-asynchronous-unwind-tables`.
2185    ///
2186    /// True is the default, which is gcc's wherever anything reads the table, and the reason is
2187    /// that the programs that read it are not the ones being compiled. C++ exceptions,
2188    /// `backtrace`, a profiler sampling a stack and a crash handler printing one all walk frames
2189    /// belonging to code that knew nothing about them, so a unit that opts out stops a walk that
2190    /// started somewhere else.
2191    ///
2192    /// What `asynchronous` asks for on top of a table is that the answer is right at every
2193    /// instruction and not only where a call is, because a signal can arrive anywhere, including
2194    /// the middle of a prologue. Rows come off the prologue as it is built here, so that is the
2195    /// only kind of table there is to write and the weaker request below is answered with it.
2196    ///
2197    /// False is for a build that knows nothing will ever walk it, which in practice is a kernel or
2198    /// a freestanding image, and what it saves is the section rather than any instruction.
2199    pub async_unwind_tables: bool,
2200    /// Whether a function is described to an unwinder at all, from `-funwind-tables` and
2201    /// `-fno-unwind-tables`.
2202    ///
2203    /// The weaker of the two requests and off by default, because the one above is on and implies
2204    /// it. A table is written when either of them is standing, which is what [`Self::unwinds`]
2205    /// answers and is how gcc resolves a line that asks for a table and against an asynchronous
2206    /// one.
2207    ///
2208    /// Neither of them is about anything but ELF. Mach-O and COFF have their own arrangements and
2209    /// neither is written yet, so on those targets nothing reads these.
2210    pub unwind_tables: bool,
2211    /// Whether each function gets a section of its own, from `-ffunction-sections`.
2212    ///
2213    /// A linker can leave out a section nothing reaches and cannot leave out half of one, so this
2214    /// is what makes `--gc-sections` able to drop a function this file defines and nothing calls.
2215    /// A kernel and an embedded image are both linked that way and are both a good deal larger
2216    /// without it, and the cost is one section header per function.
2217    pub function_sections: bool,
2218    /// Whether each variable gets a section of its own, from `-fdata-sections`.
2219    ///
2220    /// The same bargain for the data, and a separate flag because gcc has two of them: a build
2221    /// that wants one and not the other is a build that measured something. Splitting the data can
2222    /// cost more than it saves, since two variables a loop reads together are no longer certain to
2223    /// land in the same page.
2224    pub data_sections: bool,
2225    /// The GCC release claimed, from `-fgnuc-version=`.
2226    pub gnuc: GnucVersion,
2227    /// Whether there is a standard library, which is `-ffreestanding` turned around.
2228    pub hosted: bool,
2229    /// Whether a call to a C library function written under its own plain name may be taken to
2230    /// mean that function, which is `-fno-builtin` turned around.
2231    ///
2232    /// The names are reserved, so `llabs` is the library's `llabs` and the compiler is allowed to
2233    /// know what it does. A program that means something else by one of them is the reason the
2234    /// flag exists, and `-ffreestanding` turns it off as well, because a freestanding program has
2235    /// no C library for the name to be the name of. The `__builtin_` spellings are not affected by
2236    /// either, since the prefix is the program saying which function it means.
2237    pub builtins: bool,
2238    /// The names `-fno-builtin-<name>` took away one at a time, without the prefix.
2239    ///
2240    /// A build that means its own `memcpy` and the library's everything else writes this rather
2241    /// than the whole flag, which is what the kernel does for a handful of names.
2242    pub no_builtin: Vec<String>,
2243    /// The glibc release the headers on the search path are, as the minor number alone.
2244    ///
2245    /// `Some` means two things together: this is a glibc target, and step 3 of
2246    /// `spec/cross-compile/08-sysroots.md` section 8.5 resolved to the tree we bundle. Then the
2247    /// compiler defines `__GLIBC_MINOR__`, because one tree serves every version and the version is
2248    /// the part of it the target supplies. `__GLIBC__` is not ours to define either way, since it is
2249    /// in the tree and a real `features.h` defines it too.
2250    ///
2251    /// `None` is every other case, and the cases matter more than the value. A host glibc's
2252    /// `features.h` defines the macro itself, and a tree the user named has a `features.h` of its
2253    /// own, so defining it as well would be two definitions with different values, which is a
2254    /// warning on every compilation of every file. A musl or mingw target has no such macro at all.
2255    pub glibc_minor: Option<u32>,
2256    /// The deployment target on an Apple platform, the oldest release the program is promised
2257    /// to run on. It comes from `-mmacosx-version-min=` or from the tuple, as in
2258    /// `aarch64-macos.13`, and `None` leaves the platform's default in place.
2259    pub os_version: Option<rucc_tuple::Version>,
2260    /// `-D` in command line order. `FOO` means `FOO=1`, as GCC has it.
2261    pub defines: Vec<String>,
2262    /// `-U` in command line order, applied after the defines because `-U` wins.
2263    pub undefines: Vec<String>,
2264    /// Where a header is looked for.
2265    pub search: SearchPath,
2266    /// What `-imacros` and `-include` named, in command line order.
2267    pub preincludes: Vec<Preinclude>,
2268    /// Whether `-E` writes line markers, which `-P` turns off.
2269    pub line_markers: bool,
2270    /// What the `-d` family asks for.
2271    pub dumps: Dumps,
2272    /// What the `-M` family asks for.
2273    pub deps: Deps,
2274    /// Whether the intermediate files are kept, from `-save-temps`.
2275    pub save_temps: SaveTemps,
2276    /// Whether each compiled file gets a `.su` beside its output saying how much stack each of its
2277    /// functions takes, from `-fstack-usage`.
2278    ///
2279    /// gcc's flag and gcc's file, one line per function. What the number counts is on
2280    /// `rucc_codegen::frame::Frame::usage`. It is counted the way gcc counts, and it is the size of
2281    /// rucc's frame rather than gcc's, which is the point: the two files side by side say which
2282    /// functions one compiler gives more stack than the other does.
2283    pub stack_usage: bool,
2284    /// What the files kept beside an output are named after, from `-dumpbase`, in place of the
2285    /// name the output or the input would have given them.
2286    pub dump_base: Option<String>,
2287    /// The extension `-dumpbase-ext` says to take off the end of [`Options::dump_base`].
2288    pub dump_base_ext: Option<String>,
2289    /// What goes in front of the name of every file kept beside an output, from `-dumpdir`. A
2290    /// directory when it ends in a slash and the start of a name otherwise, which is gcc's rule.
2291    pub dump_dir: Option<String>,
2292    /// Whether each step says how long it took, from `-time`.
2293    pub time: bool,
2294    /// What `-f<pass>` and `-fno-<pass>` said about an optimizer pass, in the order the command
2295    /// line said it, so that the last mention of a pass is the one that decides.
2296    ///
2297    /// The pipeline the level chose is the starting point and this is what is added to and taken
2298    /// away from it. The names are checked against the pass list while the arguments are parsed,
2299    /// so anything in here is a pass the compiler has.
2300    pub passes: Vec<(String, bool)>,
2301    /// What `-fpass-fuel=<pass>=<n>` limited a pass to, by pass name.
2302    ///
2303    /// A pass with an entry here performs exactly that many transformations and then stops
2304    /// transforming, which is what bisects a miscompilation to one rewrite. See section 9.10 of
2305    /// `spec/09-optimizer.md`.
2306    pub pass_fuel: Vec<(String, u32)>,
2307    /// What `-fpass-fuel-global=<n>` limited the whole pipeline to, across every pass.
2308    ///
2309    /// The outer of the two searches in section 4.5 of `spec/optimizer/04-pass-manager.md`.
2310    /// Halving this says which pass holds the bad rewrite, and halving `-fpass-fuel` for that
2311    /// pass says which rewrite it is. Where both are given, a pass is stopped by whichever of
2312    /// the two is tighter.
2313    pub pass_fuel_global: Option<u32>,
2314    /// Where `-frucc-trace=<file>` asked for one line of JSON per compiled file saying how long
2315    /// each phase and each optimizer pass took. Appended to, never truncated, so that every
2316    /// compiler in a parallel build can share one file.
2317    pub trace: Option<String>,
2318    /// What `-fdisable-<pass>[=<range>]` and `-fenable-<pass>[=<range>]` said, in the order the
2319    /// command line said it, with `true` for the enabling half.
2320    ///
2321    /// A rule covers the functions it names and nothing else, and the last rule that covers a
2322    /// function is the one that decides for it, so the order has to survive. This is the second
2323    /// half of the bisection interface in section 41.6 of `spec/optimizer/41-correctness.md`:
2324    /// `-fpass-fuel` finds the rewrite and this finds the function. The pass names are checked
2325    /// against the pass list while the arguments are parsed.
2326    pub pass_gates: Vec<(bool, String)>,
2327    /// What `-fdump-ir=` asked to see, as it was written, which is `all`, `before-<pass>` or
2328    /// `after-<pass>`.
2329    pub dump_ir: Vec<String>,
2330    /// What `-fopt-info` asked to hear about, as the keywords were written, with the leading
2331    /// hyphen taken off, so a bare `-fopt-info` is the empty string in here.
2332    ///
2333    /// The keywords are `optimized`, `missed`, `note` and `all`, and two flags add up rather than
2334    /// the second replacing the first. Checked while the arguments are parsed, so anything in
2335    /// here is a spelling the optimizer understands. See section 42.2 of
2336    /// `spec/optimizer/42-measurement.md` for why `missed` is the one that earns the feature.
2337    pub opt_info: Vec<String>,
2338    /// Where `-fopt-info=<file>` sends the remarks, or `None` for standard error.
2339    ///
2340    /// One file for the whole run rather than one per input, the way GCC does it, and the last
2341    /// one on the command line is the one that decides. A harness that wants the remarks kept
2342    /// away from the diagnostics gives a file, which is what the corpus in `tamnd/rucc-corpus`
2343    /// does with GCC so that a rejection can still be matched against the diagnostic stream.
2344    pub opt_info_file: Option<String>,
2345    /// Whether the IR verifier runs after every pass that changed anything.
2346    ///
2347    /// On in a debug build without being asked, since that is where a broken pass should be
2348    /// caught. `-Zverify-each` turns it on in a release build, which is what CI wants.
2349    pub verify_each: bool,
2350    /// Where `-Zrule-coverage=FILE` writes which lowering rules fired, if it was given.
2351    ///
2352    /// A measurement rather than a thing a build asks for, which is why it is spelled with a `-Z`
2353    /// the way an unstable option is everywhere else: it is here for the harness in
2354    /// `tamnd/rucc-compat` to union over a corpus and report, and nothing about the code that comes
2355    /// out changes when it is on. One file per run of the compiler, holding the whole rule set with
2356    /// the rules this run reached marked, whatever the run compiled and however many files it was.
2357    pub rule_coverage: Option<String>,
2358    /// Where `-Zregister-pressure=FILE` writes what the allocator had to put on the stack.
2359    ///
2360    /// A measurement and spelled with a `-Z` for the same reason as the one above: nothing about
2361    /// the code that comes out changes when it is on. One file per run of the compiler, one line
2362    /// per function, holding how many values went to the stack and how many stores and reloads
2363    /// that cost. What reads it is `cargo xtask pressure`, which compiles the benchmarks in
2364    /// `bench/safety` with the monitor off and on and reports the difference, since
2365    /// `spec/safe-memory/13-performance.md` section 13.1 asks for that number and section 5.2.1
2366    /// says why: a capability in flight is four words, and if materializing one spills something
2367    /// else in a hot loop then check elimination cannot save it.
2368    pub register_pressure: Option<String>,
2369    /// Where `-Zlowering=FILE` writes what the pre-selection lowering group did.
2370    ///
2371    /// A measurement and spelled with a `-Z` for the same reason as the two above: nothing about
2372    /// the code that comes out changes when it is on. One file per run of the compiler, one block
2373    /// per function, holding every member of the group in the order it ran and what each of them
2374    /// found and left behind. What it is read for is a function that came out of the back end in a
2375    /// shape somebody did not expect, since the block says which lowering changed it, and what it
2376    /// is read for after that is a construct the selector refused by name, since the block says
2377    /// whether the step that answers for that construct was offered it and walked away.
2378    pub lowering_dump: Option<String>,
2379    /// The shape `-Zswitch=` forces on every `switch`, spelled as it was given: `table`, `tree` or
2380    /// `walk`. A measurement, like the two above, but one that changes the code: section 24.7
2381    /// asks what each shape costs on a hot `switch`, and building it that way is how to find out.
2382    pub switch_shape: Option<String>,
2383}
2384
2385impl Options {
2386    /// Default options for `target`.
2387    pub fn new(target: Triple) -> Self {
2388        Self {
2389            target,
2390            opt_level: OptLevel::default(),
2391            safety: Safety::default(),
2392            padding: Padding::default(),
2393            subobject: Subobject::default(),
2394            promise: Promise::default(),
2395            races: Races::default(),
2396            emit: EmitKind::default(),
2397            debug_info: false,
2398            working_dir: None,
2399            compress: Compress::None,
2400            lto: Lto::default(),
2401            profile_data: Profile::default(),
2402            frame_pointer: None,
2403            red_zone: true,
2404            isa: match target.arch {
2405                Arch::X86_64 => Isa::baseline(),
2406                Arch::Aarch64 | Arch::Riscv64 => Isa::NONE,
2407            },
2408            reorder_blocks: None,
2409            schedule_insns: None,
2410            sibling_calls: None,
2411            align_loops: None,
2412            cycle_accurate_model: None,
2413            backtracking: None,
2414            stack_reuse: None,
2415            protector: Protector::default(),
2416            stack_clash: false,
2417            control: Control::default(),
2418            profile: false,
2419            hook: Hook::default(),
2420            patchable: Patchable::default(),
2421            wrapping: Wrapping::NONE,
2422            char_signed: None,
2423            short_enums: false,
2424            ms_extensions: None,
2425            strict_aliasing: true,
2426            fp_contract: Contract::Off,
2427            trapping_math: true,
2428            math: Math::default(),
2429            exceptions: false,
2430            non_call_exceptions: false,
2431            prefix_map: PrefixMaps::default(),
2432            warnings_are_errors: false,
2433            warnings: true,
2434            system_header_warnings: false,
2435            error_limit: 20,
2436            std: Std::default(),
2437            gnu_extensions: true,
2438            pedantic: false,
2439            permissive: false,
2440            gnu89_inline: false,
2441            visibility: Visibility::default(),
2442            align_functions: None,
2443            instrument_functions: false,
2444            pic: Pic::default(),
2445            interposition: true,
2446            async_unwind_tables: true,
2447            unwind_tables: false,
2448            function_sections: false,
2449            data_sections: false,
2450            gnuc: GnucVersion::default(),
2451            hosted: true,
2452            builtins: true,
2453            no_builtin: Vec::new(),
2454            glibc_minor: None,
2455            os_version: None,
2456            defines: Vec::new(),
2457            undefines: Vec::new(),
2458            search: SearchPath::new(),
2459            preincludes: Vec::new(),
2460            line_markers: true,
2461            dumps: Dumps::default(),
2462            deps: Deps::default(),
2463            save_temps: SaveTemps::default(),
2464            stack_usage: false,
2465            dump_base: None,
2466            dump_base_ext: None,
2467            dump_dir: None,
2468            time: false,
2469            passes: Vec::new(),
2470            pass_fuel: Vec::new(),
2471            pass_fuel_global: None,
2472            trace: None,
2473            pass_gates: Vec::new(),
2474            dump_ir: Vec::new(),
2475            opt_info: Vec::new(),
2476            opt_info_file: None,
2477            verify_each: cfg!(debug_assertions),
2478            rule_coverage: None,
2479            register_pressure: None,
2480            lowering_dump: None,
2481            switch_shape: None,
2482        }
2483    }
2484
2485    /// Whether a function in this unit is described to an unwinder.
2486    ///
2487    /// Either request is answered with the same table, so what decides is whether either of them
2488    /// is standing. Asked here rather than worked out at the two places that write a table, since
2489    /// those two writing different answers for one function is what `spec/11-asm-objects-debug.md`
2490    /// section 11.1 says must not be possible. `-fexceptions` asks for one too, as it does of gcc,
2491    /// since a landing pad nothing can find is a cleanup that never runs.
2492    #[must_use]
2493    pub const fn unwinds(&self) -> bool {
2494        self.async_unwind_tables || self.unwind_tables || self.exceptions
2495    }
2496
2497    /// Whether every function keeps a frame pointer: what the command line said, or what the
2498    /// level says when it said nothing.
2499    #[must_use]
2500    pub const fn keeps_frame_pointer(&self) -> bool {
2501        match self.frame_pointer {
2502            Some(kept) => kept,
2503            None => self.opt_level.frame_pointer(),
2504        }
2505    }
2506}
2507
2508/// One compilation.
2509///
2510/// Holds the options, the string interner and the diagnostics raised so far. Passing a
2511/// `&mut Session` is how a stage reports a problem, and the return value of a stage says
2512/// what it produced, never whether it succeeded: that question is answered by
2513/// [`Session::has_errors`].
2514#[derive(Debug)]
2515pub struct Session {
2516    /// What this compilation was asked to do.
2517    pub opts: Options,
2518    /// Everything known about the target.
2519    pub target: TargetInfo,
2520    /// The one interner for the compilation.
2521    pub interner: Interner,
2522    /// Every file read during the compilation, and the flat coordinate space their spans
2523    /// live in.
2524    ///
2525    /// This is on the session rather than passed around separately because a span is only
2526    /// meaningful against the map that issued it, and one map per compilation is the rule
2527    /// that makes that true by construction.
2528    pub sources: SourceMap,
2529    diagnostics: Vec<Diagnostic>,
2530    error_count: u32,
2531    warning_count: u32,
2532}
2533
2534impl Session {
2535    /// A session for `opts`.
2536    ///
2537    /// The command line's answer about plain `char` is put into the target here rather than
2538    /// carried beside it, because every place that asks what a `char` is asks the target, and two
2539    /// answers to one question is how a front end ends up disagreeing with its own back end.
2540    pub fn new(opts: Options) -> Self {
2541        let mut target = TargetInfo::new(opts.target);
2542        if let Some(version) = opts.os_version {
2543            target.tuple = target.tuple.with_os_version(version);
2544        }
2545        if let Some(signed) = opts.char_signed {
2546            target.char_is_signed = signed;
2547        }
2548        Self {
2549            opts,
2550            target,
2551            interner: Interner::with_capacity(1024),
2552            sources: SourceMap::new(),
2553            diagnostics: Vec::new(),
2554            error_count: 0,
2555            warning_count: 0,
2556        }
2557    }
2558
2559    /// Whether Microsoft's reading of an anonymous member is taken.
2560    ///
2561    /// The command line answers where it said anything, and the target answers otherwise: gcc's
2562    /// mingw build has the flag on without being asked for it and its Linux build has it off, and
2563    /// a header written for one of the two is read by whichever compiler the platform ships.
2564    #[must_use]
2565    pub fn ms_extensions(&self) -> bool {
2566        self.opts.ms_extensions.unwrap_or(self.opts.target.os == Os::Windows)
2567    }
2568
2569    /// Records a diagnostic.
2570    ///
2571    /// Under `-Werror` a warning is promoted here, once, rather than at every site that
2572    /// raises one, and under `-w` it is dropped here for the same reason. A warning that `-w`
2573    /// dropped is not counted, so `-w -Werror` compiles rather than failing on a warning
2574    /// nobody was going to see. A warning about a line in a header that came with the machine is
2575    /// dropped here too, which is what `-Wsystem-headers` turns off, and dropping it before the
2576    /// promotion is what keeps `-Werror` from stopping a build on somebody else's header.
2577    pub fn emit(&mut self, mut diag: Diagnostic) {
2578        if rucc_diag::dropped(
2579            &diag,
2580            &self.sources,
2581            self.opts.warnings,
2582            self.opts.system_header_warnings,
2583        ) {
2584            return;
2585        }
2586        if self.opts.warnings_are_errors && diag.severity == Severity::Warning {
2587            diag.severity = Severity::Error;
2588        }
2589        match diag.severity {
2590            Severity::Error | Severity::Ice => self.error_count += 1,
2591            Severity::Warning => self.warning_count += 1,
2592            Severity::Note | Severity::Help => {}
2593        }
2594        self.diagnostics.push(diag);
2595    }
2596
2597    /// Everything raised so far, in the order it was raised.
2598    pub fn diagnostics(&self) -> &[Diagnostic] {
2599        &self.diagnostics
2600    }
2601
2602    /// Whether anything fatal has been raised.
2603    pub fn has_errors(&self) -> bool {
2604        self.error_count > 0
2605    }
2606
2607    /// How many errors have been raised.
2608    pub fn error_count(&self) -> u32 {
2609        self.error_count
2610    }
2611
2612    /// How many warnings have been raised.
2613    pub fn warning_count(&self) -> u32 {
2614        self.warning_count
2615    }
2616
2617    /// Whether the error limit has been reached and the caller should stop.
2618    pub fn error_limit_reached(&self) -> bool {
2619        self.opts.error_limit != 0 && self.error_count >= self.opts.error_limit
2620    }
2621}
2622
2623#[cfg(test)]
2624mod tests {
2625    use super::*;
2626
2627    fn session() -> Session {
2628        Session::new(Options::new("x86_64-unknown-linux-gnu".parse().unwrap()))
2629    }
2630
2631    #[test]
2632    fn a_version_claim_reads_the_way_gcc_prints_one() {
2633        // `gcc -dumpfullversion` gives all three, `gcc -dumpversion` gives one, and both are
2634        // things a script pastes straight into a flag.
2635        let all = |v: &str| v.parse::<GnucVersion>().unwrap();
2636        assert_eq!(all("15.1.0"), GnucVersion { major: 15, minor: 1, patch: 0 });
2637        assert_eq!(all("15"), GnucVersion { major: 15, minor: 0, patch: 0 });
2638        assert_eq!(all("4.2"), GnucVersion { major: 4, minor: 2, patch: 0 });
2639        assert!("".parse::<GnucVersion>().is_err());
2640        assert!("15.".parse::<GnucVersion>().is_err(), "a trailing dot is a typo, not a zero");
2641        assert!("1.2.3.4".parse::<GnucVersion>().is_err());
2642    }
2643
2644    #[test]
2645    fn a_prefix_map_rewrites_the_front_of_a_path_and_nothing_else() {
2646        let map = |pairs: &[(&str, &str)]| {
2647            let mut map = PrefixMap::new();
2648            for &(old, new) in pairs {
2649                map.push(old, new);
2650            }
2651            map
2652        };
2653        assert!(PrefixMap::new().is_empty());
2654        assert_eq!(PrefixMap::new().apply("sub/h.h"), "sub/h.h");
2655
2656        let one = map(&[("sub", "SUB")]);
2657        assert_eq!(one.apply("sub/h.h"), "SUB/h.h");
2658        assert_eq!(one.apply("a.c"), "a.c", "a path the mapping does not start");
2659        assert_eq!(one.apply("x/sub/h.h"), "x/sub/h.h", "the middle of a path is not the front");
2660
2661        // Characters rather than directories, which is what gcc compares and is worth a test of
2662        // its own because it is the part that looks like it ought to be otherwise.
2663        assert_eq!(map(&[("s", "B")]).apply("sub/h.h"), "Bub/h.h");
2664        assert_eq!(map(&[("sub/", "SUB/")]).apply("sub/h.h"), "SUB/h.h");
2665        assert_eq!(map(&[("sub", "")]).apply("sub/h.h"), "/h.h", "mapping to nothing");
2666        assert_eq!(map(&[("", "PRE")]).apply("a.c"), "PREa.c", "an empty old is in front of all");
2667
2668        // The last one that matches wins, whether or not the two ask about the same prefix, which
2669        // is what a project wide mapping plus a narrower one for a directory relies on.
2670        assert_eq!(map(&[("sub", "ONE"), ("sub", "TWO")]).apply("sub/h.h"), "TWO/h.h");
2671        assert_eq!(map(&[("sub", "A"), ("s", "B")]).apply("sub/h.h"), "Bub/h.h");
2672        assert_eq!(map(&[("s", "B"), ("sub", "A")]).apply("sub/h.h"), "A/h.h");
2673        assert_eq!(map(&[("nope", "X"), ("sub", "A")]).apply("sub/h.h"), "A/h.h");
2674    }
2675
2676    #[test]
2677    fn the_argument_is_split_at_the_last_equals_sign() {
2678        assert_eq!(PrefixMap::split("old=new"), Some(("old", "new")));
2679        assert_eq!(PrefixMap::split("=new"), Some(("", "new")), "an empty old is allowed");
2680        assert_eq!(PrefixMap::split("old="), Some(("old", "")), "and so is an empty new");
2681        // The last rather than the first, so a directory whose name has an `=` in it can be
2682        // mapped and a replacement whose name has one cannot. That is gcc's choice of which of
2683        // the two to make possible, and it is the right way round.
2684        assert_eq!(PrefixMap::split("/home/a=b=/src"), Some(("/home/a=b", "/src")));
2685        assert_eq!(PrefixMap::split("nope"), None);
2686    }
2687
2688    #[test]
2689    fn optimisation_levels_parse_the_way_gcc_spells_them() {
2690        assert_eq!("".parse::<OptLevel>().unwrap(), OptLevel::O1);
2691        assert_eq!("0".parse::<OptLevel>().unwrap(), OptLevel::O0);
2692        assert_eq!("2".parse::<OptLevel>().unwrap(), OptLevel::O2);
2693        assert_eq!("9".parse::<OptLevel>().unwrap(), OptLevel::O3);
2694        assert_eq!("s".parse::<OptLevel>().unwrap(), OptLevel::Os);
2695        assert!("q".parse::<OptLevel>().is_err());
2696    }
2697
2698    #[test]
2699    fn only_o0_skips_the_optimizer() {
2700        assert!(!OptLevel::O0.runs_optimizer());
2701        assert!(OptLevel::O1.runs_optimizer());
2702        assert!(OptLevel::Oz.runs_optimizer());
2703    }
2704
2705    #[test]
2706    fn the_safety_tiers_round_trip_and_nothing_else_is_one() {
2707        for tier in [Safety::Off, Safety::Detect, Safety::Enforce, Safety::Kernel] {
2708            assert_eq!(tier.as_str().parse::<Safety>().unwrap(), tier);
2709        }
2710        // `on` is the obvious thing to try and it is not a tier, because which tier somebody
2711        // means by it is the whole question document 02 answers.
2712        assert!("on".parse::<Safety>().is_err());
2713        assert!("".parse::<Safety>().is_err());
2714    }
2715
2716    #[test]
2717    fn room_for_a_patcher_is_written_the_way_it_was_asked_for() {
2718        for (written, total, before) in
2719            [("0", 0, 0), ("2", 2, 0), ("16", 16, 0), ("5,3", 5, 3), ("3,3", 3, 3)]
2720        {
2721            let room: Patchable = written.parse().unwrap();
2722            assert_eq!(room, Patchable { total, before });
2723            assert_eq!(room.to_string(), written);
2724            assert_eq!(room.after(), total - before);
2725            assert_eq!(room.any(), total > 0);
2726        }
2727        // A second number of zero is the same request as no second number, and it is written back
2728        // the shorter way, which is the way somebody reaching for the flag writes it.
2729        assert_eq!("2,0".parse::<Patchable>().unwrap().to_string(), "2");
2730    }
2731
2732    #[test]
2733    fn more_room_in_front_of_the_label_than_there_is_room_at_all_is_refused() {
2734        // Rather than clamped, because there is no reading of it a caller meant. gcc says the same
2735        // about each of these.
2736        assert!("1,2".parse::<Patchable>().is_err());
2737        assert!("1,2,3".parse::<Patchable>().is_err());
2738        assert!("a".parse::<Patchable>().is_err());
2739        assert!("".parse::<Patchable>().is_err());
2740        assert!("-1".parse::<Patchable>().is_err());
2741    }
2742
2743    #[test]
2744    fn the_two_places_the_intermediate_files_can_go_are_the_two_words_that_are_taken() {
2745        assert_eq!("obj".parse::<SaveTemps>().unwrap(), SaveTemps::Object);
2746        assert_eq!("cwd".parse::<SaveTemps>().unwrap(), SaveTemps::Cwd);
2747        // The names of the two flags that mean the same thing as `=obj` are not themselves
2748        // arguments of it, and neither is silence.
2749        assert!("obj,cwd".parse::<SaveTemps>().is_err());
2750        assert!("".parse::<SaveTemps>().is_err());
2751        // Nothing is kept unless something asked, and both of the words that ask do ask.
2752        assert_eq!(SaveTemps::default(), SaveTemps::No);
2753        assert!(!SaveTemps::No.wanted());
2754        assert!(SaveTemps::Object.wanted());
2755        assert!(SaveTemps::Cwd.wanted());
2756    }
2757
2758    #[test]
2759    fn a_build_that_did_not_ask_for_the_monitor_does_not_get_it() {
2760        assert_eq!(Safety::default(), Safety::Off);
2761        assert!(!Safety::Off.instruments());
2762        assert!(Safety::Detect.instruments());
2763        assert!(Safety::Enforce.instruments());
2764        assert!(Safety::Kernel.instruments());
2765    }
2766
2767    #[test]
2768    fn emit_kinds_round_trip_through_their_names() {
2769        for k in [
2770            EmitKind::Executable,
2771            EmitKind::Object,
2772            EmitKind::Asm,
2773            EmitKind::Preprocessed,
2774            EmitKind::Tast,
2775            EmitKind::Ir,
2776            EmitKind::MirFinal,
2777            EmitKind::SyntaxOnly,
2778        ] {
2779            assert_eq!(k.as_str().parse::<EmitKind>().unwrap(), k);
2780        }
2781    }
2782
2783    #[test]
2784    fn errors_are_counted_and_warnings_are_not() {
2785        let mut s = session();
2786        s.emit(Diagnostic::error("no", rucc_diag::Span::DUMMY));
2787        s.emit(Diagnostic::warning("hmm", rucc_diag::Span::DUMMY));
2788        assert_eq!(s.error_count(), 1);
2789        assert_eq!(s.warning_count(), 1);
2790        assert!(s.has_errors());
2791        assert_eq!(s.diagnostics().len(), 2);
2792    }
2793
2794    #[test]
2795    fn werror_promotes_once_at_the_sink() {
2796        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse().unwrap());
2797        opts.warnings_are_errors = true;
2798        let mut s = Session::new(opts);
2799        s.emit(Diagnostic::warning("hmm", rucc_diag::Span::DUMMY));
2800        assert_eq!(s.error_count(), 1);
2801        assert_eq!(s.warning_count(), 0);
2802        assert_eq!(s.diagnostics()[0].severity, Severity::Error);
2803    }
2804
2805    #[test]
2806    fn the_error_limit_can_be_switched_off() {
2807        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse().unwrap());
2808        opts.error_limit = 0;
2809        let mut s = Session::new(opts);
2810        for _ in 0..100 {
2811            s.emit(Diagnostic::error("no", rucc_diag::Span::DUMMY));
2812        }
2813        assert!(!s.error_limit_reached());
2814    }
2815
2816    #[test]
2817    fn the_session_carries_the_source_map_spans_are_resolved_against() {
2818        let mut s = session();
2819        let file = s.sources.add("a.c", b"int x;\n".to_vec()).unwrap();
2820        let start = s.sources.file(file).start;
2821        assert_eq!(s.sources.render_position(start + 4), "a.c:1:5");
2822    }
2823
2824    #[test]
2825    fn the_session_carries_the_resolved_target() {
2826        let s = session();
2827        assert_eq!(s.target.pointer_width, 64);
2828        assert!(s.target.char_is_signed);
2829    }
2830}