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.19.0")]
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, CodeModel, Env, Isa, Os, Speculation, 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/// `crates/rucc-object` writes all of them on ELF, and on the other formats every answer writes the
518/// sections as they are.
519#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
520pub enum Compress {
521 /// `-gz=none`, and what a command line that says nothing gets. gcc's default is the same.
522 #[default]
523 None,
524 /// `-gz` and `-gz=zlib`. The ELF way, with the `SHF_COMPRESSED` flag and an `Elf64_Chdr` in
525 /// front of the data. Bare `-gz` means this one, which is worth knowing because the manual
526 /// describes the flag without saying so.
527 Zlib,
528 /// `-gz=zlib-gnu`. The older way, where the section is renamed from `.debug_info` to
529 /// `.zdebug_info` and carries `ZLIB` and a length instead of a real header. Kept because
530 /// binutils still reads it and some build systems still ask for it by name.
531 ZlibGnu,
532 /// `-gz=zstd`. The same arrangement as `Zlib` with a different algorithm in the header, which
533 /// packs debug information smaller and unpacks it faster.
534 Zstd,
535}
536
537impl Compress {
538 /// The spelling this is asked for by, without the `-gz=` in front of it.
539 pub const fn as_str(self) -> &'static str {
540 match self {
541 Compress::None => "none",
542 Compress::Zlib => "zlib",
543 Compress::ZlibGnu => "zlib-gnu",
544 Compress::Zstd => "zstd",
545 }
546 }
547}
548
549impl fmt::Display for Compress {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 f.write_str(self.as_str())
552 }
553}
554
555impl FromStr for Compress {
556 type Err = ();
557
558 /// Parses the part after `-gz=`. Bare `-gz` is not this function's business because there is
559 /// nothing after the flag to hand it.
560 fn from_str(s: &str) -> Result<Self, ()> {
561 Ok(match s {
562 "none" => Compress::None,
563 "zlib" => Compress::Zlib,
564 "zlib-gnu" => Compress::ZlibGnu,
565 "zstd" => Compress::Zstd,
566 _ => return Err(()),
567 })
568 }
569}
570
571/// How many processes the link time work is spread over, which is what `-flto=` takes.
572///
573/// Named for the flag rather than for what it counts, because `Jobs` in the driver is already the
574/// answer to how many files are compiled at once and the two numbers are not the same number.
575///
576/// The link time half of link time optimization is where all of the time goes, because it is the
577/// half that has the whole program in front of it, and gcc's answer is to cut the program into
578/// pieces and generate code for the pieces at once. This says how many at once.
579#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
580pub enum LtoJobs {
581 /// Bare `-flto`, and `-flto=1`. One process, which is what gcc does when the flag is written
582 /// without a number after it.
583 #[default]
584 One,
585 /// `-flto=auto`. As many as the machine has, worked out when the link runs.
586 Auto,
587 /// `-flto=jobserver`. As many as `make` is willing to hand out, asked for through the
588 /// jobserver pipe it puts in the environment, which is the only answer that does not fight
589 /// with the rest of a parallel build for the same cores.
590 Jobserver,
591 /// `-flto=<n>`. Exactly that many. gcc refuses a zero, so this is never one.
592 Count(u32),
593}
594
595impl FromStr for LtoJobs {
596 type Err = ();
597
598 /// Parses the part after `-flto=`. A number has to be positive, which is gcc's rule: `-flto=0`
599 /// is refused rather than read as `-fno-lto`.
600 fn from_str(s: &str) -> Result<Self, ()> {
601 Ok(match s {
602 "auto" => LtoJobs::Auto,
603 "jobserver" => LtoJobs::Jobserver,
604 _ => match s.parse::<u32>() {
605 Ok(1) => LtoJobs::One,
606 Ok(n) if n > 1 => LtoJobs::Count(n),
607 _ => return Err(()),
608 },
609 })
610 }
611}
612
613impl fmt::Display for LtoJobs {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 match self {
616 LtoJobs::One => f.write_str("1"),
617 LtoJobs::Auto => f.write_str("auto"),
618 LtoJobs::Jobserver => f.write_str("jobserver"),
619 LtoJobs::Count(n) => write!(f, "{n}"),
620 }
621 }
622}
623
624/// How the program is cut up before the link time work is spread over it, from `-flto-partition=`.
625///
626/// A partition is a set of functions that are generated together, and where the cuts fall decides
627/// both how well the work spreads and how much is visible from inside one piece. The names are
628/// gcc's and so are the shapes: one piece per input file, pieces balanced by size, one piece for
629/// the whole program, a piece per function, or no partitioning at all.
630#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
631pub enum Partition {
632 /// `-flto-partition=balanced`, and what gcc does when nothing asks. Pieces of roughly equal
633 /// size, which is the answer that spreads the work best and is why it is the default.
634 #[default]
635 Balanced,
636 /// `-flto-partition=1to1`. One piece per input file, which keeps the generated code in the
637 /// same order the inputs were in and is what a build comparing two outputs wants.
638 OneToOne,
639 /// `-flto-partition=one`. The whole program in one piece, which is the most the optimizer can
640 /// see at once and the least the work can be spread over.
641 One,
642 /// `-flto-partition=max`. A piece per function, which is the other end of the same trade.
643 Max,
644 /// `-flto-partition=none`. No partitioning, and no streaming back out to be generated in
645 /// pieces either.
646 None,
647}
648
649impl Partition {
650 /// The spelling this is asked for by, without the `-flto-partition=` in front of it.
651 pub const fn as_str(self) -> &'static str {
652 match self {
653 Partition::Balanced => "balanced",
654 Partition::OneToOne => "1to1",
655 Partition::One => "one",
656 Partition::Max => "max",
657 Partition::None => "none",
658 }
659 }
660}
661
662impl fmt::Display for Partition {
663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
664 f.write_str(self.as_str())
665 }
666}
667
668impl FromStr for Partition {
669 type Err = ();
670
671 /// Parses the part after `-flto-partition=`.
672 fn from_str(s: &str) -> Result<Self, ()> {
673 Ok(match s {
674 "balanced" => Partition::Balanced,
675 "1to1" => Partition::OneToOne,
676 "one" => Partition::One,
677 "max" => Partition::Max,
678 "none" => Partition::None,
679 _ => return Err(()),
680 })
681 }
682}
683
684/// What the `-flto` family asked for, which is a whole optimization this compiler does not do yet.
685///
686/// Link time optimization is the optimizer run once over the whole program instead of once per
687/// translation unit, which is the only way an inliner ever sees across a file boundary and is
688/// where most of what is left on the table after `-O2` is. `spec/09-optimizer.md` says how it will
689/// work here: the IR goes into a section of the object, the driver finds those sections at link
690/// time, merges them into one module and generates code with everything visible.
691///
692/// The first half of that exists: an object compiled with `-flto` keeps its module beside its
693/// code, in a section the linker leaves out. Nothing reads it at link time yet, so apart from that
694/// the family is read, checked and recorded rather than acted on. That is a different answer from the one `-gsplit-dwarf` gets in the same specification, and the
695/// difference is what ignoring each of them does. Ignoring `-gsplit-dwarf` means a file a build
696/// asked for never appears. Ignoring this means a program that is correct and slower than it could
697/// have been, which is what section 4.1 means by a hint about speed, and which is also what every
698/// compilation at `-O0` already is.
699///
700/// The other half of the argument is about the object. gcc's `-flto` object holds the bytecode and
701/// no machine code at all, so it is only useful to a link that knows about it; the objects here
702/// always hold the code, which is what `-ffat-lto-objects` asks gcc for. So a build that passes
703/// `-flto` to this compiler gets objects that are strictly more usable than the ones it would have
704/// got, rather than different ones.
705#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
706pub struct Lto {
707 /// Whether the last of `-flto` and `-fno-lto` on the command line was the first of the two.
708 pub requested: bool,
709 /// How many processes to spread the link time work over.
710 pub jobs: LtoJobs,
711 /// How the program is cut up before the work is spread.
712 pub partition: Partition,
713 /// How hard to compress the IR on its way into the object, from `-flto-compression-level=`,
714 /// where `None` means whatever the compressor does when nobody says. Between 0 and 19, which
715 /// is zstd's range and is the range gcc checks against.
716 pub compression: Option<u8>,
717}
718
719/// What the profile reading half of the `-fprofile` family asked for.
720///
721/// A profile is a count per edge, gathered by running a build of the program that was instrumented
722/// to count, and read back on a second compilation so that the optimizer knows which way each
723/// branch actually went. It is worth more than any single optimization, because almost everything
724/// the optimizer decides is a guess about a frequency that the counts simply state.
725///
726/// Nothing here reads one yet, so this is recorded rather than acted on, and the family splits in
727/// two rather than being taken or refused as a whole. The half recorded here is the half that only
728/// costs speed when it is ignored: a build that asks to read a profile and is not read one gets the
729/// program it would have got anyway, which is what section 4.1 means by a hint about speed. The
730/// other half writes files. `-fprofile-arcs` is done, as [`Profile::arcs`], and the rest of that half
731/// is refused by the driver rather than landing here, on the same reading `-gsplit-dwarf` gets:
732/// `-ftest-coverage` writes a `.gcno` beside the object, and ignoring it means a build waits for a
733/// file that never arrives.
734///
735/// gcc's own measurement is the argument for the split. `-fprofile-use` on a file with no counts
736/// beside it produces an object byte for byte identical to the one no flag produces, and warns; the
737/// same file under `-fprofile-generate` grows from 71 bytes of code to 375 with 296 bytes of
738/// counters beside it. So one half of the family is already a no-op in gcc when there is nothing to
739/// read, and the other half is never one.
740#[derive(Debug, Clone, PartialEq, Eq, Default)]
741pub struct Profile {
742 /// Whether the last of `-fprofile-use` and `-fno-profile-use` on the command line was the
743 /// first of the two.
744 pub requested: bool,
745 /// Where to read the counts from, from `-fprofile-use=<path>`, where `None` means beside the
746 /// object the way gcc looks when nobody says. A directory or a file, which is gcc's rule and
747 /// is not something this can tell apart without looking at the filesystem.
748 pub path: Option<String>,
749 /// Where the whole family's files live, from `-fprofile-dir=`. Separate from `path` because
750 /// gcc keeps them separate: this one moves the counts for the generating half as well.
751 pub dir: Option<String>,
752 /// Whether the path recorded in those files is made absolute, from `-fprofile-abs-path`. It is
753 /// what a build with several object directories under one source tree needs so that two files
754 /// of the same name do not land on one set of counts.
755 pub absolute: bool,
756 /// Whether counts that do not add up are repaired rather than refused, from
757 /// `-fprofile-correction`. A program that forked or was killed while it ran leaves counts that
758 /// no single execution could have produced, and this says to make the best of them.
759 pub correction: bool,
760 /// Whether the parts of the program the training run never reached are optimized as if they
761 /// were cold rather than as if nothing were known about them, from `-fprofile-partial-training`.
762 pub partial_training: bool,
763 /// Whether every function gets arc counters and the unit a record registering them, from
764 /// `-fprofile-arcs`. See `rucc_opt::coverage`.
765 pub arcs: bool,
766 /// The `.gcda` file the counters of this unit are written to, which the driver fills in for
767 /// each job from the object's name, the working directory and `-fprofile-dir=`.
768 pub counts: Option<String>,
769 /// Whether the graph the counters are on is written to a `.gcno` file beside the object, from
770 /// `-ftest-coverage`.
771 pub notes: bool,
772}
773
774/// Which functions get a stack protector, which is what the `-fstack-protector` family asks.
775///
776/// A canary is a word the prologue copies into the frame above everything a local can be written
777/// through, and the epilogue compares it against the copy the runtime still holds before it
778/// returns. A write that runs off the end of a local and keeps going passes the canary on its way
779/// to the return address, so a function that returns with the word changed calls
780/// `__stack_chk_fail` instead of returning at all.
781///
782/// Which functions are worth the slot and the comparison is what the three levels disagree about,
783/// and the middle one is the one that matters: every distribution has built its packages with
784/// `-fstack-protector-strong` for a decade, so a compiler that cannot take the flag cannot be the
785/// `CC` of a package build whatever else it can do.
786#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
787pub enum Protector {
788 /// `-fno-stack-protector`, and what a command line that says nothing gets. gcc's own default
789 /// is the same, and it is the distributions rather than the compiler that turn it on.
790 #[default]
791 None,
792 /// `-fstack-protector`. A function with a local array of at least eight bytes, or one whose
793 /// stack grows while it runs.
794 Buffers,
795 /// `-fstack-protector-strong`. Any of those, and any function with a local array at all, a
796 /// local holding one, or a local whose address is taken.
797 Strong,
798 /// `-fstack-protector-all`. Every function that has a frame.
799 All,
800 /// `-fstack-protector-explicit`. Only a function that asks with `stack_protect`.
801 Explicit,
802}
803
804/// What overflows rather than being undefined, from `-fwrapv` and its relatives.
805///
806/// C says a signed addition that overflows and a pointer that walks off the end of the object it
807/// points into are both undefined, and an optimizer that believes it reads a great deal into every
808/// loop: that a counter going up one at a time never turns round, that an index widened to an
809/// address may be widened before the arithmetic rather than after, that a bound is reached. These
810/// flags withdraw exactly that. They do not make the program mean something else, they make it mean
811/// less, and the code that asks for them is code that overflows on purpose and wants the answer the
812/// machine gives rather than the answer the standard declines to give.
813///
814/// Two of them because gcc has two, and a build that wants one usually wants the other. Signed
815/// arithmetic and pointer arithmetic are separate assumptions and a kernel turns both off.
816///
817/// `-ftrapv` is the third answer to the first question and is here for that reason. Undefined,
818/// wrapping and stopping are the three things a signed overflow can be, and a command line picks
819/// one of them: the last of `-fwrapv` and `-ftrapv` wins, which is gcc's behaviour and what makes
820/// them one field rather than two that can both be set.
821#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
822pub struct Wrapping {
823 /// Whether signed arithmetic wraps, from `-fwrapv`.
824 pub signed: bool,
825 /// Whether pointer arithmetic wraps, from `-fwrapv-pointer`.
826 pub pointer: bool,
827 /// Whether a signed overflow stops the program instead, from `-ftrapv`.
828 ///
829 /// Never set at the same time as [`Wrapping::signed`], since a program cannot both wrap and
830 /// stop, and the driver is what keeps that true by clearing each when the other is asked for.
831 pub trap: bool,
832}
833
834impl Wrapping {
835 /// Both of them, which is what `-fno-strict-overflow` asks for.
836 ///
837 /// gcc says so itself: its help text for `-fstrict-overflow` reads "negated as `-fwrapv`
838 /// `-fwrapv-pointer`", so the older flag is a name for the pair rather than a third knob. And
839 /// asking for wrapping is asking for not stopping, so this is the whole answer and not two
840 /// thirds of one.
841 pub const ALL: Self = Self { signed: true, pointer: true, trap: false };
842
843 /// Neither, which is the default and what a command line that says nothing about any of this
844 /// gets.
845 pub const NONE: Self = Self { signed: false, pointer: false, trap: false };
846}
847
848/// A list of `old=new` rewrites to apply to a path before it is written into the output, which is
849/// what the `-f*-prefix-map=` family asks for.
850///
851/// The point of them is a build whose output does not depend on where it was built. A path is the
852/// last thing in an object that a second machine cannot reproduce: two people who check out the
853/// same commit and run the same compiler get the same instructions and different `__FILE__`
854/// strings, and a distribution that wants to prove its binaries came from its sources has to make
855/// that difference go away. So the build says what its root is called, and every path that would
856/// name the real one names that instead.
857///
858/// The rule is a plain string prefix and nothing more, which is worth saying because it looks like
859/// it ought to be about directories. gcc compares the characters, so `s=B` turns `sub/h.h` into
860/// `Bub/h.h`, and an empty `old` matches everything and puts `new` in front of it. The path
861/// compared against is the one the search found, so a header reached through a relative `-I` is
862/// mapped as a relative path and the same header reached through an absolute one is mapped as an
863/// absolute path.
864#[derive(Debug, Clone, Default, PartialEq, Eq)]
865pub struct PrefixMap {
866 /// The rewrites, in the order the command line gave them.
867 entries: Vec<(String, String)>,
868}
869
870impl PrefixMap {
871 /// No rewrites, which is what a command line that says nothing about this gets.
872 #[must_use]
873 pub fn new() -> Self {
874 Self::default()
875 }
876
877 /// Whether nothing was asked for, which is the case worth not spending anything on.
878 #[must_use]
879 pub fn is_empty(&self) -> bool {
880 self.entries.is_empty()
881 }
882
883 /// Adds a rewrite, which is what one flag on the command line is.
884 pub fn push(&mut self, old: impl Into<String>, new: impl Into<String>) {
885 self.entries.push((old.into(), new.into()));
886 }
887
888 /// The two halves of one flag's argument, split at the last `=` rather than the first.
889 ///
890 /// That is where gcc splits it, and it is the answer that makes a path containing an `=`
891 /// mappable: `-ffile-prefix-map=/home/a=b=/src` maps the directory `/home/a=b`. The cost is
892 /// that a replacement cannot contain one, which is the rarer thing to want. `None` when there
893 /// is no `=` at all, which gcc refuses rather than reading as a mapping to nothing.
894 #[must_use]
895 pub fn split(arg: &str) -> Option<(&str, &str)> {
896 arg.rsplit_once('=')
897 }
898
899 /// `path` with the last rewrite that matches it applied, or `path` where none does.
900 ///
901 /// The last rather than the first, because that is gcc's answer and because it is the one a
902 /// build relies on: a mapping set for the whole project and a narrower one set for one
903 /// directory is a command line where the second is meant to win.
904 #[must_use]
905 pub fn apply<'a>(&self, path: &'a str) -> Cow<'a, str> {
906 for (old, new) in self.entries.iter().rev() {
907 if let Some(rest) = path.strip_prefix(old.as_str()) {
908 return Cow::Owned(format!("{new}{rest}"));
909 }
910 }
911 Cow::Borrowed(path)
912 }
913}
914
915/// The three answers to the question the `-f*-prefix-map=` family asks, which is one question
916/// asked about three kinds of output.
917///
918/// They are separate because gcc's flags are separate and a build uses that: a distribution maps
919/// its debug paths to something a debugger can find the sources under and leaves `__FILE__` alone,
920/// or maps `__FILE__` so that an assertion message does not name a build directory and leaves the
921/// debug info pointing at the real tree. `-ffile-prefix-map=` is the shorthand for all three and is
922/// what a build that simply wants to be reproducible writes.
923#[derive(Debug, Clone, Default, PartialEq, Eq)]
924pub struct PrefixMaps {
925 /// What `__FILE__` and `__BASE_FILE__` are rewritten by, from `-fmacro-prefix-map=`.
926 ///
927 /// The only one of the three this compiler acts on today, because it is the only one whose
928 /// output exists: `__FILE__` is a string literal in the binary and an assertion message a user
929 /// reads.
930 pub macros: PrefixMap,
931 /// What a path in the debug info is rewritten by, from `-fdebug-prefix-map=`.
932 ///
933 /// Read by the driver rather than by `rucc-debug`, because the driver is the layer where a path
934 /// is still a path and by the time one reaches the DWARF writer it is a string in a table that
935 /// nothing is allowed to reinterpret. Every path that reaches the line table goes through it,
936 /// the unit's own name and the directory it was compiled in among them.
937 pub debug: PrefixMap,
938 /// What a path in the profile data is rewritten by, from `-fprofile-prefix-map=`.
939 ///
940 /// Nothing reads this yet either, and for the same reason: there is no profile data.
941 pub profile: PrefixMap,
942}
943
944/// How far a multiply and an addition may be fused into one rounding, from `-ffp-contract=`.
945///
946/// A fused multiply add computes `a * b + c` with one rounding instead of two, which is both
947/// faster and closer to the exact answer, and is therefore a different answer. C lets an
948/// implementation do it within one expression and lets a program turn it off with the
949/// `FP_CONTRACT` pragma, gcc does it across a whole function by default, and code that cares about
950/// reproducing a result bit for bit turns it off everywhere.
951///
952/// This is the command line's answer to that question, and it is carried into the IR as an
953/// attribute on each function so that the code generator still has it by the time it would matter.
954/// It is a separate question from the flag on one instruction: a licence granted to an expression
955/// the optimizer has since taken apart is a licence about operations that no longer sit together,
956/// and only the function level answer survives that.
957#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
958pub enum Contract {
959 /// `-ffp-contract=off`. Never, so every rounding the source asked for happens.
960 ///
961 /// The default here, which is not gcc's. gcc defaults to `fast` under its own dialects and to
962 /// `off` under a strict `-std=`, and the reason the default is this one anyway is that nothing
963 /// in this compiler fuses anything: the two settings are the same program today, and of the two
964 /// this is the one that does not write a licence nobody reads onto every function in the file.
965 /// The day the code generator learns to fuse, the default moves to gcc's, and that is a change
966 /// to the code generator rather than to this flag.
967 #[default]
968 Off,
969 /// `-ffp-contract=on`. Within one expression, which is what C allows an implementation to do
970 /// without being asked.
971 On,
972 /// `-ffp-contract=fast`. Anywhere in the function, across statements and across whatever the
973 /// optimizer has rearranged, which is what gcc does under its own dialects.
974 Fast,
975}
976
977impl Contract {
978 /// The spelling after the `=`.
979 pub const fn as_str(self) -> &'static str {
980 match self {
981 Contract::Off => "off",
982 Contract::On => "on",
983 Contract::Fast => "fast",
984 }
985 }
986}
987
988impl fmt::Display for Contract {
989 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
990 f.write_str(self.as_str())
991 }
992}
993
994impl FromStr for Contract {
995 type Err = ();
996
997 fn from_str(s: &str) -> Result<Self, ()> {
998 Ok(match s {
999 "off" => Contract::Off,
1000 "on" => Contract::On,
1001 "fast" => Contract::Fast,
1002 _ => return Err(()),
1003 })
1004 }
1005}
1006
1007impl Protector {
1008 /// The spelling this is asked for by, which is the whole flag rather than a part of one,
1009 /// because these are five flags and not one flag with an argument.
1010 pub const fn as_str(self) -> &'static str {
1011 match self {
1012 Protector::None => "-fno-stack-protector",
1013 Protector::Buffers => "-fstack-protector",
1014 Protector::Strong => "-fstack-protector-strong",
1015 Protector::All => "-fstack-protector-all",
1016 Protector::Explicit => "-fstack-protector-explicit",
1017 }
1018 }
1019}
1020
1021impl fmt::Display for Protector {
1022 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1023 f.write_str(self.as_str())
1024 }
1025}
1026
1027/// Which control flow transfers are checked, which is what `-fcf-protection=` asks.
1028///
1029/// Two mechanisms and one flag, because the hardware turns them on together and a program built
1030/// for one and not the other is a program with a hole in whichever half was left out. The forward
1031/// edge is an indirect call or jump, and it is checked by a landing pad at every address one is
1032/// allowed to arrive at, so a corrupted function pointer reaches somewhere somebody meant rather
1033/// than any byte of the program. The backward edge is a return, and it is checked against a second
1034/// copy of the return address the program cannot write to, which needs no instructions at all: the
1035/// machine keeps the copy and the loader turns it on.
1036///
1037/// Which is why the marker matters as much as the code. An object says in a note which halves it
1038/// was built for, the linker takes the intersection over every input, and the loader turns on what
1039/// survives. One object built without the note is enough to turn the whole program's protection
1040/// off, so the note goes in even for a mode that changes no instruction.
1041#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1042pub enum Control {
1043 /// `-fcf-protection=none` and `-fno-cf-protection`, and what a command line that says nothing
1044 /// gets. gcc's own default is the same on the targets this compiler has a back end for.
1045 #[default]
1046 None,
1047 /// `-fcf-protection=branch`. The forward edge alone: a landing pad at every function, and a
1048 /// note that asks for the check on indirect transfers and not on returns.
1049 Branch,
1050 /// `-fcf-protection=return`. The backward edge alone, which is the note and nothing else,
1051 /// since the copy of the return address is the machine's own and no instruction maintains it.
1052 Return,
1053 /// `-fcf-protection=full`, and what the bare `-fcf-protection` means. Both halves.
1054 Full,
1055 /// `-fcf-protection=check`. Asks that the compilation be checked for compatibility with the
1056 /// mode rather than built in it, so nothing is instrumented and no note is written, which is
1057 /// exactly what gcc emits for it.
1058 Check,
1059}
1060
1061impl Control {
1062 /// Whether a landing pad goes at the top of every function.
1063 #[must_use]
1064 pub const fn branch(self) -> bool {
1065 matches!(self, Control::Branch | Control::Full)
1066 }
1067
1068 /// Whether returns are asked to be checked against the machine's own copy.
1069 #[must_use]
1070 pub const fn ret(self) -> bool {
1071 matches!(self, Control::Return | Control::Full)
1072 }
1073
1074 /// Whether anything at all is asked for, which is what decides whether the file says what it
1075 /// was built for.
1076 ///
1077 /// False for the two modes that build nothing. [`Control::None`] asks for nothing and
1078 /// [`Control::Check`] asks that the compilation be looked at rather than changed, and gcc
1079 /// writes no note for either.
1080 #[must_use]
1081 pub const fn any(self) -> bool {
1082 self.branch() || self.ret()
1083 }
1084
1085 /// What the argument was spelled as, which is the part after the equals sign.
1086 pub const fn as_str(self) -> &'static str {
1087 match self {
1088 Control::None => "none",
1089 Control::Branch => "branch",
1090 Control::Return => "return",
1091 Control::Full => "full",
1092 Control::Check => "check",
1093 }
1094 }
1095}
1096
1097impl fmt::Display for Control {
1098 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1099 f.write_str(self.as_str())
1100 }
1101}
1102
1103impl FromStr for Control {
1104 type Err = ();
1105
1106 /// Parses the part after `-fcf-protection=`.
1107 fn from_str(s: &str) -> Result<Self, ()> {
1108 Ok(match s {
1109 "none" => Control::None,
1110 "branch" => Control::Branch,
1111 "return" => Control::Return,
1112 "full" => Control::Full,
1113 "check" => Control::Check,
1114 _ => return Err(()),
1115 })
1116 }
1117}
1118
1119/// Where the call `-pg` puts at the top of every function goes, which `-mfentry` chooses.
1120///
1121/// Two conventions for one job, and the difference is what the hook can see when it runs. See
1122/// [`rucc_target::Trace`] for what each of them is and why a kernel needs the earlier one.
1123///
1124/// A third answer, because a command line that named neither has not asked a question: the
1125/// platform's own answer is the one it gets, and that is a fact about the target rather than about
1126/// the flags, so it is settled where the target is known and not here.
1127#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1128pub enum Hook {
1129 /// Whichever the platform puts first, which is what a command line that said neither gets.
1130 #[default]
1131 Platform,
1132 /// `-mfentry`. In front of the prologue, so the return address is the top thing on the stack
1133 /// and the arguments are still where the call left them.
1134 Early,
1135 /// `-mno-fentry`. Once the frame is taken, so the hook can walk back through the frame pointer,
1136 /// which is why a function that has this one is given a frame pointer whatever else was said.
1137 Late,
1138}
1139
1140impl Hook {
1141 /// That answer as it is written on a command line, which is what `--print-config` reports.
1142 #[must_use]
1143 pub const fn as_str(self) -> &'static str {
1144 match self {
1145 Hook::Platform => "platform",
1146 Hook::Early => "fentry",
1147 Hook::Late => "mcount",
1148 }
1149 }
1150
1151 /// Whether the call goes in front of the prologue, given what the platform puts first.
1152 #[must_use]
1153 pub const fn early(self, fentry: bool) -> bool {
1154 match self {
1155 Hook::Platform => fentry,
1156 Hook::Early => true,
1157 Hook::Late => false,
1158 }
1159 }
1160}
1161
1162impl fmt::Display for Hook {
1163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1164 f.write_str(self.as_str())
1165 }
1166}
1167
1168/// How much room at the top of every function is reserved for somebody to write over later, which
1169/// `-fpatchable-function-entry=` asks for.
1170///
1171/// Room rather than instructions. What goes there is a run of the shortest instruction the machine
1172/// has that does nothing, and the point of them is that they are never executed for long: a tracer
1173/// or a live patcher overwrites them with a jump or a call once the program is running, and what it
1174/// needs from the compiler is a known address, a known number of bytes, and a promise that nothing
1175/// in the function jumps into the middle of them.
1176///
1177/// Two numbers because the room can be on either side of the function's own label, and the two
1178/// sides are not the same thing. Room after the label is room inside the function, which is what a
1179/// patcher that redirects a call into the function wants. Room in front of the label is outside it,
1180/// so what goes there is reached only by something that already knows the address, and a patcher
1181/// that wants somewhere to put a whole instruction it can reach from the first one needs it.
1182///
1183/// The address recorded for the function is the start of the room, which is the front of the part
1184/// before the label when there is one and the front of the part after it when there is not.
1185#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1186pub struct Patchable {
1187 /// How many bytes in total, which is the first number and the one a command line must give.
1188 pub total: u32,
1189 /// How many of them go in front of the function's own label, which is the second number and is
1190 /// zero on a command line that gave one number.
1191 pub before: u32,
1192}
1193
1194impl Patchable {
1195 /// Whether any room at all was asked for, which is what decides whether a function gets a
1196 /// record.
1197 ///
1198 /// `=0` is a command line that asked for none, and gcc accepts it and writes nothing, so the
1199 /// question is about the number rather than about whether the flag was written.
1200 #[must_use]
1201 pub const fn any(self) -> bool {
1202 self.total > 0
1203 }
1204
1205 /// How many bytes go after the function's own label, which is the rest of them.
1206 #[must_use]
1207 pub const fn after(self) -> u32 {
1208 self.total - self.before
1209 }
1210}
1211
1212impl FromStr for Patchable {
1213 type Err = ();
1214
1215 /// Parses the part after `-fpatchable-function-entry=`, which is a number or two of them.
1216 ///
1217 /// A second number larger than the first is refused rather than clamped, because it asks for
1218 /// more room in front of the label than there is room at all and there is no reading of that a
1219 /// caller meant. So is a third, and so is anything that is not a number, which is what gcc does
1220 /// with each of them.
1221 fn from_str(s: &str) -> Result<Self, ()> {
1222 let (total, before) = match s.split_once(',') {
1223 Some((total, before)) => (total, before),
1224 None => (s, "0"),
1225 };
1226 let total: u32 = total.parse().map_err(|_| ())?;
1227 let before: u32 = before.parse().map_err(|_| ())?;
1228 if before > total {
1229 return Err(());
1230 }
1231 Ok(Patchable { total, before })
1232 }
1233}
1234
1235impl fmt::Display for Patchable {
1236 /// Written the way it was asked for, which is one number when the second is zero.
1237 ///
1238 /// Not because the two forms mean different things, they do not, but because that is the form
1239 /// a command line reaching for this feature writes and reading back what was written is what
1240 /// `--print-config` is for.
1241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1242 match self.before {
1243 0 => write!(f, "{}", self.total),
1244 before => write!(f, "{},{before}", self.total),
1245 }
1246 }
1247}
1248
1249/// Which link the output is written for, and whether it has to be position independent at all.
1250///
1251/// The first two answers are both position independent, so between them this is not about whether
1252/// there are absolute addresses in the text. It is about whether the link that reads the object is
1253/// one that puts every name in the same program. An executable is such a link and a shared library is not,
1254/// and the difference decides how a name is reached: from the instruction pointer where the
1255/// distance is a number the linker has, and out of the global offset table where it is not.
1256///
1257/// The expensive answer is the one that has to be asked for, which is gcc's arrangement and is why
1258/// `-fPIC` is on the compile line of every library and nowhere else. A name is only reached the
1259/// expensive way when it is one another object may define or replace, so `-fPIC -fvisibility=hidden`
1260/// costs no more than an executable does.
1261#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1262pub enum Pic {
1263 /// `-fPIE`, `-fpie` and nothing at all. The link puts every name in one program, so a name this
1264 /// file defines is at a distance from the instruction asking, and a name it declares ends up at
1265 /// one too, because the linker answers a reference to a variable defined in a library by making
1266 /// room for it here and copying it. That is what a distribution's default build is.
1267 #[default]
1268 Executable,
1269 /// `-fPIC` and `-fpic`. The output may end up in a shared library, where a name the file
1270 /// exports is one something loaded earlier may define too, and where a name defined elsewhere
1271 /// is not copied in. Both are reached through the global offset table.
1272 Library,
1273 /// `-fno-pic`, `-fno-pie` and their capital spellings. The output is linked where it runs and
1274 /// nothing relocates it afterwards, which is a kernel and a `-no-pie` executable. Every name is
1275 /// reached directly, so the object asks for no global offset table at all: a weak name nothing
1276 /// defines is resolved to zero by the static linker rather than read out of a slot, which is
1277 /// what a kernel's link script asserts when it checks that `.got` is empty. See tamnd/rucc#2276.
1278 Absolute,
1279}
1280
1281impl Pic {
1282 /// The spelling this is asked for by, which is the one gcc's manual leads with.
1283 pub const fn as_str(self) -> &'static str {
1284 match self {
1285 Pic::Executable => "-fPIE",
1286 Pic::Library => "-fPIC",
1287 Pic::Absolute => "-fno-pic",
1288 }
1289 }
1290}
1291
1292impl fmt::Display for Pic {
1293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1294 f.write_str(self.as_str())
1295 }
1296}
1297
1298/// What the compiler should produce.
1299///
1300/// The intermediate forms are not a debugging convenience bolted on later. Every one of them
1301/// is a documented textual form that round-trips, which is what makes the per-stage testing
1302/// in `spec/15-testing.md` section 15.2 possible.
1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1304// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
1305// match that needs to change, in this workspace and in anyone else's code. That is
1306// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
1307// target is a data change: the compiler tells you every place the data is read.
1308pub enum EmitKind {
1309 /// A linked executable. The default.
1310 #[default]
1311 Executable,
1312 /// An object file, `-c`.
1313 Object,
1314 /// A static library holding the objects of every input, `--emit=archive`.
1315 ///
1316 /// Not a GCC mode, because GCC has `ar` beside it and we have said we ship a toolchain rather
1317 /// than half of one. What needs it first is `cargo xtask builtins`, which has to turn a
1318 /// directory of C files into the `librucc_builtins.a` a cross link looks for, for a target
1319 /// whose machine may have no `ar` that knows the format.
1320 ///
1321 /// It is a mode of the compiler rather than a second program because of the symbol index. A
1322 /// static link resolves through it, so writing one means knowing what each member defines, and
1323 /// the compiler has just finished compiling them. An `ar` would have to read the objects back
1324 /// to find out the same thing.
1325 Archive,
1326 /// Assembly text, `-S`.
1327 Asm,
1328 /// Preprocessed source, `-E`.
1329 Preprocessed,
1330 /// The typed AST, `--emit=tast`.
1331 Tast,
1332 /// The IR, `--emit=ir`.
1333 Ir,
1334 /// The machine IR after register allocation, `--emit=mir-final`.
1335 MirFinal,
1336 /// The safety summary, `--emit=safety-summary`.
1337 ///
1338 /// Not an intermediate form of the program the way the three above are. It is the answer to
1339 /// "what does this build's guarantee actually rest on", which
1340 /// `spec/safe-memory/07-check-elimination.md` section 7.8 asks for and
1341 /// `spec/safe-memory/10-boundaries.md` section 10.2 says why.
1342 SafetySummary,
1343 /// How the bytes of the translation unit's records fall into granules,
1344 /// `--emit=type-granules`.
1345 ///
1346 /// Not an intermediate form either. It is the measurement
1347 /// `spec/safe-memory/17-open-questions.md` question 6 asks for, which decides whether the
1348 /// type plane fits inside Tier D's memory budget, and it needs nothing past the type
1349 /// checker because it is a question about layouts rather than about code.
1350 TypeGranules,
1351 /// Nothing at all, `-fsyntax-only`.
1352 ///
1353 /// The front end runs through the type checker and the diagnostics come out, and then the
1354 /// compile stops. Build systems use it to ask whether a file compiles without paying for code
1355 /// generation: meson runs its `has_header_symbol` and `has_function` probes this way, and
1356 /// editors run it on every save.
1357 SyntaxOnly,
1358}
1359
1360impl EmitKind {
1361 /// The name used by `--emit=` and by `--print-config`.
1362 pub const fn as_str(self) -> &'static str {
1363 match self {
1364 EmitKind::Executable => "exe",
1365 EmitKind::Object => "obj",
1366 EmitKind::Archive => "archive",
1367 EmitKind::Asm => "asm",
1368 EmitKind::Preprocessed => "preprocessed",
1369 EmitKind::Tast => "tast",
1370 EmitKind::Ir => "ir",
1371 EmitKind::MirFinal => "mir-final",
1372 EmitKind::SafetySummary => "safety-summary",
1373 EmitKind::TypeGranules => "type-granules",
1374 EmitKind::SyntaxOnly => "syntax-only",
1375 }
1376 }
1377}
1378
1379impl FromStr for EmitKind {
1380 type Err = ();
1381
1382 fn from_str(s: &str) -> Result<Self, ()> {
1383 Ok(match s {
1384 "exe" => EmitKind::Executable,
1385 "obj" => EmitKind::Object,
1386 "archive" => EmitKind::Archive,
1387 "asm" => EmitKind::Asm,
1388 "preprocessed" => EmitKind::Preprocessed,
1389 "tast" => EmitKind::Tast,
1390 "ir" => EmitKind::Ir,
1391 "mir-final" => EmitKind::MirFinal,
1392 "safety-summary" => EmitKind::SafetySummary,
1393 "type-granules" => EmitKind::TypeGranules,
1394 "syntax-only" => EmitKind::SyntaxOnly,
1395 _ => return Err(()),
1396 })
1397 }
1398}
1399
1400/// Which C the source is written in.
1401///
1402/// The GNU variants are the same language with `__STRICT_ANSI__` left undefined, so the
1403/// dialect and the extension question are two fields rather than ten variants.
1404#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1405pub enum Std {
1406 /// `-std=c89`, and `-ansi`.
1407 C89,
1408 /// `-std=c99`.
1409 C99,
1410 /// `-std=c11`.
1411 C11,
1412 /// `-std=c17`, which is C11 with the defect reports applied.
1413 C17,
1414 /// `-std=c23`. The default, matching current GCC.
1415 #[default]
1416 C23,
1417 /// `-std=c2y`, the draft after C23, which gcc 16 takes and reports as `202500L`. It is C23
1418 /// with whatever the next standard has added so far, and the one addition anything here reads
1419 /// yet is the four unsigned absolute value functions under their plain names.
1420 C2y,
1421}
1422
1423impl Std {
1424 /// What `__STDC_VERSION__` says, which C89 does not define at all.
1425 pub const fn stdc_version(self) -> Option<&'static str> {
1426 match self {
1427 Std::C89 => None,
1428 Std::C99 => Some("199901L"),
1429 Std::C11 => Some("201112L"),
1430 Std::C17 => Some("201710L"),
1431 Std::C23 => Some("202311L"),
1432 Std::C2y => Some("202500L"),
1433 }
1434 }
1435
1436 /// The name in `-std=`.
1437 pub const fn as_str(self) -> &'static str {
1438 match self {
1439 Std::C89 => "c89",
1440 Std::C99 => "c99",
1441 Std::C11 => "c11",
1442 Std::C17 => "c17",
1443 Std::C23 => "c23",
1444 Std::C2y => "c2y",
1445 }
1446 }
1447
1448 /// Whether this dialect has `_Atomic`, `_Thread_local` and the rest of C11.
1449 pub const fn has_c11(self) -> bool {
1450 matches!(self, Std::C11 | Std::C17 | Std::C23 | Std::C2y)
1451 }
1452
1453 /// Reads a `-std=` argument, and says whether the GNU extensions came with it.
1454 ///
1455 /// Every alias GCC takes is here, including the `iso9899` spellings and the year based
1456 /// ones, because a build system that passes `-std=iso9899:1999` is passing what its
1457 /// author tested against and rejecting it helps nobody. An unknown dialect is `None`
1458 /// rather than a guess, since guessing means compiling a different language than the one
1459 /// asked for.
1460 #[must_use]
1461 pub fn from_flag(name: &str) -> Option<(Std, bool)> {
1462 let gnu = name.starts_with("gnu");
1463 let std = match name {
1464 "c89" | "c90" | "gnu89" | "gnu90" | "iso9899:1990" | "iso9899:199409" => Std::C89,
1465 "c99" | "c9x" | "gnu99" | "gnu9x" | "iso9899:1999" | "iso9899:199x" => Std::C99,
1466 "c11" | "c1x" | "gnu11" | "gnu1x" | "iso9899:2011" => Std::C11,
1467 "c17" | "c18" | "gnu17" | "gnu18" | "iso9899:2017" | "iso9899:2018" => Std::C17,
1468 "c23" | "c2x" | "gnu23" | "gnu2x" => Std::C23,
1469 "c2y" | "gnu2y" => Std::C2y,
1470 _ => return None,
1471 };
1472 Some((std, gnu))
1473 }
1474}
1475
1476/// The GCC release the compiler claims to be, as `__GNUC__`, `__GNUC_MINOR__` and
1477/// `__GNUC_PATCHLEVEL__`.
1478///
1479/// Design: `spec/04-driver-and-cli.md` section 4.5, which makes this a knob rather than a
1480/// constant and says to start conservative and raise it as the matrix in `rucc-gnu` fills in.
1481///
1482/// The default is sixteen, which is the release this compiler is written against. glibc gates
1483/// most of what it hands a caller on `__GNUC_PREREQ`, so the claim decides which half of
1484/// `sys/cdefs.h` we get, and a project does the same thing to itself: it asks what compiler this
1485/// is and writes different code depending on the answer. The claim is therefore not a boast, it
1486/// is the sentence that selects which of a program's own branches gets compiled, and claiming an
1487/// old release means compiling the code that release needed rather than the code this compiler
1488/// wants.
1489///
1490/// It stood at seven for a long time, and seven was the right number then. Below seven
1491/// `bits/floatn-common.h` writes `typedef float _Float32;` over a keyword this compiler already
1492/// has and every header that reaches it stops there, so moving from 4.2.1 to seven took Ubuntu
1493/// 24.04's glibc 2.39 from 180 of 214 headers to 202 and took the amalgamated sqlite from four
1494/// errors to none. What kept it at seven after that was that nothing needed more, and claiming a
1495/// version whose promises have not been kept means being handed syntax the compiler cannot parse.
1496///
1497/// What needed more was micropython. Its `py/nlrx64.c` asks for `__GNUC__ >= 8` before it writes
1498/// `__attribute__((naked))`, and at seven it took the gcc 7 path instead and handed this compiler
1499/// an ordinary function ending in a bare `jmp`, which is refused and ought to be. The naked
1500/// function it writes at eight and above compiles here, byte for byte what gcc 16 emits, and had
1501/// compiled for a week without micropython ever reaching it.
1502///
1503/// The measurement that moved it is the real corpus at the rung the claim could break: fifty six
1504/// projects across rungs zero through three, built at `-O2` with the claim at seven and again at
1505/// sixteen, on gcc 16.0.1 and glibc. Fifty of fifty six passed both times, and it was the same
1506/// fifty both times, with the same four not passing for the same four reasons. Nothing regressed
1507/// and nothing started working by accident. Thirteen and sixteen had already been measured
1508/// identical to seven on glibc, on the macOS SDK and on sqlite when seven was chosen, so this
1509/// confirms on real builds what the header sweep said.
1510///
1511/// Sixteen point zero rather than the point release on any particular machine, because
1512/// `__GNUC_PREREQ(16, 1)` is a promise about a specific release and the honest claim is the
1513/// earliest one in the series whose promises this compiler means to keep.
1514#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1515pub struct GnucVersion {
1516 /// `__GNUC__`.
1517 pub major: u32,
1518 /// `__GNUC_MINOR__`.
1519 pub minor: u32,
1520 /// `__GNUC_PATCHLEVEL__`.
1521 pub patch: u32,
1522}
1523
1524impl Default for GnucVersion {
1525 fn default() -> GnucVersion {
1526 GnucVersion { major: 16, minor: 0, patch: 0 }
1527 }
1528}
1529
1530impl FromStr for GnucVersion {
1531 type Err = String;
1532
1533 /// Reads `-fgnuc-version=`, which is `15`, `15.1` or `15.1.0`.
1534 ///
1535 /// The short forms are not a convenience, they are what people write. A missing component
1536 /// is zero, the same way GCC treats a release with no patchlevel.
1537 fn from_str(text: &str) -> Result<GnucVersion, String> {
1538 let mut parts = text.split('.');
1539 let mut next = |what: &str| -> Result<u32, String> {
1540 match parts.next() {
1541 None => Ok(0),
1542 Some(field) => {
1543 field.parse().map_err(|_| format!("`{text}` has a {what} that is not a number"))
1544 }
1545 }
1546 };
1547 let major = next("major")?;
1548 let minor = next("minor")?;
1549 let patch = next("patchlevel")?;
1550 if parts.next().is_some() {
1551 return Err(format!("`{text}` has more than three components"));
1552 }
1553 Ok(GnucVersion { major, minor, patch })
1554 }
1555}
1556
1557impl GnucVersion {
1558 /// The dialect that GCC release compiles when the command line has no `-std=`.
1559 ///
1560 /// A build that claims an old GCC is usually an old tree, and an old tree that passes no
1561 /// `-std=` was written against that release's default rather than ours. Kernels up to 3.17 are
1562 /// the example that matters: they rely on gnu89, and under gnu23 their own identifiers meet
1563 /// keywords. GCC 5 moved the default to gnu11, GCC 8 to gnu17 and GCC 15 to gnu23. Every one of
1564 /// these is a GNU dialect, so the extensions stay on. `spec/04-driver-and-cli.md` section 4.6.
1565 #[must_use]
1566 pub const fn default_std(self) -> Std {
1567 match self.major {
1568 0..=4 => Std::C89,
1569 5..=7 => Std::C11,
1570 8..=14 => Std::C17,
1571 _ => Std::C23,
1572 }
1573 }
1574
1575 /// Whether that GCC release put a tentative definition in a common symbol when the command
1576 /// line did not say, which every release before 10 did.
1577 #[must_use]
1578 pub const fn common_by_default(self) -> bool {
1579 self.major < 10
1580 }
1581
1582 /// What `-dumpversion` prints for that release.
1583 ///
1584 /// Before GCC 7 it was the whole version, and there was nothing else to ask. GCC 7 added
1585 /// `-dumpfullversion` for that and, built the way the distributions build it, prints only the
1586 /// major number for `-dumpversion`, which is the shape scripts written since then expect.
1587 #[must_use]
1588 pub fn dumpversion(self) -> String {
1589 if self.major < 7 { self.to_string() } else { self.major.to_string() }
1590 }
1591}
1592
1593impl fmt::Display for GnucVersion {
1594 /// All three numbers, which is what `-dumpfullversion` prints and what ends GCC's banner.
1595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1596 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
1597 }
1598}
1599
1600/// The GNU assembler release claimed, which is the number at the end of what `-Wa,--version`
1601/// prints.
1602///
1603/// This compiler has no separate assembler, but build systems ask for one's version all the same.
1604/// The Linux kernel's `scripts/as-version.sh` runs `$(CC) -Wa,--version` and stops the build with
1605/// "unknown assembler invoked" unless the first line starts with `GNU assembler` and ends with a
1606/// version, which Kconfig then compares against the binutils release a feature needs. What does the
1607/// assembling is `rucc-asm`, so the claim is about which gas this is meant to stand in for.
1608///
1609/// 2.46 by default, which is the binutils release that was current when GCC 16 came out, and GCC 16
1610/// is what [`GnucVersion`] claims by default. A build that claims an older GCC with
1611/// `-fgnuc-version=` will usually want an older assembler too, and `-fgnu-as-version=` says so.
1612/// `spec/04-driver-and-cli.md` section 4.9 has the rest.
1613#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1614pub struct GasVersion {
1615 /// The major number, which is 2 for every binutils release a build is likely to ask about.
1616 pub major: u32,
1617 /// The minor number, which is what moves from one binutils release to the next.
1618 pub minor: u32,
1619 /// The point release, which binutils rarely has and prints only when it is not zero.
1620 pub patch: u32,
1621}
1622
1623impl Default for GasVersion {
1624 fn default() -> GasVersion {
1625 GasVersion { major: 2, minor: 46, patch: 0 }
1626 }
1627}
1628
1629impl FromStr for GasVersion {
1630 type Err = String;
1631
1632 /// Reads `-fgnu-as-version=`, which is `2`, `2.44` or `2.44.1`, the same shapes
1633 /// `-fgnuc-version=` takes.
1634 fn from_str(text: &str) -> Result<GasVersion, String> {
1635 let GnucVersion { major, minor, patch } = text.parse()?;
1636 Ok(GasVersion { major, minor, patch })
1637 }
1638}
1639
1640impl fmt::Display for GasVersion {
1641 /// The way gas prints its own: `2.44` for a release and `2.44.1` for a point release.
1642 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1643 write!(f, "{}.{}", self.major, self.minor)?;
1644 if self.patch != 0 {
1645 write!(f, ".{}", self.patch)?;
1646 }
1647 Ok(())
1648 }
1649}
1650
1651/// The MSVC release an MSVC row claims, which is `_MSC_VER` and `_MSC_FULL_VER`.
1652///
1653/// 19.40 is Visual Studio 2022 17.10, which is the release the design names and the one whose
1654/// SDK headers this was written against. The build number is zero unless one is given, which
1655/// makes `_MSC_FULL_VER` 194000000, and that is what clang says for `-fms-compatibility-version=19.40`.
1656#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1657pub struct MscVersion {
1658 /// The compiler's major version, 19 for every release since Visual Studio 2015.
1659 pub major: u32,
1660 /// The minor version, which is what moves from one Visual Studio update to the next.
1661 pub minor: u32,
1662 /// The build number, which only `_MSC_FULL_VER` says.
1663 pub build: u32,
1664}
1665
1666impl MscVersion {
1667 /// `_MSC_VER`, which is the major and minor versions run together: 1940.
1668 #[must_use]
1669 pub const fn msc_ver(self) -> u32 {
1670 self.major * 100 + self.minor
1671 }
1672
1673 /// `_MSC_FULL_VER`, which adds five digits of build number: 194033811 for 19.40.33811.
1674 #[must_use]
1675 pub const fn msc_full_ver(self) -> u64 {
1676 self.major as u64 * 10_000_000 + self.minor as u64 * 100_000 + self.build as u64
1677 }
1678}
1679
1680impl Default for MscVersion {
1681 fn default() -> MscVersion {
1682 MscVersion { major: 19, minor: 40, build: 0 }
1683 }
1684}
1685
1686impl FromStr for MscVersion {
1687 type Err = String;
1688
1689 /// Reads `-fms-compatibility-version=`, which is `19`, `19.40` or `19.40.33811`.
1690 ///
1691 /// A fourth component is taken and dropped, since clang takes one and no macro says it.
1692 fn from_str(text: &str) -> Result<MscVersion, String> {
1693 let fields: Vec<&str> = text.split('.').collect();
1694 if fields.len() > 4 {
1695 return Err(format!("`{text}` has more than four components"));
1696 }
1697 let field = |n: usize, what: &str| -> Result<u32, String> {
1698 match fields.get(n) {
1699 None => Ok(0),
1700 Some(field) => {
1701 field.parse().map_err(|_| format!("`{text}` has a {what} that is not a number"))
1702 }
1703 }
1704 };
1705 let version = MscVersion {
1706 major: field(0, "major")?,
1707 minor: field(1, "minor")?,
1708 build: field(2, "build")?,
1709 };
1710 field(3, "revision")?;
1711 if version.minor > 99 || version.build > 99_999 {
1712 return Err(format!("`{text}` does not fit in `_MSC_FULL_VER`"));
1713 }
1714 Ok(version)
1715 }
1716}
1717
1718/// What the `-d` family asks to be dumped alongside, or instead of, the preprocessed output.
1719///
1720/// Design: `spec/04-driver-and-cli.md` section 4.4.
1721///
1722/// GCC spells these as letters packed into one flag, so `-dDI` is two of them, and a letter it
1723/// does not know is ignored rather than rejected. That last part is deliberate on GCC's side
1724/// and worth copying: the family is a debugging aid and a build that passes `-dumpbase` should
1725/// not die on the `-d`.
1726#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1727pub struct Dumps {
1728 /// `-dM`. Print the macros that are defined at the end, and nothing else.
1729 pub macros: bool,
1730}
1731
1732impl Dumps {
1733 /// The letters GCC's preprocessor takes after `-d`.
1734 ///
1735 /// `M` is the macros, `D` is the macros in place, `N` is their names only, `I` is the
1736 /// `#include` lines and `U` is the macros as they are used. Only `M` does anything so far.
1737 const LETTERS: &'static str = "MDNIU";
1738
1739 /// Whether `arg` is a flag from this family rather than something else beginning with
1740 /// `-d`.
1741 ///
1742 /// The check is here rather than in the driver so that the set of letters and the set of
1743 /// flags accepted cannot drift apart. It matters because `-dumpversion` also begins with
1744 /// `-d`, and a family that swallowed every such flag would turn a flag we have not written
1745 /// into a dump of nothing.
1746 #[must_use]
1747 pub fn is_family(arg: &str) -> bool {
1748 match arg.strip_prefix("-d") {
1749 Some("") | None => false,
1750 Some(letters) => letters.chars().all(|c| Dumps::LETTERS.contains(c)),
1751 }
1752 }
1753
1754 /// Reads the letters after `-d`, ignoring the ones we do not implement yet.
1755 pub fn add(&mut self, letters: &str) {
1756 for letter in letters.chars() {
1757 if letter == 'M' {
1758 self.macros = true;
1759 }
1760 }
1761 }
1762
1763 /// Whether anything at all was asked for.
1764 #[must_use]
1765 pub const fn any(self) -> bool {
1766 self.macros
1767 }
1768}
1769
1770/// A file `-imacros` or `-include` named, read before the source file.
1771///
1772/// Design: `spec/04-driver-and-cli.md` section 4.4.
1773///
1774/// The flag a build reaches for when a whole tree has to see a definition that is not in any of
1775/// its files. The kernel builds every object with `-include` of its own configuration header, and
1776/// a configure script that has produced a `config.h` gets it into a third party source tree the
1777/// same way, without a patch.
1778#[derive(Debug, Clone, PartialEq, Eq)]
1779pub struct Preinclude {
1780 /// The name as it was written, which is looked for the way a quoted include is looked for.
1781 pub name: String,
1782 /// Whether only the definitions it makes are wanted, which is what `-imacros` asks for.
1783 ///
1784 /// The text of an `-imacros` file is read and thrown away, so a header full of declarations
1785 /// contributes its macros and nothing else. That is what makes it usable on a file that has
1786 /// already been included by the source: the definitions arrive early and the declarations do
1787 /// not arrive twice.
1788 pub macros_only: bool,
1789}
1790
1791/// What the `-M` family asks for, which is a make rule saying what a source file was built from.
1792///
1793/// Design: `spec/04-driver-and-cli.md` section 4.4.
1794///
1795/// This is a compiler flag rather than a separate tool because the answer is the set of files the
1796/// preprocessor opened, and nothing outside the preprocessor knows what that was. A build system
1797/// that generates its own makefiles asks for it on every compilation, which is why section 4.4
1798/// calls the family required rather than convenient.
1799#[derive(Debug, Clone, PartialEq, Eq)]
1800pub struct Deps {
1801 /// Whether a rule is produced at all, which is any of `-M`, `-MM`, `-MD` and `-MMD`.
1802 pub emit: bool,
1803 /// Whether the rule is produced instead of compiling, which is `-M` and `-MM` and not the
1804 /// two that end in `D`.
1805 ///
1806 /// The split is GCC's and it is about who reads the answer. The two that stop after the rule
1807 /// write it to standard output for a person, and the two that do not write it to a file
1808 /// beside the object for `make` to include on the next run.
1809 pub instead_of_compiling: bool,
1810 /// Whether a header found in a system directory is listed, which `-MM` and `-MMD` turn off.
1811 ///
1812 /// A build that lists them is a build that rebuilds the world when the C library is updated,
1813 /// which is either what somebody wanted or the reason they reached for the other spelling.
1814 ///
1815 /// On unless a flag turned it off, and nothing turns it back on. That is GCC's behaviour and
1816 /// not an oversight: `-MM -M` leaves the system headers out, because the flag that asks for
1817 /// fewer of them is read as the answer to a question the other one never asked.
1818 pub system_headers: bool,
1819 /// Where the rule is written, from `-MF`, with `-` meaning standard output.
1820 ///
1821 /// `None` is the default, which is standard output when the rule replaces the compilation and
1822 /// the output file with a `.d` suffix when it does not.
1823 pub file: Option<String>,
1824 /// What the rule's targets are, from `-MT` and `-MQ`, in the order they were given.
1825 ///
1826 /// Already escaped, because that is the whole of the difference between the two flags: `-MQ`
1827 /// escapes what it is given and `-MT` writes it through untouched. Empty means the target is
1828 /// worked out from the output file, which is what a build that passes neither expects.
1829 pub targets: Vec<String>,
1830 /// Whether every prerequisite except the source gets a target of its own with no recipe,
1831 /// from `-MP`.
1832 ///
1833 /// This is what stops `make` failing outright when a header is deleted. Without it the old
1834 /// rule names a file that is gone and no rule makes it, and the build stops on a header that
1835 /// nothing needs any more.
1836 pub phony: bool,
1837}
1838
1839impl Default for Deps {
1840 fn default() -> Deps {
1841 Deps {
1842 emit: false,
1843 instead_of_compiling: false,
1844 system_headers: true,
1845 file: None,
1846 targets: Vec::new(),
1847 phony: false,
1848 }
1849 }
1850}
1851
1852/// Whether `-save-temps` was given and where it puts the files it keeps.
1853///
1854/// Design: `spec/04-driver-and-cli.md` section 4.10.
1855///
1856/// The flag is how a build gets at the preprocessed source of the file that failed without running
1857/// the compiler a second time under different flags, which is the one way to be sure the text being
1858/// read is the text that was compiled. A bug report against a compiler is usually a preprocessed
1859/// file and nothing else, and this is where that file comes from.
1860#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1861pub enum SaveTemps {
1862 /// Not asked for, and nothing is kept.
1863 #[default]
1864 No,
1865 /// Beside the file the compilation produced, which is `-save-temps=obj`.
1866 ///
1867 /// This is what the bare `-save-temps` does as well. GCC's manual says the bare spelling is
1868 /// `-save-temps=cwd`, and gcc 16 does not do that: `-save-temps -c a.c -o out/a.o` leaves
1869 /// `out/a.i` and `out/a.s` rather than `a.i` and `a.s`. The measurement is what is followed
1870 /// here, because a build that reads the manual and a build that reads the compiler both end up
1871 /// looking for the files where the compiler put them.
1872 Object,
1873 /// In the working directory, which is `-save-temps=cwd`.
1874 Cwd,
1875}
1876
1877impl SaveTemps {
1878 /// Whether anything is kept at all.
1879 #[must_use]
1880 pub const fn wanted(self) -> bool {
1881 !matches!(self, SaveTemps::No)
1882 }
1883}
1884
1885impl FromStr for SaveTemps {
1886 type Err = String;
1887
1888 /// Reads what came after the `=`, which is the only part that varies.
1889 ///
1890 /// # Errors
1891 ///
1892 /// Returns the offending word. GCC treats an unknown one as fatal rather than ignoring it,
1893 /// which is right: a misspelled keyword here means the files a person went looking for are not
1894 /// written and nothing said so.
1895 fn from_str(s: &str) -> Result<SaveTemps, String> {
1896 match s {
1897 "obj" => Ok(SaveTemps::Object),
1898 "cwd" => Ok(SaveTemps::Cwd),
1899 _ => Err(format!("`{s}` is not a -save-temps option; accepted: cwd, obj")),
1900 }
1901 }
1902}
1903
1904/// The members of `-ffast-math` that are licences about what an arithmetic may answer, one field
1905/// each, so that a build writing `-ffast-math -fno-finite-math-only` gets what gcc gives it.
1906///
1907/// Nothing here folds floating point arithmetic in a function body at any level, so none of these
1908/// changes the code this compiler writes. What each one does change is the predefined set: gcc
1909/// names each licence it was given with a macro of its own, `<math.h>` and a numerics library read
1910/// those, and a header that configured itself for a licence the other objects were built without is
1911/// a program answering two ways. `-ftrapping-math` is the sixth member and lives in
1912/// [`Options::trapping_math`], because it was taken before the rest and it is the one that does
1913/// change an answer here.
1914#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1915pub struct Math {
1916 /// Whether a function in the maths library is taken to set `errno`, from `-fmath-errno`. On,
1917 /// which is gcc's default on a target whose library does it.
1918 pub errno: bool,
1919 /// Whether the program promises there are no NaNs and no infinities, from
1920 /// `-ffinite-math-only`.
1921 pub finite_only: bool,
1922 /// Whether the sign of a zero is kept, from `-fsigned-zeros`. On by default.
1923 pub signed_zeros: bool,
1924 /// Whether a division may become a multiplication by the reciprocal, from
1925 /// `-freciprocal-math`.
1926 pub reciprocal: bool,
1927 /// Whether an addition may be regrouped, from `-fassociative-math`. gcc drops this with a
1928 /// warning unless signed zeros and trapping are both off, so what counts is
1929 /// [`Math::associative`] rather than the field.
1930 pub associative: bool,
1931 /// `-funsafe-math-optimizations`, which is its own flag as well as the three it turns on.
1932 pub unsafe_math: bool,
1933}
1934
1935impl Default for Math {
1936 fn default() -> Math {
1937 Math {
1938 errno: true,
1939 finite_only: false,
1940 signed_zeros: true,
1941 reciprocal: false,
1942 associative: false,
1943 unsafe_math: false,
1944 }
1945 }
1946}
1947
1948impl Math {
1949 /// `-funsafe-math-optimizations` and its negative, which set or clear the members gcc's
1950 /// `set_unsafe_math_optimizations_flags` does. Trapping is one of them, so it is handed back
1951 /// for the caller to store where it lives.
1952 pub fn set_unsafe(&mut self, on: bool) -> bool {
1953 self.unsafe_math = on;
1954 self.associative = on;
1955 self.reciprocal = on;
1956 self.signed_zeros = !on;
1957 !on
1958 }
1959
1960 /// `-ffast-math` and `-fno-fast-math`, which are `set_fast_math_flags` in gcc: the unsafe
1961 /// group, the maths library's `errno` and the promise about NaNs. The value handed back is
1962 /// trapping, as above.
1963 pub fn set_fast(&mut self, on: bool) -> bool {
1964 self.errno = !on;
1965 self.finite_only = on;
1966 self.set_unsafe(on)
1967 }
1968
1969 /// Whether regrouping survives, which it does only where nothing could tell: a regrouped sum
1970 /// can move a zero's sign and can raise an exception the original order did not.
1971 #[must_use]
1972 pub fn associative(&self, trapping: bool) -> bool {
1973 self.associative && !self.signed_zeros && !trapping
1974 }
1975
1976 /// Whether every member is in the permissive position, which is what `__FAST_MATH__` says.
1977 /// Written out rather than remembered from the flag, because `-ffast-math -ftrapping-math` is
1978 /// not fast math and gcc does not define the macro for it.
1979 #[must_use]
1980 pub fn fast(&self, trapping: bool) -> bool {
1981 !trapping && self.unsafe_math && self.finite_only && !self.signed_zeros && !self.errno
1982 }
1983
1984 /// Whether the arithmetic is still IEC 60559's, which is what `__GCC_IEC_559` answers and what
1985 /// glibc writes `__STDC_IEC_559__` from. Any of these licences is an answer the standard does
1986 /// not give.
1987 #[must_use]
1988 pub fn iec_559(&self, trapping: bool) -> bool {
1989 !(self.unsafe_math
1990 || self.associative(trapping)
1991 || self.reciprocal
1992 || self.finite_only
1993 || !self.signed_zeros)
1994 }
1995}
1996
1997/// Everything a compilation was asked to do.
1998///
1999/// Options are a plain value with no interior mutability, so a caller can build one, clone
2000/// it, tweak one field and run a second compilation, which is exactly what the differential
2001/// testing in `spec/15-testing.md` needs.
2002#[derive(Debug, Clone, PartialEq, Eq)]
2003#[non_exhaustive]
2004pub struct Options {
2005 /// The target to generate code for.
2006 pub target: Triple,
2007 /// The optimisation level.
2008 pub opt_level: OptLevel,
2009 /// How much of the memory safety monitor is on, from `-fsafety=`.
2010 ///
2011 /// Off unless it was asked for. A program built without the flag is compiled by exactly the
2012 /// pipeline it was compiled by before the monitor existed, which is the only way the feature
2013 /// can be developed in the open without every build paying for it.
2014 pub safety: Safety,
2015 /// Whether padding participates in the init plane, from `-fsafety-init=`.
2016 ///
2017 /// Means nothing unless `safety` asked for a tier. The default is the one section 9.3 gives
2018 /// library code, which is that it does not, so a record filled a member at a time is not
2019 /// reported when something later reads it whole.
2020 pub padding: Padding,
2021 /// Whether an access has to stay inside the member it names, from `-fsafety-subobject`.
2022 ///
2023 /// Means nothing unless `safety` asked for a tier. Off by default, which section 9.4 argues
2024 /// for: this is the row most likely to fire on code that is doing what its author meant.
2025 pub subobject: Subobject,
2026 /// Whether the `restrict` contract is checked, from `-fsafety-restrict`.
2027 ///
2028 /// Means nothing unless `safety` asked for a tier. Off by default, which section 9.6 argues
2029 /// for: the cost lands entirely inside the loops `restrict` is written for.
2030 pub promise: Promise,
2031 /// Whether pointer races are watched, from `-fsafety-races=`.
2032 ///
2033 /// Means nothing unless `safety` asked for a tier. Off by default, and [`Races`] says why that
2034 /// one is not a cost argument like the others.
2035 pub races: Races,
2036 /// What to produce.
2037 pub emit: EmitKind,
2038 /// Whether to emit debug information.
2039 pub debug_info: bool,
2040 /// The version of DWARF that debug information is written in, which is 5 unless `-gdwarf-4`
2041 /// asked for 4. Only 4 and 5 are ever here.
2042 pub dwarf_version: u8,
2043 /// The directory the compiler ran in, which is what `DW_AT_comp_dir` says.
2044 ///
2045 /// A debugger joins it onto every file name in the line table that is relative, and the names
2046 /// in there are the ones the command line gave, so a build invoked as `rucc -g a/b.c` produces
2047 /// nothing a debugger can open without it. It is asked of the process by the driver rather than
2048 /// read here, so that a caller that is not a command line gets to say what it was and so that a
2049 /// test does not depend on where it was run from. [`None`] when the process could not say, which
2050 /// is written out as a single dot.
2051 pub working_dir: Option<String>,
2052 /// How the debug sections are compressed, from `-gz`.
2053 ///
2054 /// Nothing reads this yet, because nothing compresses a debug section yet. There are sections
2055 /// to compress now, so what this is waiting on is the compressor rather than the producer.
2056 pub compress: Compress,
2057 /// What the `-flto` family asked for, which nothing does yet.
2058 pub lto: Lto,
2059 /// What the profile reading half of the `-fprofile` family asked for, which nothing reads yet.
2060 ///
2061 /// Named for the data rather than for the flag, because `profile` next door is already the
2062 /// answer to whether `-pg` asked for a call to a profiler on the way into every function, and
2063 /// the two are different questions about the same word.
2064 pub profile_data: Profile,
2065 /// Whether every function keeps a frame pointer, from `-fno-omit-frame-pointer` and
2066 /// `-fomit-frame-pointer`.
2067 ///
2068 /// `None` is a command line that said neither, and then the level decides: kept at `-O0` and
2069 /// left out above it, which is what gcc does and what leaves the register free for the
2070 /// allocator once there is an allocator worth leaving it to. A profiler that walks the stack
2071 /// by following saved frame pointers needs it on, and so does any code a debugger has to
2072 /// unwind without unwind tables. Read it through `keeps_frame_pointer`.
2073 pub frame_pointer: Option<bool>,
2074 /// Whether the red zone may be used, from `-mno-red-zone` turned around.
2075 ///
2076 /// The 128 bytes below the stack pointer that the System V psABI promises no signal handler
2077 /// will touch, which lets a small leaf function keep its locals without moving the stack
2078 /// pointer at all. A kernel turns this off, because an interrupt taken on the kernel stack
2079 /// makes the promise false, and every kernel build in the wild passes `-mno-red-zone` for
2080 /// exactly that reason. A convention without a red zone ignores this.
2081 pub red_zone: bool,
2082 /// Where the code and static data are promised to be, which is `-mcmodel=`. The kernel model is
2083 /// x86-64 ELF only and only beside `-fno-pic`, which the driver checks. tamnd/rucc#2275.
2084 pub code_model: CodeModel,
2085 /// The boundary in bytes the stack pointer is kept on at every call, from
2086 /// `-mpreferred-stack-boundary=`, or `None` for the convention's own.
2087 ///
2088 /// The x86-64 kernel passes 3, which is eight bytes, because interrupt entry does not align the
2089 /// stack and the psABI's sixteen cannot be counted on there. A function is then entitled to
2090 /// no more than this on entry and owes no more than this at its calls, and a local that asks
2091 /// for more is aligned by the prologue behind a frame pointer, as gcc does.
2092 pub stack_boundary: Option<u32>,
2093 /// Whether a value may be kept in a vector register, which `-mno-sse` on x86-64 and
2094 /// `-mgeneral-regs-only` on either machine turn off. A kernel turns them off so that entering
2095 /// it does not mean saving them, and a function with a `float` in it is then refused.
2096 pub vector: bool,
2097 /// Whether a value may be kept on the x87 stack, which `-mno-80387` and `-msoft-float` turn
2098 /// off. That is the eighty bit `long double`.
2099 pub x87: bool,
2100 /// Whether a `long double` may be returned on the x87 stack, which `-mno-fp-ret-in-387` turns
2101 /// off while leaving the stack itself on. A function returning one is then refused, which is
2102 /// what gcc does.
2103 pub x87_return: bool,
2104 /// Where the stack protector's canary is copied from when the command line moved it, from the
2105 /// `-mstack-protector-guard` flags. `None` is the target's own place, which is `%fs:40` on
2106 /// x86-64 Linux. A kernel moves it to its per CPU block behind `%gs`.
2107 pub guard: Option<rucc_target::Guard>,
2108 /// Which registers every `ret` clears first, from `-fzero-call-used-regs=`. `None` is `skip`,
2109 /// and otherwise the first is whether it is every register a call may clobber rather than the
2110 /// ones the function used, the second is whether it is only the argument registers, and the
2111 /// third is whether the vector registers and the x87 stack are cleared as well as the general
2112 /// purpose ones, which is the choices without `-gpr` in them.
2113 pub zero_regs: Option<(bool, bool, bool)>,
2114 /// Which extensions of the instruction set the unit is built for, from `-march=` and the `-m`
2115 /// flags that name one, such as `-msse4.2`.
2116 ///
2117 /// What it decides today is the macros, `__SSE4_2__` and the rest, which is how a header or a
2118 /// configure probe finds out, and which functions a function built for less may call without
2119 /// saying so. The code generator reads it for the bit counts, which are `popcnt`, `lzcnt` and
2120 /// `tzcnt` where this has them, and emits the baseline for everything else, so a unit built
2121 /// for more is slower than it could be rather than wrong. On AArch64 it is
2122 /// whether `-march=` gave the CRC32 extension, which `__ARM_FEATURE_CRC32` and
2123 /// `<arm_acle.h>` follow, and every other target's is [`Isa::NONE`]. See `rucc_target::isa`.
2124 pub isa: Isa,
2125 /// Whether the blocks of a function are put in the order their weights say rather than in the
2126 /// order the shape of the graph gives, from `-freorder-blocks` and `-fno-reorder-blocks`.
2127 ///
2128 /// `None` is a command line that said neither, which is nearly every one, and then the level
2129 /// decides: on above `-O0`, which is where gcc turns it on. It is a three way answer rather
2130 /// than a `bool` because `-O2 -fno-reorder-blocks` and `-O0` have to be different things and
2131 /// a `bool` set from the level could not tell them apart.
2132 pub reorder_blocks: Option<bool>,
2133 /// Whether the blocks that are not expected to run go in a part of the function of their own
2134 /// in `.text.unlikely`, from `-freorder-blocks-and-partition` and its `-fno-` form. `None` is
2135 /// a command line that said neither, and then it is on at `-O2` and `-O3`, which is where gcc
2136 /// turns it on for x86.
2137 pub partition_blocks: Option<bool>,
2138 /// Whether a function written `cold` goes in `.text.unlikely` rather than `.text`, from
2139 /// `-freorder-functions` and its `-fno-` form. `None` is a command line that said neither, and
2140 /// then it is on from `-O2`, the size levels included, which is where gcc turns it on.
2141 pub reorder_functions: Option<bool>,
2142 /// Whether the instructions of a block are put in the order the machine finishes soonest, from
2143 /// `-fschedule-insns2` and `-fno-schedule-insns2`.
2144 ///
2145 /// `None` is a command line that said neither, and then the level decides: on from `-O2`,
2146 /// which is where gcc turns it on. Three way rather than a `bool` for the reason
2147 /// `reorder_blocks` above is.
2148 ///
2149 /// gcc's name, and gcc's `2` in it, which is the one that runs after the registers are handed
2150 /// out. `-fschedule-insns` without it is the pass before allocation, which rucc does not have:
2151 /// `spec/optimizer/38-scheduling-and-layout.md` section 38.6 decides on one scheduler and puts
2152 /// it after allocation, and section 38.8 owes the measurement that would justify a second.
2153 pub schedule_insns: Option<bool>,
2154 /// Whether a call in tail position becomes a jump, from `-foptimize-sibling-calls` and
2155 /// `-fno-optimize-sibling-calls`.
2156 ///
2157 /// `None` is a command line that said neither, and then the level decides: on from `-O2` and at
2158 /// `-Os`, which is where gcc turns it on. Three way rather than a `bool` for the reason
2159 /// `reorder_blocks` above is.
2160 pub sibling_calls: Option<bool>,
2161 /// Whether a hot loop that fits in a 64 byte line is padded so that it does not cross one,
2162 /// when that costs at most 31 bytes, from `-falign-loops` and `-fno-align-loops`.
2163 ///
2164 /// `None` is a command line that said neither, and then it is off at every level. gcc turns its
2165 /// own rule on at `-O2` and `-O3`, and rucc does not, because what it buys is a machine's and
2166 /// not every machine's: tamnd/rucc#1838 measured 18% on a loop on AMD EPYC and nothing on the
2167 /// same loop on an Intel Core, for about half a percent of text. Three way rather than a `bool` for the reason
2168 /// `reorder_blocks` above is, so that the day a level turns it on, `-fno-align-loops` still
2169 /// means something.
2170 pub align_loops: Option<bool>,
2171 /// Whether the target's timing model is believed about the machine's units as well as about
2172 /// its latencies, from `-Zcycle-accurate-model=`.
2173 ///
2174 /// `None` is a command line that said neither, and then the model's own answer decides. gcc
2175 /// spells this `--param=cycle-accurate-model=`, `Init(1)`, and describes it as whether the
2176 /// scheduling description "is mostly a cycle-accurate model of the target processor". No model
2177 /// in this compiler is, every one of them says so, and this is how a person measuring the cost
2178 /// of that can compile the same program both ways.
2179 pub cycle_accurate_model: Option<bool>,
2180 /// Whether the backtracking register allocator decides where the values go rather than the
2181 /// single pass one, from `-Zregalloc=backtracking` and `-Zregalloc=single`.
2182 ///
2183 /// `None` is a command line that said neither, and then the level decides.
2184 pub backtracking: Option<bool>,
2185 /// Whether two things in a frame that are never both wanted may be the same bytes, from
2186 /// `-fstack-reuse=`.
2187 ///
2188 /// `None` is a command line that did not write the flag, and then the level decides: on above
2189 /// `-O0`, off at it, so that a person stepping through unoptimized code sees every local in a
2190 /// place of its own. Three way rather than a `bool` for the reason `reorder_blocks` above is,
2191 /// which is that `-O2 -fstack-reuse=none` and `-O0` have to be different things.
2192 ///
2193 /// gcc's flag takes `all`, `named_vars` or `none`. The first two are the same answer here: what
2194 /// rucc shares is a local whose address provably stays inside the function, or one declared in a
2195 /// block whose every way out the front end could see and mark, which is narrower than either of
2196 /// gcc's and is contained in both. The second kind is tamnd/rucc#2201.
2197 pub stack_reuse: Option<bool>,
2198 /// Which functions get a stack protector, from the `-fstack-protector` family.
2199 pub protector: Protector,
2200 /// Whether a prologue takes its frame a page at a time, from `-fstack-clash-protection`.
2201 ///
2202 /// An operating system leaves one page unmapped below every stack so that a stack growing
2203 /// into it faults. A function whose frame is larger than that page moves the stack pointer
2204 /// clean over it in one subtraction and can then write below it, into whatever the program
2205 /// mapped next, which is a way of reaching one allocation from another that costs an attacker
2206 /// nothing but a large local array. A prologue that takes the frame a page at a time and
2207 /// writes to each page as it arrives faults on the first one that is not there.
2208 ///
2209 /// Off by default, which is gcc's default. Distributions that build with it build everything
2210 /// with it, because the hole is in whichever function was left out.
2211 pub stack_clash: bool,
2212 /// Which control flow transfers are checked, from `-fcf-protection=`.
2213 ///
2214 /// See [`Control`]. Off by default, which is gcc's default on these targets, and on again in
2215 /// every distribution's global flags for the same reason the stack protector is.
2216 pub control: Control,
2217 /// Which indirect branches and returns are rewritten against speculative execution, from
2218 /// `-mindirect-branch=`, `-mindirect-branch-cs-prefix`, `-mfunction-return=` and
2219 /// `-mharden-sls=`. x86-64 only, which the driver checks. tamnd/rucc#2280.
2220 ///
2221 /// All off by default, which is gcc's default. The kernel turns every one of them on when it is
2222 /// configured with the mitigations, and links in the thunks the rewritten branches go to.
2223 pub speculation: Speculation,
2224 /// Whether a `switch` may become a jump table, from `-fjump-tables` and `-fno-jump-tables`.
2225 ///
2226 /// On by default. The kernel turns it off beside the thunks, because a jump through a table is
2227 /// an indirect branch and a table is read with no thunk in the way.
2228 pub jump_tables: bool,
2229 /// Whether the machine has a conditional move, which every x86-64 has and an i386 has from
2230 /// the Pentium Pro on. `-march=i486`, `-march=i586`, `-march=k6` and the rest of what came
2231 /// before it turn it off, and then a select is a branch, as gcc writes it.
2232 pub cmov: bool,
2233 /// What an automatic object with no initializer starts out holding, from
2234 /// `-ftrivial-auto-var-init=`. `None` is `uninitialized`, which is the default, and otherwise
2235 /// the byte every byte of it is, zero for `zero` and `0xfe` for `pattern`.
2236 pub auto_var_init: Option<u8>,
2237 /// Whether every function calls a profiler's hook on the way in, from `-pg` and `-p`.
2238 ///
2239 /// A profiler wants a count of which function called which, and the moment a function is
2240 /// entered is the only place a compiler can hand it one. It changes the link as well as the
2241 /// code, since the counts have to be started before `main` and written out after it, and the
2242 /// start file that does that is a different one.
2243 ///
2244 /// A tracer wants the same call for a different reason. The hook is one instruction the kernel
2245 /// can overwrite while the program runs, which is what makes a function traceable without
2246 /// rebuilding it, and it is why Linux is built this way rather than to be profiled.
2247 pub profile: bool,
2248 /// Where that call goes, from `-mfentry` and `-mno-fentry`.
2249 ///
2250 /// See [`Hook`]. Read even on a command line that did not ask for the call, since gcc accepts
2251 /// the flag on its own and does nothing with it.
2252 pub hook: Hook,
2253 /// Whether every call the profiler's hook gets is listed in a `__mcount_loc` section, from
2254 /// `-mrecord-mcount`.
2255 ///
2256 /// The list is how a kernel finds the calls to turn into nops at boot and back into calls when
2257 /// a function is traced. Kernels before 5.12 read it from the compiler, and later ones have
2258 /// objtool write it, so kbuild only passes the flag on the older ones.
2259 pub record_mcount: bool,
2260 /// Whether that call is written as a five byte nop rather than a call, from `-mnop-mcount`,
2261 /// which leaves the patching entirely to whoever reads the list.
2262 pub nop_mcount: bool,
2263 /// How much room every function opens with for somebody to write over later, from
2264 /// `-fpatchable-function-entry=`.
2265 ///
2266 /// See [`Patchable`]. A kernel asks for this so that a function can be traced without being
2267 /// rebuilt: the room is a known number of bytes at a known address, and the addresses are
2268 /// collected into a section of their own so that whatever does the patching can find every one
2269 /// of them without reading the symbol table.
2270 pub patchable: Patchable,
2271 /// What happens rather than nothing being defined when arithmetic overflows, from `-fwrapv`,
2272 /// `-fwrapv-pointer`, `-fno-strict-overflow` and `-ftrapv`.
2273 ///
2274 /// See [`Wrapping`]. Nothing wraps and nothing stops by default, which is what C says and what
2275 /// lets the optimizer read a loop counter as a number rather than as a number that may turn
2276 /// round.
2277 pub wrapping: Wrapping,
2278 /// What a plain `char` is, from `-fsigned-char` and `-funsigned-char`, with nothing meaning
2279 /// the answer the target's ABI gives.
2280 ///
2281 /// Plain `char` is a third type either way, distinct from both `signed char` and
2282 /// `unsigned char` in every place a type is compared, and this says which of the two it has
2283 /// the range of. Changing it changes the ABI, so it is a decision about the whole program
2284 /// rather than about one file, and `__CHAR_UNSIGNED__` is defined when the answer is unsigned
2285 /// so that a header can see what was decided.
2286 pub char_signed: Option<bool>,
2287 /// Whether `wchar_t` is a 16 bit unsigned type whatever the target says, from `-fshort-wchar`,
2288 /// with `false` meaning the target's own answer.
2289 ///
2290 /// Like plain `char`, this is put into the target when the session is made, so that the lexer
2291 /// converting `L""`, the checker typing it and the macros that spell `wchar_t` all read one
2292 /// answer. It changes the ABI of anything that passes a `wchar_t`.
2293 pub short_wchar: bool,
2294 /// Whether inlining may grow a frame only as far as gcc lets it under `-fconserve-stack`,
2295 /// which is 40 percent more than the caller's own locals or 100 bytes. The kernel passes it,
2296 /// since its stacks are a few pages.
2297 pub conserve_stack: bool,
2298 /// Whether an enumeration nothing wrote an underlying type for is represented in the smallest
2299 /// integer type that holds its enumerators, from `-fshort-enums`.
2300 ///
2301 /// The default is `int` or wider, which is what C says and what every psABI in the table
2302 /// expects. This makes it `char` or wider instead, so `enum { A }` is one byte, and that
2303 /// changes the size and the alignment of anything holding one. It is here because a great deal
2304 /// of embedded C and every ARM EABI object is built with it, and mixing the two answers in one
2305 /// program is a silent disagreement about layout rather than a link error.
2306 pub short_enums: bool,
2307 /// Whether Microsoft's reading of an anonymous member is taken, from `-fms-extensions` and
2308 /// `-fno-ms-extensions`, with the target deciding when neither was written.
2309 ///
2310 /// C takes a `struct` or a `union` member with neither a tag nor a name as anonymous, and
2311 /// Microsoft's rule takes one written with a tag or named through a typedef as well. The
2312 /// default is on for a Windows target and off everywhere else, which is what gcc does: its
2313 /// mingw build has the flag on without being asked and its Linux build has it off. The
2314 /// Windows headers need it, because `<objidl.h>` and the rest close a nameless union with
2315 /// `} DUMMYUNIONNAME;` and the macro expands to nothing.
2316 pub ms_extensions: Option<bool>,
2317 /// Whether a tentative definition is offered to the linker as a common symbol, from
2318 /// `-fcommon` and `-fno-common`.
2319 ///
2320 /// Two files that each write `int g;` link under it and are a duplicate definition without
2321 /// it. The default is on for a Darwin target and off everywhere else, which is what the
2322 /// compiler each platform ships does: Apple's clang still has it on, and gcc has had it off
2323 /// since 10, as has clang everywhere but Darwin.
2324 pub common: Option<bool>,
2325 /// Whether an access names the type it goes through, from `-fstrict-aliasing` and
2326 /// `-fno-strict-aliasing`.
2327 ///
2328 /// On, which is gcc's answer at every level above `-O0` and is what C 6.5 paragraph 7 already
2329 /// says. Clearing it makes the front end leave the type off every load and every store, and an
2330 /// access with no type on it is one the alias analysis has no type based reason to separate
2331 /// from any other, which is what the flag asks for.
2332 pub strict_aliasing: bool,
2333 /// How far a multiply and an addition may be fused into one rounding, from `-ffp-contract=`.
2334 ///
2335 /// See [`Contract`]. Most of the floating point flags have nowhere to be kept, because they
2336 /// withdraw licences that nothing here takes in the first place: no arithmetic in a function
2337 /// body is folded at any level, so a flag saying the rounding mode may have changed describes
2338 /// what already happens. This one and [`Options::trapping_math`] are the two that have
2339 /// somewhere to go.
2340 pub fp_contract: Contract,
2341 /// Whether an operation may raise an exception the program then looks at, from
2342 /// `-ftrapping-math` and `-fno-trapping-math`.
2343 ///
2344 /// On, which is gcc's default. What clearing it licenses here is one thing: the conversion of
2345 /// a constant floating value to an integer type it does not fit in. Left to the hardware that
2346 /// conversion is one instruction and the answer is the integer indefinite value, which is what
2347 /// both compilers give by default. gcc folds it under this flag instead, to the nearest end of
2348 /// the integer's range, and the difference is visible because the conversion is undefined
2349 /// behaviour rather than a value, so neither answer is wrong and the one a program was written
2350 /// against is gcc's.
2351 pub trapping_math: bool,
2352 /// The rest of the `-ffast-math` family, from the flag itself and from each member spelled on
2353 /// its own. See [`Math`].
2354 pub math: Math,
2355 /// Whether an exception may unwind through the code this unit produces, from `-fexceptions`
2356 /// and `-fno-exceptions`, and from `-fnon-call-exceptions` when neither of those was written.
2357 ///
2358 /// Off, which is gcc's default for C. What it changes in C is small: `__EXCEPTIONS` is defined,
2359 /// which is what glibc's `pthread_cleanup_push` reads to choose a `cleanup` attribute over its
2360 /// `setjmp` spelling, and a `cleanup` handler is owed a call when an unwind passes through its
2361 /// scope as well as when the scope is left the ordinary way. The tables an unwinder reads to get
2362 /// through a frame at all are [`Options::unwind_tables`] and are there either way.
2363 pub exceptions: bool,
2364 /// Whether an instruction that is not a call may raise an exception, from
2365 /// `-fnon-call-exceptions`. It turns [`Options::exceptions`] on unless `-fno-exceptions` was
2366 /// written, which is gcc's rule, and it is kept apart because it is a second promise about
2367 /// which instructions a handler covers rather than a second way of saying the first one.
2368 pub non_call_exceptions: bool,
2369 /// What a path is rewritten by before it is written into the output, from the
2370 /// `-f*-prefix-map=` family.
2371 ///
2372 /// See [`PrefixMaps`]. This is what makes a build reproducible from a different directory, and
2373 /// it is three lists rather than one because gcc has three flags and a build uses them apart.
2374 pub prefix_map: PrefixMaps,
2375 /// Whether warnings are errors.
2376 pub warnings_are_errors: bool,
2377 /// Whether a warning is raised at all, which is `-w` turned around.
2378 ///
2379 /// A build that passes this has decided it does not want to hear about anything that is not
2380 /// fatal, and the flag is dropped at the one place every diagnostic goes through rather than
2381 /// tested at each site that raises one. `-w` beats `-Werror` where both are given, because a
2382 /// warning that was never raised cannot be promoted.
2383 pub warnings: bool,
2384 /// Whether a warning about something in a header that came with the machine is printed, which
2385 /// is `-Wsystem-headers` and is off the way gcc has it off.
2386 ///
2387 /// The person compiling did not write the file and cannot change it, so a warning about it is
2388 /// noise, and under `-Werror` it is a build that stops on a line nobody in the project typed.
2389 /// It is worth having the flag rather than nothing at all, because somebody porting a header
2390 /// or reading what a new compiler thinks of one does want to hear all of it.
2391 pub system_header_warnings: bool,
2392 /// What `-Wno-<name>`, `-Werror=<name>` and `-Wno-error=<name>` said, by the name gcc gives
2393 /// the option that controls a warning.
2394 pub named_warnings: rucc_diag::Named,
2395 /// How many diagnostics to print before giving up. Past a certain point the output is
2396 /// noise from a single earlier mistake, and GCC's default of no limit is not a kindness.
2397 pub error_limit: u32,
2398 /// The dialect, from `-std=`.
2399 pub std: Std,
2400 /// Whether the GNU extensions are on, which is `-std=gnu23` rather than `-std=c23`.
2401 pub gnu_extensions: bool,
2402 /// Whether `-trigraphs` was given, which replaces trigraphs whatever the dialect says. The
2403 /// ISO modes before C23 replace them without it.
2404 pub trigraphs: bool,
2405 /// Whether `-pedantic` was given, which is what turns a use of an extension from silence
2406 /// into a diagnostic. It is not the same knob as the dialect: `-std=c17 -pedantic` warns
2407 /// about a construct that `-std=c17` alone accepts without a word.
2408 pub pedantic: bool,
2409 /// Whether `-fpermissive` was given, which turns the rules gcc 14 promoted from errors back
2410 /// into warnings.
2411 ///
2412 /// Six of them, all about code written before the language settled: a declaration with no
2413 /// type in it, a call to a function nothing declared, a parameter in an old style definition
2414 /// with no type, a pointer made from an integer, a pointer assigned from a pointer to
2415 /// something else, and a `return` whose value disagrees with what was promised. The flag says
2416 /// nothing about any other diagnostic, and it does not say to compile something different: a
2417 /// program it accepts is compiled the way the rule it broke says it means.
2418 pub permissive: bool,
2419 /// Whether `-ffixed-x18` was given, which keeps `x18` for something outside the program on
2420 /// AArch64. Nothing here ever gives `x18` to a value, so the one thing it changes is that a
2421 /// nested function, whose static chain travels in `x18`, cannot be built.
2422 pub fixed_x18: bool,
2423 /// Whether the whole unit is under GNU's reading of `inline` rather than C's, which is
2424 /// `-fgnu89-inline`.
2425 ///
2426 /// Under C's reading a definition every file-scope declaration wrote `inline` for and none
2427 /// wrote `extern` for emits nothing, and under GNU's it is the definition alone that decides
2428 /// and `extern inline` is the one that emits nothing. The C89 dialects are under GNU's
2429 /// whatever this says, since that is where the older reading came from, so this is the flag a
2430 /// program written against it reaches for when it is being compiled under a later dialect.
2431 pub gnu89_inline: bool,
2432 /// Which arrays at the end of a structure count as flexible, from `-fstrict-flex-arrays=`.
2433 ///
2434 /// Zero, the default, takes every trailing array as flexible. One takes `[]`, `[0]` and `[1]`,
2435 /// two takes `[]` and `[0]`, and three takes only `[]`. A trailing array that is not flexible
2436 /// has the size it was declared with when `__builtin_object_size` is asked about it through a
2437 /// pointer, which is what lets a fortified copy into one be checked.
2438 pub strict_flex_arrays: u8,
2439 /// What a name that nothing in the source said anything about reaches, from `-fvisibility=`.
2440 pub visibility: Visibility,
2441 /// What every function is aligned to unless it asked for more itself, from
2442 /// `-falign-functions` and `-fno-align-functions`.
2443 ///
2444 /// `None` is the target's own answer, which is `rucc_object::FUNC_ALIGN`, and it is what a
2445 /// bare `-falign-functions` asks for as well, since gcc's bare form means the default and the
2446 /// default on this target is the same sixteen bytes. A number is a floor rather than a
2447 /// setting: a function carrying `__attribute__((aligned(N)))` keeps the larger of the two,
2448 /// because the attribute is a statement about that function and this is a preference about
2449 /// the unit.
2450 pub align_functions: Option<u32>,
2451 /// Whether every function calls `__cyg_profile_func_enter` on the way in and
2452 /// `__cyg_profile_func_exit` on the way out, from `-finstrument-functions`.
2453 ///
2454 /// Off unless asked for. A function declared `no_instrument_function` is left alone whatever
2455 /// this says, which is how the two hooks avoid calling themselves.
2456 pub instrument_functions: bool,
2457 /// Whether the object may end up in a shared library, from `-fPIC` and `-fPIE`, or is not
2458 /// position independent at all, from `-fno-pic` and `-fno-pie`.
2459 pub pic: Pic,
2460 /// Whether a definition in this unit may be replaced at load time by one in another object,
2461 /// from `-fsemantic-interposition` and `-fno-semantic-interposition`.
2462 ///
2463 /// True is the honest answer and is gcc's default, because that is what an exported name in a
2464 /// shared library means: the dynamic linker takes the first definition it finds in load order,
2465 /// so a function this unit defines and calls may not be the one that runs. Everything the
2466 /// optimizer reads off a body has to stop at a name like that.
2467 ///
2468 /// False is a promise the build makes, and every distribution makes it, because otherwise a
2469 /// library cannot inline its own functions into each other. It is a promise rather than a
2470 /// deduction: nothing checks it, and a program that then interposes one of those names gets a
2471 /// mixture of the two definitions. It says nothing about `-fPIE`, where no name is replaceable
2472 /// to begin with, and it says nothing about how an address is reached, which is the separate
2473 /// question `-fPIC` decides.
2474 pub interposition: bool,
2475 /// Whether a function is described to an unwinder at every instruction, from
2476 /// `-fasynchronous-unwind-tables` and `-fno-asynchronous-unwind-tables`.
2477 ///
2478 /// True is the default, which is gcc's wherever anything reads the table, and the reason is
2479 /// that the programs that read it are not the ones being compiled. C++ exceptions,
2480 /// `backtrace`, a profiler sampling a stack and a crash handler printing one all walk frames
2481 /// belonging to code that knew nothing about them, so a unit that opts out stops a walk that
2482 /// started somewhere else.
2483 ///
2484 /// What `asynchronous` asks for on top of a table is that the answer is right at every
2485 /// instruction and not only where a call is, because a signal can arrive anywhere, including
2486 /// the middle of a prologue. Rows come off the prologue as it is built here, so that is the
2487 /// only kind of table there is to write and the weaker request below is answered with it.
2488 ///
2489 /// False is for a build that knows nothing will ever walk it, which in practice is a kernel or
2490 /// a freestanding image, and what it saves is the section rather than any instruction.
2491 pub async_unwind_tables: bool,
2492 /// Whether a function is described to an unwinder at all, from `-funwind-tables` and
2493 /// `-fno-unwind-tables`.
2494 ///
2495 /// The weaker of the two requests and off by default, because the one above is on and implies
2496 /// it. A table is written when either of them is standing, which is what [`Self::unwinds`]
2497 /// answers and is how gcc resolves a line that asks for a table and against an asynchronous
2498 /// one.
2499 ///
2500 /// Neither of them is about anything but ELF. Mach-O and COFF have their own arrangements and
2501 /// neither is written yet, so on those targets nothing reads these.
2502 pub unwind_tables: bool,
2503 /// Whether each function gets a section of its own, from `-ffunction-sections`.
2504 ///
2505 /// A linker can leave out a section nothing reaches and cannot leave out half of one, so this
2506 /// is what makes `--gc-sections` able to drop a function this file defines and nothing calls.
2507 /// A kernel and an embedded image are both linked that way and are both a good deal larger
2508 /// without it, and the cost is one section header per function.
2509 pub function_sections: bool,
2510 /// Whether each variable gets a section of its own, from `-fdata-sections`.
2511 ///
2512 /// The same bargain for the data, and a separate flag because gcc has two of them: a build
2513 /// that wants one and not the other is a build that measured something. Splitting the data can
2514 /// cost more than it saves, since two variables a loop reads together are no longer certain to
2515 /// land in the same page.
2516 pub data_sections: bool,
2517 /// The GCC release claimed, from `-fgnuc-version=`.
2518 pub gnuc: GnucVersion,
2519 /// Whether `-fgnuc-version=` was written at all.
2520 ///
2521 /// An MSVC row claims no GCC release unless it was asked to, which is what clang does for
2522 /// `-target x86_64-pc-windows-msvc`: the SDK headers take a path for `__GNUC__` that they
2523 /// were never tested on with the MSVC runtime. The flag is the way to ask, as it is in clang.
2524 pub gnuc_given: bool,
2525 /// The GNU assembler release claimed, from `-fgnu-as-version=`, which is what
2526 /// `-Wa,--version` prints.
2527 pub gnu_as: GasVersion,
2528 /// Whether `-Wa,--fatal-warnings` was given, which makes the assembler's one warning, the
2529 /// `.warning` directive, an error the way it is in gas.
2530 pub asm_fatal_warnings: bool,
2531 /// Whether `-Wa,--noexecstack` was given, which gives a file of assembly the marker that says
2532 /// its stack is not executable when it did not write one itself.
2533 pub asm_noexecstack: bool,
2534 /// Whether `-Wa,-mrelax-relocations=no` was given, which reads every slot of the global offset
2535 /// table through the relocation the linker may not rewrite, as gas does under it.
2536 pub asm_keep_slots: bool,
2537 /// Whether `-m16` was given, which builds for 32 bit x86 and assembles the result as
2538 /// `.code16gcc`: code that runs in real mode with 32 bit operands and a 32 bit stack, the way
2539 /// the kernel's boot and real mode trampoline are built.
2540 pub sixteen: bool,
2541 /// How many words of a function's arguments go in registers, from `-mregparm=`, which only
2542 /// 32 bit x86 has and the driver refuses anywhere else. See
2543 /// [`rucc_target::TargetInfo::with_regparm`].
2544 pub regparm: u8,
2545 /// Whether a small structure comes back in registers, from `-freg-struct-return`, which only
2546 /// changes anything on i386 System V. See
2547 /// [`rucc_target::TargetInfo::with_reg_struct_return`].
2548 pub reg_struct_return: bool,
2549 /// Whether the object says what made it, which `-fno-ident` turns off. See
2550 /// `rucc_object::Output::ident`.
2551 pub ident: bool,
2552 /// The MSVC release claimed on an MSVC row, from `-fms-compatibility-version=`.
2553 pub msc: MscVersion,
2554 /// Whether an MSVC row is built against the C runtime in a DLL, which is
2555 /// `-fms-runtime-lib=dll` and `cl.exe`'s `/MD`, rather than the static one, which is `/MT`.
2556 ///
2557 /// The headers are told through `_DLL`, which is how they know to declare the runtime's
2558 /// functions as imported, and the link is told which three libraries to name.
2559 pub ms_dll_runtime: bool,
2560 /// Whether there is a standard library, which is `-ffreestanding` turned around.
2561 pub hosted: bool,
2562 /// Whether a call to a C library function written under its own plain name may be taken to
2563 /// mean that function, which is `-fno-builtin` turned around.
2564 ///
2565 /// The names are reserved, so `llabs` is the library's `llabs` and the compiler is allowed to
2566 /// know what it does. A program that means something else by one of them is the reason the
2567 /// flag exists, and `-ffreestanding` turns it off as well, because a freestanding program has
2568 /// no C library for the name to be the name of. The `__builtin_` spellings are not affected by
2569 /// either, since the prefix is the program saying which function it means.
2570 pub builtins: bool,
2571 /// The names `-fno-builtin-<name>` took away one at a time, without the prefix.
2572 ///
2573 /// A build that means its own `memcpy` and the library's everything else writes this rather
2574 /// than the whole flag, which is what the kernel does for a handful of names.
2575 pub no_builtin: Vec<String>,
2576 /// The glibc release the headers on the search path are, as the minor number alone.
2577 ///
2578 /// `Some` means two things together: this is a glibc target, and step 3 of
2579 /// `spec/cross-compile/08-sysroots.md` section 8.5 resolved to the tree we bundle. Then the
2580 /// compiler defines `__GLIBC_MINOR__`, because one tree serves every version and the version is
2581 /// the part of it the target supplies. `__GLIBC__` is not ours to define either way, since it is
2582 /// in the tree and a real `features.h` defines it too.
2583 ///
2584 /// `None` is every other case, and the cases matter more than the value. A host glibc's
2585 /// `features.h` defines the macro itself, and a tree the user named has a `features.h` of its
2586 /// own, so defining it as well would be two definitions with different values, which is a
2587 /// warning on every compilation of every file. A musl or mingw target has no such macro at all.
2588 pub glibc_minor: Option<u32>,
2589 /// The deployment target on an Apple platform, the oldest release the program is promised
2590 /// to run on. It comes from `-mmacosx-version-min=` or from the tuple, as in
2591 /// `aarch64-macos.13`, and `None` leaves the platform's default in place.
2592 pub os_version: Option<rucc_tuple::Version>,
2593 /// `-D` in command line order. `FOO` means `FOO=1`, as GCC has it.
2594 pub defines: Vec<String>,
2595 /// `-U` in command line order, applied after the defines because `-U` wins.
2596 pub undefines: Vec<String>,
2597 /// Where a header is looked for.
2598 pub search: SearchPath,
2599 /// What `-imacros` and `-include` named, in command line order.
2600 pub preincludes: Vec<Preinclude>,
2601 /// Whether `-E` writes line markers, which `-P` turns off.
2602 pub line_markers: bool,
2603 /// What the `-d` family asks for.
2604 pub dumps: Dumps,
2605 /// What the `-M` family asks for.
2606 pub deps: Deps,
2607 /// Whether the intermediate files are kept, from `-save-temps`.
2608 pub save_temps: SaveTemps,
2609 /// Whether each compiled file gets a `.su` beside its output saying how much stack each of its
2610 /// functions takes, from `-fstack-usage`.
2611 ///
2612 /// gcc's flag and gcc's file, one line per function. What the number counts is on
2613 /// `rucc_codegen::frame::Frame::usage`. It is counted the way gcc counts, and it is the size of
2614 /// rucc's frame rather than gcc's, which is the point: the two files side by side say which
2615 /// functions one compiler gives more stack than the other does.
2616 pub stack_usage: bool,
2617 /// What the files kept beside an output are named after, from `-dumpbase`, in place of the
2618 /// name the output or the input would have given them.
2619 pub dump_base: Option<String>,
2620 /// The extension `-dumpbase-ext` says to take off the end of [`Options::dump_base`].
2621 pub dump_base_ext: Option<String>,
2622 /// What goes in front of the name of every file kept beside an output, from `-dumpdir`. A
2623 /// directory when it ends in a slash and the start of a name otherwise, which is gcc's rule.
2624 pub dump_dir: Option<String>,
2625 /// Whether each step says how long it took, from `-time`.
2626 pub time: bool,
2627 /// What `-f<pass>` and `-fno-<pass>` said about an optimizer pass, in the order the command
2628 /// line said it, so that the last mention of a pass is the one that decides.
2629 ///
2630 /// The pipeline the level chose is the starting point and this is what is added to and taken
2631 /// away from it. The names are checked against the pass list while the arguments are parsed,
2632 /// so anything in here is a pass the compiler has.
2633 pub passes: Vec<(String, bool)>,
2634 /// What `-fpass-fuel=<pass>=<n>` limited a pass to, by pass name.
2635 ///
2636 /// A pass with an entry here performs exactly that many transformations and then stops
2637 /// transforming, which is what bisects a miscompilation to one rewrite. See section 9.10 of
2638 /// `spec/09-optimizer.md`.
2639 pub pass_fuel: Vec<(String, u32)>,
2640 /// What `-fpass-fuel-global=<n>` limited the whole pipeline to, across every pass.
2641 ///
2642 /// The outer of the two searches in section 4.5 of `spec/optimizer/04-pass-manager.md`.
2643 /// Halving this says which pass holds the bad rewrite, and halving `-fpass-fuel` for that
2644 /// pass says which rewrite it is. Where both are given, a pass is stopped by whichever of
2645 /// the two is tighter.
2646 pub pass_fuel_global: Option<u32>,
2647 /// Where `-frucc-trace=<file>` asked for one line of JSON per compiled file saying how long
2648 /// each phase and each optimizer pass took. Appended to, never truncated, so that every
2649 /// compiler in a parallel build can share one file.
2650 pub trace: Option<String>,
2651 /// What `-fdisable-<pass>[=<range>]` and `-fenable-<pass>[=<range>]` said, in the order the
2652 /// command line said it, with `true` for the enabling half.
2653 ///
2654 /// A rule covers the functions it names and nothing else, and the last rule that covers a
2655 /// function is the one that decides for it, so the order has to survive. This is the second
2656 /// half of the bisection interface in section 41.6 of `spec/optimizer/41-correctness.md`:
2657 /// `-fpass-fuel` finds the rewrite and this finds the function. The pass names are checked
2658 /// against the pass list while the arguments are parsed.
2659 pub pass_gates: Vec<(bool, String)>,
2660 /// What `-fdump-ir=` asked to see, as it was written, which is `all`, `before-<pass>` or
2661 /// `after-<pass>`.
2662 pub dump_ir: Vec<String>,
2663 /// What `-fopt-info` asked to hear about, as the keywords were written, with the leading
2664 /// hyphen taken off, so a bare `-fopt-info` is the empty string in here.
2665 ///
2666 /// The keywords are `optimized`, `missed`, `note` and `all`, and two flags add up rather than
2667 /// the second replacing the first. Checked while the arguments are parsed, so anything in
2668 /// here is a spelling the optimizer understands. See section 42.2 of
2669 /// `spec/optimizer/42-measurement.md` for why `missed` is the one that earns the feature.
2670 pub opt_info: Vec<String>,
2671 /// Where `-fopt-info=<file>` sends the remarks, or `None` for standard error.
2672 ///
2673 /// One file for the whole run rather than one per input, the way GCC does it, and the last
2674 /// one on the command line is the one that decides. A harness that wants the remarks kept
2675 /// away from the diagnostics gives a file, which is what the corpus in `tamnd/rucc-corpus`
2676 /// does with GCC so that a rejection can still be matched against the diagnostic stream.
2677 pub opt_info_file: Option<String>,
2678 /// Whether the IR verifier runs after every pass that changed anything.
2679 ///
2680 /// On in a debug build without being asked, since that is where a broken pass should be
2681 /// caught. `-Zverify-each` turns it on in a release build, which is what CI wants.
2682 pub verify_each: bool,
2683 /// Where `-Zrule-coverage=FILE` writes which lowering rules fired, if it was given.
2684 ///
2685 /// A measurement rather than a thing a build asks for, which is why it is spelled with a `-Z`
2686 /// the way an unstable option is everywhere else: it is here for the harness in
2687 /// `tamnd/rucc-compat` to union over a corpus and report, and nothing about the code that comes
2688 /// out changes when it is on. One file per run of the compiler, holding the whole rule set with
2689 /// the rules this run reached marked, whatever the run compiled and however many files it was.
2690 pub rule_coverage: Option<String>,
2691 /// Where `-Zregister-pressure=FILE` writes what the allocator had to put on the stack.
2692 ///
2693 /// A measurement and spelled with a `-Z` for the same reason as the one above: nothing about
2694 /// the code that comes out changes when it is on. One file per run of the compiler, one line
2695 /// per function, holding how many values went to the stack and how many stores and reloads
2696 /// that cost. What reads it is `cargo xtask pressure`, which compiles the benchmarks in
2697 /// `bench/safety` with the monitor off and on and reports the difference, since
2698 /// `spec/safe-memory/13-performance.md` section 13.1 asks for that number and section 5.2.1
2699 /// says why: a capability in flight is four words, and if materializing one spills something
2700 /// else in a hot loop then check elimination cannot save it.
2701 pub register_pressure: Option<String>,
2702 /// Where `-Zlowering=FILE` writes what the pre-selection lowering group did.
2703 ///
2704 /// A measurement and spelled with a `-Z` for the same reason as the two above: nothing about
2705 /// the code that comes out changes when it is on. One file per run of the compiler, one block
2706 /// per function, holding every member of the group in the order it ran and what each of them
2707 /// found and left behind. What it is read for is a function that came out of the back end in a
2708 /// shape somebody did not expect, since the block says which lowering changed it, and what it
2709 /// is read for after that is a construct the selector refused by name, since the block says
2710 /// whether the step that answers for that construct was offered it and walked away.
2711 pub lowering_dump: Option<String>,
2712 /// The shape `-Zswitch=` forces on every `switch`, spelled as it was given: `table`, `tree` or
2713 /// `walk`. A measurement, like the two above, but one that changes the code: section 24.7
2714 /// asks what each shape costs on a hot `switch`, and building it that way is how to find out.
2715 pub switch_shape: Option<String>,
2716}
2717
2718impl Options {
2719 /// Default options for `target`.
2720 pub fn new(target: Triple) -> Self {
2721 Self {
2722 target,
2723 opt_level: OptLevel::default(),
2724 safety: Safety::default(),
2725 padding: Padding::default(),
2726 subobject: Subobject::default(),
2727 promise: Promise::default(),
2728 races: Races::default(),
2729 emit: EmitKind::default(),
2730 debug_info: false,
2731 dwarf_version: 5,
2732 working_dir: None,
2733 compress: Compress::None,
2734 lto: Lto::default(),
2735 profile_data: Profile::default(),
2736 frame_pointer: None,
2737 red_zone: true,
2738 code_model: CodeModel::Small,
2739 stack_boundary: None,
2740 vector: true,
2741 x87: true,
2742 x87_return: true,
2743 guard: None,
2744 zero_regs: None,
2745 isa: match target.arch {
2746 Arch::X86_64 => Isa::baseline(),
2747 // Nothing for i686 yet: x86-64's baseline promises SSE2, which an i686 does not.
2748 Arch::Aarch64 | Arch::Riscv64 | Arch::X86 => Isa::NONE,
2749 },
2750 reorder_blocks: None,
2751 partition_blocks: None,
2752 reorder_functions: None,
2753 schedule_insns: None,
2754 sibling_calls: None,
2755 align_loops: None,
2756 cycle_accurate_model: None,
2757 backtracking: None,
2758 stack_reuse: None,
2759 protector: Protector::default(),
2760 stack_clash: false,
2761 control: Control::default(),
2762 speculation: Speculation::default(),
2763 jump_tables: true,
2764 cmov: true,
2765 auto_var_init: None,
2766 profile: false,
2767 hook: Hook::default(),
2768 record_mcount: false,
2769 nop_mcount: false,
2770 patchable: Patchable::default(),
2771 wrapping: Wrapping::NONE,
2772 char_signed: None,
2773 short_wchar: false,
2774 conserve_stack: false,
2775 short_enums: false,
2776 ms_extensions: None,
2777 common: None,
2778 strict_aliasing: true,
2779 fp_contract: Contract::Off,
2780 trapping_math: true,
2781 math: Math::default(),
2782 exceptions: false,
2783 non_call_exceptions: false,
2784 prefix_map: PrefixMaps::default(),
2785 warnings_are_errors: false,
2786 warnings: true,
2787 system_header_warnings: false,
2788 named_warnings: rucc_diag::Named::default(),
2789 error_limit: 20,
2790 std: Std::default(),
2791 gnu_extensions: true,
2792 trigraphs: false,
2793 pedantic: false,
2794 permissive: false,
2795 fixed_x18: false,
2796 gnu89_inline: false,
2797 strict_flex_arrays: 0,
2798 visibility: Visibility::default(),
2799 align_functions: None,
2800 instrument_functions: false,
2801 pic: Pic::default(),
2802 interposition: true,
2803 async_unwind_tables: true,
2804 unwind_tables: false,
2805 function_sections: false,
2806 data_sections: false,
2807 gnuc: GnucVersion::default(),
2808 gnuc_given: false,
2809 gnu_as: GasVersion::default(),
2810 asm_fatal_warnings: false,
2811 asm_noexecstack: false,
2812 asm_keep_slots: false,
2813 sixteen: false,
2814 regparm: 0,
2815 reg_struct_return: false,
2816 ident: true,
2817 msc: MscVersion::default(),
2818 ms_dll_runtime: false,
2819 hosted: true,
2820 builtins: true,
2821 no_builtin: Vec::new(),
2822 glibc_minor: None,
2823 os_version: None,
2824 defines: Vec::new(),
2825 undefines: Vec::new(),
2826 search: SearchPath::new(),
2827 preincludes: Vec::new(),
2828 line_markers: true,
2829 dumps: Dumps::default(),
2830 deps: Deps::default(),
2831 save_temps: SaveTemps::default(),
2832 stack_usage: false,
2833 dump_base: None,
2834 dump_base_ext: None,
2835 dump_dir: None,
2836 time: false,
2837 passes: Vec::new(),
2838 pass_fuel: Vec::new(),
2839 pass_fuel_global: None,
2840 trace: None,
2841 pass_gates: Vec::new(),
2842 dump_ir: Vec::new(),
2843 opt_info: Vec::new(),
2844 opt_info_file: None,
2845 verify_each: cfg!(debug_assertions),
2846 rule_coverage: None,
2847 register_pressure: None,
2848 lowering_dump: None,
2849 switch_shape: None,
2850 }
2851 }
2852
2853 /// Whether a function in this unit is described to an unwinder.
2854 ///
2855 /// Either request is answered with the same table, so what decides is whether either of them
2856 /// is standing. Asked here rather than worked out at the two places that write a table, since
2857 /// those two writing different answers for one function is what `spec/11-asm-objects-debug.md`
2858 /// section 11.1 says must not be possible. `-fexceptions` asks for one too, as it does of gcc,
2859 /// since a landing pad nothing can find is a cleanup that never runs.
2860 #[must_use]
2861 pub const fn unwinds(&self) -> bool {
2862 self.async_unwind_tables || self.unwind_tables || self.exceptions
2863 }
2864
2865 /// Whether every function keeps a frame pointer: what the command line said, or what the
2866 /// level says when it said nothing.
2867 #[must_use]
2868 pub const fn keeps_frame_pointer(&self) -> bool {
2869 match self.frame_pointer {
2870 Some(kept) => kept,
2871 None => self.opt_level.frame_pointer(),
2872 }
2873 }
2874}
2875
2876/// One compilation.
2877///
2878/// Holds the options, the string interner and the diagnostics raised so far. Passing a
2879/// `&mut Session` is how a stage reports a problem, and the return value of a stage says
2880/// what it produced, never whether it succeeded: that question is answered by
2881/// [`Session::has_errors`].
2882#[derive(Debug)]
2883pub struct Session {
2884 /// What this compilation was asked to do.
2885 pub opts: Options,
2886 /// Everything known about the target.
2887 pub target: TargetInfo,
2888 /// The one interner for the compilation.
2889 pub interner: Interner,
2890 /// Every file read during the compilation, and the flat coordinate space their spans
2891 /// live in.
2892 ///
2893 /// This is on the session rather than passed around separately because a span is only
2894 /// meaningful against the map that issued it, and one map per compilation is the rule
2895 /// that makes that true by construction.
2896 pub sources: SourceMap,
2897 diagnostics: Vec<Diagnostic>,
2898 error_count: u32,
2899 warning_count: u32,
2900}
2901
2902impl Session {
2903 /// A session for `opts`.
2904 ///
2905 /// The command line's answers about plain `char`, `wchar_t` and the calling convention are put
2906 /// into the target here
2907 /// rather than carried beside it, because every place that asks what either is asks the
2908 /// target, and two answers to one question is how a front end ends up disagreeing with its own
2909 /// back end.
2910 pub fn new(opts: Options) -> Self {
2911 let mut target = TargetInfo::new(opts.target);
2912 if opts.reg_struct_return {
2913 target = target.with_reg_struct_return(true);
2914 }
2915 if opts.regparm > 0 {
2916 // A count the target cannot take was refused by the driver.
2917 target = target.clone().with_regparm(opts.regparm).unwrap_or(target);
2918 }
2919 if let Some(version) = opts.os_version {
2920 target.tuple = target.tuple.with_os_version(version);
2921 }
2922 if let Some(signed) = opts.char_signed {
2923 target.char_is_signed = signed;
2924 }
2925 if opts.short_wchar {
2926 target.wchar_width = 16;
2927 target.wchar_is_signed = false;
2928 }
2929 // The boundary the stack is kept on, whether the vector registers carry arguments and
2930 // where the canary is, which the front end reads to decide how far it may align a local
2931 // and the back end reads for everything else.
2932 if let Some(regs) = target.call_regs {
2933 let regs = opts.stack_boundary.map_or(regs, |bytes| regs.aligned_to(bytes));
2934 let regs = if opts.vector { regs } else { regs.without_vectors() };
2935 target.call_regs = Some(opts.guard.map_or(regs, |guard| regs.guarded_by(guard)));
2936 }
2937 Self {
2938 opts,
2939 target,
2940 interner: Interner::with_capacity(1024),
2941 sources: SourceMap::new(),
2942 diagnostics: Vec::new(),
2943 error_count: 0,
2944 warning_count: 0,
2945 }
2946 }
2947
2948 /// Whether Microsoft's reading of an anonymous member is taken.
2949 ///
2950 /// The command line answers where it said anything, and the target answers otherwise: gcc's
2951 /// mingw build has the flag on without being asked for it and its Linux build has it off, and
2952 /// a header written for one of the two is read by whichever compiler the platform ships.
2953 #[must_use]
2954 pub fn ms_extensions(&self) -> bool {
2955 self.opts.ms_extensions.unwrap_or(self.opts.target.os == Os::Windows)
2956 }
2957
2958 /// Whether a tentative definition is a common symbol rather than one in `.bss`.
2959 ///
2960 /// The command line answers where it said anything and the target answers otherwise, for the
2961 /// reason [`Options::common`] gives. A claim of a GCC before 10 answers yes as well, since
2962 /// those releases did and a tree written for them can define the same variable in two files.
2963 /// An MSVC row is left alone, because `-fgnuc-version=` there only claims `__GNUC__`, as it
2964 /// does in clang.
2965 #[must_use]
2966 pub fn common(&self) -> bool {
2967 self.opts.common.unwrap_or_else(|| {
2968 self.opts.target.os == Os::Darwin
2969 || (self.opts.target.env != Env::Msvc && self.opts.gnuc.common_by_default())
2970 })
2971 }
2972
2973 /// Records a diagnostic.
2974 ///
2975 /// Under `-Werror` a warning is promoted here, once, rather than at every site that
2976 /// raises one, and under `-w` it is dropped here for the same reason. A warning that `-w`
2977 /// dropped is not counted, so `-w -Werror` compiles rather than failing on a warning
2978 /// nobody was going to see. A warning about a line in a header that came with the machine is
2979 /// dropped here too, which is what `-Wsystem-headers` turns off, and dropping it before the
2980 /// promotion is what keeps `-Werror` from stopping a build on somebody else's header.
2981 pub fn emit(&mut self, mut diag: Diagnostic) {
2982 if rucc_diag::dropped(
2983 &diag,
2984 &self.sources,
2985 self.opts.warnings,
2986 self.opts.system_header_warnings,
2987 ) || self.opts.named_warnings.silenced(&diag)
2988 {
2989 return;
2990 }
2991 if self.opts.named_warnings.promoted(&diag, self.opts.warnings_are_errors) {
2992 diag.severity = Severity::Error;
2993 }
2994 match diag.severity {
2995 Severity::Error | Severity::Ice => self.error_count += 1,
2996 Severity::Warning => self.warning_count += 1,
2997 Severity::Note | Severity::Help => {}
2998 }
2999 self.diagnostics.push(diag);
3000 }
3001
3002 /// Everything raised so far, in the order it was raised.
3003 pub fn diagnostics(&self) -> &[Diagnostic] {
3004 &self.diagnostics
3005 }
3006
3007 /// Whether anything fatal has been raised.
3008 pub fn has_errors(&self) -> bool {
3009 self.error_count > 0
3010 }
3011
3012 /// How many errors have been raised.
3013 pub fn error_count(&self) -> u32 {
3014 self.error_count
3015 }
3016
3017 /// How many warnings have been raised.
3018 pub fn warning_count(&self) -> u32 {
3019 self.warning_count
3020 }
3021
3022 /// Whether the error limit has been reached and the caller should stop.
3023 pub fn error_limit_reached(&self) -> bool {
3024 self.opts.error_limit != 0 && self.error_count >= self.opts.error_limit
3025 }
3026}
3027
3028#[cfg(test)]
3029mod tests {
3030 use super::*;
3031
3032 fn session() -> Session {
3033 Session::new(Options::new("x86_64-unknown-linux-gnu".parse().unwrap()))
3034 }
3035
3036 #[test]
3037 fn a_version_claim_reads_the_way_gcc_prints_one() {
3038 // `gcc -dumpfullversion` gives all three, `gcc -dumpversion` gives one, and both are
3039 // things a script pastes straight into a flag.
3040 let all = |v: &str| v.parse::<GnucVersion>().unwrap();
3041 assert_eq!(all("15.1.0"), GnucVersion { major: 15, minor: 1, patch: 0 });
3042 assert_eq!(all("15"), GnucVersion { major: 15, minor: 0, patch: 0 });
3043 assert_eq!(all("4.2"), GnucVersion { major: 4, minor: 2, patch: 0 });
3044 assert!("".parse::<GnucVersion>().is_err());
3045 assert!("15.".parse::<GnucVersion>().is_err(), "a trailing dot is a typo, not a zero");
3046 assert!("1.2.3.4".parse::<GnucVersion>().is_err());
3047 }
3048
3049 #[test]
3050 fn a_claimed_release_answers_for_its_defaults_the_way_that_gcc_did() {
3051 let v = |text: &str| text.parse::<GnucVersion>().unwrap();
3052 assert_eq!(v("4.9.4").default_std(), Std::C89);
3053 assert_eq!(v("5.1.0").default_std(), Std::C11);
3054 assert_eq!(v("7.5.0").default_std(), Std::C11);
3055 assert_eq!(v("8.1.0").default_std(), Std::C17);
3056 assert_eq!(v("14.2.0").default_std(), Std::C17);
3057 assert_eq!(v("15.1.0").default_std(), Std::C23);
3058 assert_eq!(GnucVersion::default().default_std(), Std::default());
3059 assert!(v("9.5.0").common_by_default());
3060 assert!(!v("10.1.0").common_by_default());
3061 assert_eq!(v("4.9").to_string(), "4.9.0");
3062 assert_eq!(v("4.9.4").dumpversion(), "4.9.4");
3063 assert_eq!(v("6.5.0").dumpversion(), "6.5.0");
3064 assert_eq!(v("7.1.0").dumpversion(), "7");
3065 assert_eq!(v("14.2.0").dumpversion(), "14");
3066 }
3067
3068 #[test]
3069 fn an_msvc_version_reads_the_way_clang_reads_it() {
3070 let read = |v: &str| v.parse::<MscVersion>().unwrap();
3071 assert_eq!(MscVersion::default().msc_ver(), 1940);
3072 assert_eq!(MscVersion::default().msc_full_ver(), 194_000_000);
3073 assert_eq!(read("19.29").msc_ver(), 1929);
3074 assert_eq!(read("19.40.33811").msc_full_ver(), 194_033_811);
3075 assert_eq!(read("19.40.33811.2"), read("19.40.33811"));
3076 assert_eq!(read("19"), MscVersion { major: 19, minor: 0, build: 0 });
3077 assert!("".parse::<MscVersion>().is_err());
3078 assert!("19.x".parse::<MscVersion>().is_err());
3079 assert!("19.100".parse::<MscVersion>().is_err(), "the minor version is two digits");
3080 }
3081
3082 #[test]
3083 fn a_prefix_map_rewrites_the_front_of_a_path_and_nothing_else() {
3084 let map = |pairs: &[(&str, &str)]| {
3085 let mut map = PrefixMap::new();
3086 for &(old, new) in pairs {
3087 map.push(old, new);
3088 }
3089 map
3090 };
3091 assert!(PrefixMap::new().is_empty());
3092 assert_eq!(PrefixMap::new().apply("sub/h.h"), "sub/h.h");
3093
3094 let one = map(&[("sub", "SUB")]);
3095 assert_eq!(one.apply("sub/h.h"), "SUB/h.h");
3096 assert_eq!(one.apply("a.c"), "a.c", "a path the mapping does not start");
3097 assert_eq!(one.apply("x/sub/h.h"), "x/sub/h.h", "the middle of a path is not the front");
3098
3099 // Characters rather than directories, which is what gcc compares and is worth a test of
3100 // its own because it is the part that looks like it ought to be otherwise.
3101 assert_eq!(map(&[("s", "B")]).apply("sub/h.h"), "Bub/h.h");
3102 assert_eq!(map(&[("sub/", "SUB/")]).apply("sub/h.h"), "SUB/h.h");
3103 assert_eq!(map(&[("sub", "")]).apply("sub/h.h"), "/h.h", "mapping to nothing");
3104 assert_eq!(map(&[("", "PRE")]).apply("a.c"), "PREa.c", "an empty old is in front of all");
3105
3106 // The last one that matches wins, whether or not the two ask about the same prefix, which
3107 // is what a project wide mapping plus a narrower one for a directory relies on.
3108 assert_eq!(map(&[("sub", "ONE"), ("sub", "TWO")]).apply("sub/h.h"), "TWO/h.h");
3109 assert_eq!(map(&[("sub", "A"), ("s", "B")]).apply("sub/h.h"), "Bub/h.h");
3110 assert_eq!(map(&[("s", "B"), ("sub", "A")]).apply("sub/h.h"), "A/h.h");
3111 assert_eq!(map(&[("nope", "X"), ("sub", "A")]).apply("sub/h.h"), "A/h.h");
3112 }
3113
3114 #[test]
3115 fn the_argument_is_split_at_the_last_equals_sign() {
3116 assert_eq!(PrefixMap::split("old=new"), Some(("old", "new")));
3117 assert_eq!(PrefixMap::split("=new"), Some(("", "new")), "an empty old is allowed");
3118 assert_eq!(PrefixMap::split("old="), Some(("old", "")), "and so is an empty new");
3119 // The last rather than the first, so a directory whose name has an `=` in it can be
3120 // mapped and a replacement whose name has one cannot. That is gcc's choice of which of
3121 // the two to make possible, and it is the right way round.
3122 assert_eq!(PrefixMap::split("/home/a=b=/src"), Some(("/home/a=b", "/src")));
3123 assert_eq!(PrefixMap::split("nope"), None);
3124 }
3125
3126 #[test]
3127 fn optimisation_levels_parse_the_way_gcc_spells_them() {
3128 assert_eq!("".parse::<OptLevel>().unwrap(), OptLevel::O1);
3129 assert_eq!("0".parse::<OptLevel>().unwrap(), OptLevel::O0);
3130 assert_eq!("2".parse::<OptLevel>().unwrap(), OptLevel::O2);
3131 assert_eq!("9".parse::<OptLevel>().unwrap(), OptLevel::O3);
3132 assert_eq!("s".parse::<OptLevel>().unwrap(), OptLevel::Os);
3133 assert!("q".parse::<OptLevel>().is_err());
3134 }
3135
3136 #[test]
3137 fn only_o0_skips_the_optimizer() {
3138 assert!(!OptLevel::O0.runs_optimizer());
3139 assert!(OptLevel::O1.runs_optimizer());
3140 assert!(OptLevel::Oz.runs_optimizer());
3141 }
3142
3143 #[test]
3144 fn the_safety_tiers_round_trip_and_nothing_else_is_one() {
3145 for tier in [Safety::Off, Safety::Detect, Safety::Enforce, Safety::Kernel] {
3146 assert_eq!(tier.as_str().parse::<Safety>().unwrap(), tier);
3147 }
3148 // `on` is the obvious thing to try and it is not a tier, because which tier somebody
3149 // means by it is the whole question document 02 answers.
3150 assert!("on".parse::<Safety>().is_err());
3151 assert!("".parse::<Safety>().is_err());
3152 }
3153
3154 #[test]
3155 fn room_for_a_patcher_is_written_the_way_it_was_asked_for() {
3156 for (written, total, before) in
3157 [("0", 0, 0), ("2", 2, 0), ("16", 16, 0), ("5,3", 5, 3), ("3,3", 3, 3)]
3158 {
3159 let room: Patchable = written.parse().unwrap();
3160 assert_eq!(room, Patchable { total, before });
3161 assert_eq!(room.to_string(), written);
3162 assert_eq!(room.after(), total - before);
3163 assert_eq!(room.any(), total > 0);
3164 }
3165 // A second number of zero is the same request as no second number, and it is written back
3166 // the shorter way, which is the way somebody reaching for the flag writes it.
3167 assert_eq!("2,0".parse::<Patchable>().unwrap().to_string(), "2");
3168 }
3169
3170 #[test]
3171 fn more_room_in_front_of_the_label_than_there_is_room_at_all_is_refused() {
3172 // Rather than clamped, because there is no reading of it a caller meant. gcc says the same
3173 // about each of these.
3174 assert!("1,2".parse::<Patchable>().is_err());
3175 assert!("1,2,3".parse::<Patchable>().is_err());
3176 assert!("a".parse::<Patchable>().is_err());
3177 assert!("".parse::<Patchable>().is_err());
3178 assert!("-1".parse::<Patchable>().is_err());
3179 }
3180
3181 #[test]
3182 fn the_two_places_the_intermediate_files_can_go_are_the_two_words_that_are_taken() {
3183 assert_eq!("obj".parse::<SaveTemps>().unwrap(), SaveTemps::Object);
3184 assert_eq!("cwd".parse::<SaveTemps>().unwrap(), SaveTemps::Cwd);
3185 // The names of the two flags that mean the same thing as `=obj` are not themselves
3186 // arguments of it, and neither is silence.
3187 assert!("obj,cwd".parse::<SaveTemps>().is_err());
3188 assert!("".parse::<SaveTemps>().is_err());
3189 // Nothing is kept unless something asked, and both of the words that ask do ask.
3190 assert_eq!(SaveTemps::default(), SaveTemps::No);
3191 assert!(!SaveTemps::No.wanted());
3192 assert!(SaveTemps::Object.wanted());
3193 assert!(SaveTemps::Cwd.wanted());
3194 }
3195
3196 #[test]
3197 fn a_build_that_did_not_ask_for_the_monitor_does_not_get_it() {
3198 assert_eq!(Safety::default(), Safety::Off);
3199 assert!(!Safety::Off.instruments());
3200 assert!(Safety::Detect.instruments());
3201 assert!(Safety::Enforce.instruments());
3202 assert!(Safety::Kernel.instruments());
3203 }
3204
3205 #[test]
3206 fn emit_kinds_round_trip_through_their_names() {
3207 for k in [
3208 EmitKind::Executable,
3209 EmitKind::Object,
3210 EmitKind::Asm,
3211 EmitKind::Preprocessed,
3212 EmitKind::Tast,
3213 EmitKind::Ir,
3214 EmitKind::MirFinal,
3215 EmitKind::SyntaxOnly,
3216 ] {
3217 assert_eq!(k.as_str().parse::<EmitKind>().unwrap(), k);
3218 }
3219 }
3220
3221 #[test]
3222 fn errors_are_counted_and_warnings_are_not() {
3223 let mut s = session();
3224 s.emit(Diagnostic::error("no", rucc_diag::Span::DUMMY));
3225 s.emit(Diagnostic::warning("hmm", rucc_diag::Span::DUMMY));
3226 assert_eq!(s.error_count(), 1);
3227 assert_eq!(s.warning_count(), 1);
3228 assert!(s.has_errors());
3229 assert_eq!(s.diagnostics().len(), 2);
3230 }
3231
3232 #[test]
3233 fn werror_promotes_once_at_the_sink() {
3234 let mut opts = Options::new("x86_64-unknown-linux-gnu".parse().unwrap());
3235 opts.warnings_are_errors = true;
3236 let mut s = Session::new(opts);
3237 s.emit(Diagnostic::warning("hmm", rucc_diag::Span::DUMMY));
3238 assert_eq!(s.error_count(), 1);
3239 assert_eq!(s.warning_count(), 0);
3240 assert_eq!(s.diagnostics()[0].severity, Severity::Error);
3241 }
3242
3243 #[test]
3244 fn the_error_limit_can_be_switched_off() {
3245 let mut opts = Options::new("x86_64-unknown-linux-gnu".parse().unwrap());
3246 opts.error_limit = 0;
3247 let mut s = Session::new(opts);
3248 for _ in 0..100 {
3249 s.emit(Diagnostic::error("no", rucc_diag::Span::DUMMY));
3250 }
3251 assert!(!s.error_limit_reached());
3252 }
3253
3254 #[test]
3255 fn the_session_carries_the_source_map_spans_are_resolved_against() {
3256 let mut s = session();
3257 let file = s.sources.add("a.c", b"int x;\n".to_vec()).unwrap();
3258 let start = s.sources.file(file).start;
3259 assert_eq!(s.sources.render_position(start + 4), "a.c:1:5");
3260 }
3261
3262 #[test]
3263 fn the_session_carries_the_resolved_target() {
3264 let s = session();
3265 assert_eq!(s.target.pointer_width, 64);
3266 assert!(s.target.char_is_signed);
3267 }
3268
3269 #[test]
3270 fn short_wchar_makes_wchar_t_sixteen_bits_and_unsigned() {
3271 assert_eq!((session().target.wchar_width, session().target.wchar_is_signed), (32, true));
3272 let mut opts = Options::new("x86_64-unknown-linux-gnu".parse().unwrap());
3273 opts.short_wchar = true;
3274 let s = Session::new(opts);
3275 assert_eq!((s.target.wchar_width, s.target.wchar_is_signed), (16, false));
3276 }
3277}