rucc_ir/module.rs
1//! The module: the target it is for, its functions, its globals, its aliases and its metadata.
2//!
3//! Design: `spec/08-ir.md` sections 8.1 and 8.8.
4//!
5//! A module is one translation unit, or after LTO the several that were linked into one. It
6//! owns the functions rather than pointing at them, so the whole of a compilation is one value
7//! that is dropped in one go, and a reference to anything in it is a four-byte index.
8//!
9//! # Globals are bytes, not values
10//!
11//! There are no aggregate types in the IR, so a global's initializer cannot be a typed
12//! constant the way it is in LLVM. It is a sized, aligned image described by a run of
13//! [`Datum`]s: zero bytes, literal bytes, a scalar of a given IR type, or the address of
14//! another symbol. That is what an object file wants anyway, it needs no type the type system
15//! does not have, and a large `static const` table costs one [`Datum`] rather than one per
16//! element.
17//!
18//! # What the module does not hold
19//!
20//! It does not hold an [`Interner`](rucc_base::Interner). Every name in here is a
21//! [`Symbol`], and resolving one back to text needs the interner it came from, which the
22//! printer takes as an argument the way `rucc_ast::print` does. A module that owned one could
23//! not be built from the same session as the AST it was lowered from.
24//!
25//! Function attributes are not here yet. They arrive with the printer, which is where their
26//! spelling has to be settled.
27
28use std::collections::HashMap;
29use std::fmt;
30use std::ops::{Index, IndexMut};
31
32use rucc_base::float::Format;
33use rucc_base::{Idx, IdxRange, Symbol};
34use rucc_target::TargetInfo;
35use rucc_tuple::TargetTuple;
36
37use crate::func::Func;
38#[cfg(test)]
39use crate::inst::TbaaNode;
40use crate::inst::{Imm, Meta, MetaNode};
41use crate::ty::Type;
42
43/// A function in a module.
44pub type FuncId = Idx<Func>;
45
46/// A global variable in a module.
47pub type GlobalId = Idx<Global>;
48
49/// An alias in a module.
50pub type AliasId = Idx<Alias>;
51
52/// A run of [`Datum`]s in a module's data pool, which is what a global's initializer is.
53pub type DataList = IdxRange<Datum>;
54
55/// Marker for the byte pool, so that a range into it cannot be confused with any other range.
56#[derive(Debug)]
57pub struct Byte;
58
59/// A run of literal bytes in a module's byte pool.
60pub type ByteRange = IdxRange<Byte>;
61
62/// How a symbol is seen outside the object it is defined in.
63///
64/// The set is the one C needs and no more. C++ vague linkage and the ODR variants are not
65/// here because nothing produces them.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
67pub enum Linkage {
68 /// Defined here and visible to every other object. The default, and what a plain
69 /// definition at file scope gets.
70 #[default]
71 External,
72 /// Defined here and invisible outside it, which is what `static` at file scope means.
73 Internal,
74 /// Defined here, visible, and allowed to be replaced by a strong definition elsewhere.
75 /// `__attribute__((weak))`. A reference to one that nothing defines is a null address
76 /// rather than a link error.
77 Weak,
78 /// Defined here, visible, and allowed to be identical to a definition in another object,
79 /// with one of them kept and the rest discarded. What `extern inline` under the GNU
80 /// semantics and a compiler-generated helper get.
81 LinkOnce,
82 /// A tentative definition, which the linker merges with any other tentative definition of
83 /// the same name and any real definition. `int x;` at file scope under `-fcommon`.
84 Common,
85}
86
87impl Linkage {
88 /// The spelling in the textual form.
89 #[must_use]
90 pub const fn name(self) -> &'static str {
91 match self {
92 Self::External => "external",
93 Self::Internal => "internal",
94 Self::Weak => "weak",
95 Self::LinkOnce => "linkonce",
96 Self::Common => "common",
97 }
98 }
99
100 /// The linkage that spelling names.
101 #[must_use]
102 pub fn from_name(name: &str) -> Option<Self> {
103 Self::all().find(|linkage| linkage.name() == name)
104 }
105
106 /// Every linkage, in declaration order.
107 pub fn all() -> impl Iterator<Item = Self> {
108 [Self::External, Self::Internal, Self::Weak, Self::LinkOnce, Self::Common].into_iter()
109 }
110
111 /// Whether the symbol is invisible outside this object, so that a pass may rewrite every
112 /// use of it because it can see every use of it.
113 #[must_use]
114 pub const fn is_local(self) -> bool {
115 matches!(self, Self::Internal)
116 }
117
118 /// Whether the definition here may lose to one in another object at link time.
119 ///
120 /// The optimizer must not fold a use against the definition it can see when this is true,
121 /// because the definition that wins may be a different one.
122 #[must_use]
123 pub const fn may_be_replaced(self) -> bool {
124 matches!(self, Self::Weak | Self::LinkOnce | Self::Common)
125 }
126}
127
128/// What the dynamic linker is allowed to do with a symbol.
129///
130/// Orthogonal to [`Linkage`], which is about the static linker. A hidden symbol is still
131/// external as far as the object file is concerned; it just does not go in the dynamic symbol
132/// table, so nothing outside the shared object can interpose it.
133#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
134pub enum Visibility {
135 /// Exported and interposable, which is what a symbol in a shared library gets unless
136 /// something says otherwise.
137 #[default]
138 Default,
139 /// Not in the dynamic symbol table at all. `__attribute__((visibility("hidden")))` and
140 /// `-fvisibility=hidden`.
141 Hidden,
142 /// In the dynamic symbol table, but a reference from inside this shared object always
143 /// binds to the definition inside it.
144 Protected,
145}
146
147impl Visibility {
148 /// The spelling in the textual form.
149 #[must_use]
150 pub const fn name(self) -> &'static str {
151 match self {
152 Self::Default => "default",
153 Self::Hidden => "hidden",
154 Self::Protected => "protected",
155 }
156 }
157
158 /// The visibility that spelling names.
159 #[must_use]
160 pub fn from_name(name: &str) -> Option<Self> {
161 Self::all().find(|visibility| visibility.name() == name)
162 }
163
164 /// Every visibility, in declaration order.
165 pub fn all() -> impl Iterator<Item = Self> {
166 [Self::Default, Self::Hidden, Self::Protected].into_iter()
167 }
168}
169
170/// Whether a name is in another DLL, or is offered to other DLLs by this one.
171///
172/// Only a COFF target reads it, and it is orthogonal to [`Linkage`] and [`Visibility`] the way
173/// those two are to each other. `__declspec(dllimport)` and `__declspec(dllexport)`, which are the
174/// GNU attributes of the same names written the way Windows headers write them.
175#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
176pub enum Dll {
177 /// Neither, which is what every name on every other format is.
178 #[default]
179 Default,
180 /// Defined in another DLL and reached through the pointer the loader fills in for it, which
181 /// the import library names `__imp_` and the name. Only on a declaration.
182 Import,
183 /// Offered to other DLLs by the one this unit is linked into, which is a line in the object's
184 /// `.drectve` section. Only on a definition.
185 Export,
186}
187
188impl Dll {
189 /// The spelling in the textual form.
190 #[must_use]
191 pub const fn name(self) -> &'static str {
192 match self {
193 Self::Default => "default",
194 Self::Import => "import",
195 Self::Export => "export",
196 }
197 }
198
199 /// The storage that spelling names.
200 #[must_use]
201 pub fn from_name(name: &str) -> Option<Self> {
202 Self::all().find(|dll| dll.name() == name)
203 }
204
205 /// Every one, in declaration order.
206 pub fn all() -> impl Iterator<Item = Self> {
207 [Self::Default, Self::Import, Self::Export].into_iter()
208 }
209}
210
211/// Which link the module is being compiled for.
212///
213/// Everything this compiler writes is position independent, so this is not about whether there are
214/// absolute addresses in the text. It is about whether the link that reads the object puts every
215/// name in the same program. An executable is such a link and a shared library is not, and that
216/// decides whether a name is one another object may define or replace, which is the question
217/// [`Self::replaceable`] answers and the reason the field is carried this far down.
218///
219/// `-fPIC` and `-fPIE` on the command line. The expensive answer is the one that has to be asked
220/// for, which is gcc's arrangement.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
222pub enum Pic {
223 /// The link puts every name in one program. `-fPIE` and the default.
224 #[default]
225 Executable,
226 /// The output may end up in a shared library. `-fPIC`.
227 Library,
228}
229
230impl Pic {
231 /// Whether another object may define or replace a name with that linkage and that visibility.
232 ///
233 /// Nothing is replaceable in an executable. The definition in the executable is the one the
234 /// whole program uses, and a reference to a variable some library defines is answered by
235 /// making room for it in the executable and copying it there, so even a name this file only
236 /// declares ends up somewhere this file could have measured the distance to.
237 ///
238 /// In a shared library the exported names are, which is the whole of what exporting means: the
239 /// dynamic linker looks a name up in load order and the first definition it finds is the one
240 /// everything in the process uses, so a library that reached its own copy from the instruction
241 /// pointer would be the one part of the program not using it. Hidden and protected names are
242 /// not, since one is not in the table to be looked up and the other says a reference from
243 /// inside binds to the definition inside. `static` is not, for the reason it is never anything.
244 #[must_use]
245 pub const fn replaceable(self, linkage: Linkage, visibility: Visibility) -> bool {
246 match self {
247 Self::Executable => false,
248 Self::Library => match visibility {
249 Visibility::Hidden | Visibility::Protected => false,
250 Visibility::Default => !matches!(linkage, Linkage::Internal),
251 },
252 }
253 }
254}
255
256/// How a thread-local variable is reached.
257///
258/// The models are ordered from the most general to the fastest, and a model may always be
259/// replaced by a more general one. The frontend picks from the storage class and the
260/// visibility, `-ftls-model=` overrides it, and the linker may relax a general one into a
261/// faster one when it turns out the definition is in the executable.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
263pub enum TlsModel {
264 /// Works for any variable in any object, at the cost of a call to `__tls_get_addr`.
265 #[default]
266 GlobalDynamic,
267 /// One call to `__tls_get_addr` for several variables that are known to share a module.
268 LocalDynamic,
269 /// The offset is loaded from the GOT. Needs the variable to be in a module loaded at
270 /// program start rather than by `dlopen`.
271 InitialExec,
272 /// The offset is a link-time constant. Only for a variable in the executable itself.
273 LocalExec,
274}
275
276impl TlsModel {
277 /// The spelling in the textual form.
278 #[must_use]
279 pub const fn name(self) -> &'static str {
280 match self {
281 Self::GlobalDynamic => "global_dynamic",
282 Self::LocalDynamic => "local_dynamic",
283 Self::InitialExec => "initial_exec",
284 Self::LocalExec => "local_exec",
285 }
286 }
287
288 /// The model that spelling names.
289 #[must_use]
290 pub fn from_name(name: &str) -> Option<Self> {
291 Self::all().find(|model| model.name() == name)
292 }
293
294 /// Every model, from the most general to the fastest.
295 pub fn all() -> impl Iterator<Item = Self> {
296 [Self::GlobalDynamic, Self::LocalDynamic, Self::InitialExec, Self::LocalExec].into_iter()
297 }
298}
299
300/// One piece of a global's initial image.
301///
302/// Sixteen bytes, so an initializer built out of them is a flat array and a table of a
303/// million bytes is one of these rather than a million.
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum Datum {
306 /// That many zero bytes. What `.bss` is made of, and what the tail of a partly
307 /// initialized array is.
308 Zero(u64),
309 /// Those literal bytes, from the module's byte pool. String literals and anything the
310 /// frontend has already laid out.
311 Bytes(ByteRange),
312 /// One scalar of that IR type, from the module's immediate pool. An integer holds its
313 /// value and a float holds its bit pattern, both target-independently: which byte comes
314 /// first is decided by the datalayout when the object file is written, not here.
315 Scalar {
316 /// The type of the scalar, which gives its width.
317 ty: Type,
318 /// Its value, in the module's immediate pool.
319 value: Idx<Imm>,
320 },
321 /// The address of another symbol, from the module's relocation pool. `&x` in an
322 /// initializer, which the linker fills in.
323 Addr(Idx<Reloc>),
324 /// How far another symbol is from where this is written, from the same pool. `.long
325 /// target - .` in an `asm` at file scope, which is what a table of places in a program
326 /// holds when the table and the places are both in it: the distance fits in four bytes
327 /// where an address takes eight, and it is the same number wherever the image is loaded,
328 /// so nothing has to be written into it at startup.
329 Away(Idx<Reloc>),
330 /// How far the symbol in the relocation is from another, `.long to - from`. GNU C's
331 /// `&&to - &&from` in an initializer, where both are labels of one function and the distance
332 /// is a number once the function is laid out, so the assembler writes it and the linker is
333 /// never asked. The relocation says where it is measured to and how wide it is written.
334 Apart {
335 /// The place the distance is measured to, with what to add and the width.
336 to: Idx<Reloc>,
337 /// The place it is measured from.
338 from: Symbol,
339 },
340}
341
342impl Datum {
343 /// How many bytes it contributes to the image.
344 ///
345 /// The module is an argument because four of the five kinds keep what they are made of in
346 /// one of its pools, and a datum on its own is four words that mean nothing without it.
347 #[must_use]
348 pub fn size(self, module: &Module) -> u64 {
349 match self {
350 Self::Zero(bytes) => bytes,
351 Self::Bytes(range) => range.len() as u64,
352 // Rounded up, so that an `i1` in an image is a byte and a `_BitInt(24)` is three.
353 Self::Scalar { ty, .. } => u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes()),
354 Self::Addr(reloc) | Self::Away(reloc) | Self::Apart { to: reloc, .. } => {
355 u64::from(module[reloc].size)
356 }
357 }
358 }
359}
360
361/// The address of a symbol, written into a global's image by the linker.
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
363pub struct Reloc {
364 /// The symbol whose address this is.
365 pub symbol: Symbol,
366 /// What to add to that address. `&array[2]` is the address of `array` plus eight.
367 pub addend: i64,
368 /// How many bytes the address occupies, which is the pointer width except where a target
369 /// has a smaller relocation for it.
370 pub size: u32,
371}
372
373/// A global variable.
374///
375/// A size and an alignment and an image, which is what the object writer needs. `init` is
376/// `None` for a declaration of something defined in another object, which is the only thing
377/// that distinguishes the two.
378#[derive(Debug, Clone)]
379pub struct Global {
380 /// The name it is reached by.
381 pub name: Symbol,
382 /// Its size in bytes, which the image must add up to.
383 pub size: u64,
384 /// Its required alignment in bytes, always a power of two.
385 pub align: u32,
386 /// How the linker sees it.
387 pub linkage: Linkage,
388 /// How the dynamic linker sees it.
389 pub visibility: Visibility,
390 /// Whether it is in another DLL or offered to others by this one, which only a COFF target
391 /// reads.
392 pub dll: Dll,
393 /// The model to reach it by if it is thread-local, and `None` if it is not.
394 pub tls: Option<TlsModel>,
395 /// Whether writing through a pointer to it is undefined, which is what puts it in
396 /// `.rodata` rather than `.data`.
397 pub constant: bool,
398 /// The section to put it in, from `__attribute__((section(...)))`, or `None` to let the
399 /// object writer choose from the other fields.
400 pub section: Option<Symbol>,
401 /// Its initial image, or `None` if it is only declared here.
402 pub init: Option<DataList>,
403}
404
405impl Global {
406 /// A definition-less global of that size and alignment, external and not thread-local.
407 #[must_use]
408 pub fn new(name: Symbol, size: u64, align: u32) -> Self {
409 Self {
410 name,
411 size,
412 align,
413 linkage: Linkage::External,
414 visibility: Visibility::Default,
415 dll: Dll::Default,
416 tls: None,
417 constant: false,
418 section: None,
419 init: None,
420 }
421 }
422
423 /// Whether this only says the variable exists somewhere.
424 #[must_use]
425 pub fn is_declaration(&self) -> bool {
426 self.init.is_none()
427 }
428}
429
430/// What an alias resolves to at link time.
431#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
432pub enum AliasKind {
433 /// A second name for a symbol in this same object, resolved by the assembler.
434 /// `__attribute__((alias("real")))`.
435 #[default]
436 Alias,
437 /// A name resolved once at program start by calling a resolver function in this object,
438 /// which picks an implementation from what the processor turns out to support.
439 /// `__attribute__((ifunc("resolver")))`, which is how glibc dispatches `memcpy`.
440 IFunc,
441}
442
443impl AliasKind {
444 /// The spelling in the textual form.
445 #[must_use]
446 pub const fn name(self) -> &'static str {
447 match self {
448 Self::Alias => "alias",
449 Self::IFunc => "ifunc",
450 }
451 }
452
453 /// The kind that spelling names.
454 #[must_use]
455 pub fn from_name(name: &str) -> Option<Self> {
456 match name {
457 "alias" => Some(Self::Alias),
458 "ifunc" => Some(Self::IFunc),
459 _ => None,
460 }
461 }
462}
463
464/// A second name for something else.
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub struct Alias {
467 /// The name being defined.
468 pub name: Symbol,
469 /// What it resolves to: the aliased symbol, or for an ifunc the resolver to call.
470 pub target: Symbol,
471 /// Which of those two it is.
472 pub kind: AliasKind,
473 /// How the linker sees the new name.
474 pub linkage: Linkage,
475 /// How the dynamic linker sees the new name.
476 pub visibility: Visibility,
477}
478
479impl Alias {
480 /// An external alias of `target`.
481 #[must_use]
482 pub fn new(name: Symbol, target: Symbol) -> Self {
483 Self {
484 name,
485 target,
486 kind: AliasKind::Alias,
487 linkage: Linkage::External,
488 visibility: Visibility::Default,
489 }
490 }
491}
492
493/// What a name in a module refers to.
494#[derive(Debug, Clone, Copy, PartialEq, Eq)]
495pub enum SymbolRef {
496 /// A function, defined or declared.
497 Func(FuncId),
498 /// A global variable, defined or declared.
499 Global(GlobalId),
500 /// An alias or an ifunc.
501 Alias(AliasId),
502}
503
504/// The layout facts a printed module carries so it can be compiled without the command line
505/// that produced it.
506///
507/// A subset of the string LLVM writes, in the same syntax, because that syntax is what tools
508/// around the ecosystem already read. It says what the module was built assuming, and the
509/// verifier is what checks it against the target actually being compiled for: a module built
510/// for a 64-bit pointer cannot be finished for a 32-bit one, and finding that out here is
511/// better than finding it out as wrong output.
512#[derive(Debug, Clone, Copy, PartialEq, Eq)]
513pub struct DataLayout {
514 /// Whether the low byte of a scalar is stored first.
515 pub little_endian: bool,
516 /// The width of a pointer in bits.
517 pub pointer_bits: u32,
518 /// The alignment of a pointer in bits.
519 pub pointer_align: u32,
520 /// The alignment of a 64-bit integer in bits, which is the one integer alignment that
521 /// varies across the targets anybody still builds for.
522 pub i64_align: u32,
523 /// The alignment of the x87 eighty bit format in bits, and `None` on a target that does
524 /// not have it.
525 pub f80_align: Option<u32>,
526 /// The alignment the stack is kept at in bits, which is 128 on every target here.
527 pub stack_align: u32,
528}
529
530impl DataLayout {
531 /// The layout of that target.
532 ///
533 /// # Panics
534 ///
535 /// If the target aligns a `long long` to more than half a billion bytes, which no target
536 /// does. The alignment is a byte count here and a bit count in the IR, and the multiplication
537 /// between the two is the only arithmetic in this function.
538 #[must_use]
539 pub fn for_target(target: &TargetInfo) -> Self {
540 Self {
541 little_endian: target.little_endian,
542 pointer_bits: target.pointer_width,
543 pointer_align: target.pointer_width,
544 // Four on System V i386 and eight everywhere else, which is the one integer
545 // alignment that varies across the table and the reason this is a field. It changes
546 // the layout of every struct with a `long long` in it.
547 i64_align: u32::try_from(target.scalars.long_long_align * 8)
548 .expect("no integer alignment is four billion bits"),
549 f80_align: match target.long_double_format {
550 Format::X87Extended => Some(128),
551 _ => None,
552 },
553 stack_align: 128,
554 }
555 }
556
557 /// The layout back from the string [`Display`](fmt::Display) wrote, or `None` if the
558 /// string is not one.
559 ///
560 /// The fields may come in any order, because a string written by hand will not have them
561 /// in ours. A string this crate printed round-trips byte for byte, which is what
562 /// `spec/03-architecture.md` asks of the textual form.
563 #[must_use]
564 pub fn parse(text: &str) -> Option<Self> {
565 let mut little_endian = None;
566 let mut pointer = None;
567 let mut i64_align = None;
568 let mut f80_align = None;
569 let mut stack_align = None;
570 for field in text.split('-') {
571 let seen = match field {
572 "e" => little_endian.replace(true).is_some(),
573 "E" => little_endian.replace(false).is_some(),
574 _ if field.starts_with("p:") => {
575 let (bits, align) = field[2..].split_once(':')?;
576 pointer.replace((number(bits)?, number(align)?)).is_some()
577 }
578 _ if field.starts_with("i64:") => i64_align.replace(number(&field[4..])?).is_some(),
579 _ if field.starts_with("f80:") => f80_align.replace(number(&field[4..])?).is_some(),
580 _ if field.starts_with('S') => stack_align.replace(number(&field[1..])?).is_some(),
581 _ => return None,
582 };
583 if seen {
584 return None;
585 }
586 }
587 let (pointer_bits, pointer_align) = pointer?;
588 Some(Self {
589 little_endian: little_endian?,
590 pointer_bits,
591 pointer_align,
592 i64_align: i64_align?,
593 f80_align,
594 stack_align: stack_align?,
595 })
596 }
597}
598
599impl fmt::Display for DataLayout {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 write!(f, "{}", if self.little_endian { "e" } else { "E" })?;
602 write!(f, "-p:{}:{}", self.pointer_bits, self.pointer_align)?;
603 write!(f, "-i64:{}", self.i64_align)?;
604 if let Some(align) = self.f80_align {
605 write!(f, "-f80:{align}")?;
606 }
607 write!(f, "-S{}", self.stack_align)
608 }
609}
610
611/// A number in the textual form: digits, no sign, and no leading zero.
612///
613/// `p:64:064` would otherwise parse and then print back as `p:64:64`, which breaks the
614/// round-trip for no benefit to anybody.
615fn number(text: &str) -> Option<u32> {
616 if text.is_empty() || (text.len() > 1 && text.starts_with('0')) {
617 return None;
618 }
619 if !text.bytes().all(|byte| byte.is_ascii_digit()) {
620 return None;
621 }
622 text.parse().ok()
623}
624
625/// One translation unit, or after LTO the several that were linked into one.
626#[derive(Debug)]
627pub struct Module {
628 /// What it is called, which is the source file name for a module from the frontend. It
629 /// appears in the textual form and in the debug info and nothing branches on it.
630 pub name: Symbol,
631 /// The target it is for.
632 pub tuple: TargetTuple,
633 /// The layout it was built assuming.
634 pub datalayout: DataLayout,
635
636 funcs: Vec<Func>,
637 globals: Vec<Global>,
638 aliases: Vec<Alias>,
639 metadata: Vec<MetaNode>,
640
641 data: Vec<Datum>,
642 bytes: Vec<u8>,
643 imms: Vec<Imm>,
644 relocs: Vec<Reloc>,
645
646 symbols: HashMap<Symbol, SymbolRef>,
647
648 /// The `asm` written at file scope that the lowering could not read into globals, which is one
649 /// with an instruction in it, kept as the text it was written as. See
650 /// [`Module::add_file_asm`].
651 file_asm: Vec<String>,
652
653 /// What the unit asks the linker for, as the options a COFF linker reads out of `.drectve`.
654 /// See [`Module::add_linker_option`].
655 linker_options: Vec<String>,
656}
657
658impl Module {
659 /// An empty module for that target.
660 #[must_use]
661 pub fn new(name: Symbol, target: &TargetInfo) -> Self {
662 Self {
663 name,
664 tuple: target.tuple,
665 datalayout: DataLayout::for_target(target),
666 funcs: Vec::new(),
667 globals: Vec::new(),
668 aliases: Vec::new(),
669 metadata: Vec::new(),
670 data: Vec::new(),
671 bytes: Vec::new(),
672 imms: Vec::new(),
673 relocs: Vec::new(),
674 symbols: HashMap::new(),
675 file_asm: Vec::new(),
676 linker_options: Vec::new(),
677 }
678 }
679
680 /// Keeps the text of an `asm` at file scope to be handed to the assembler as it was written.
681 ///
682 /// What it defines is invisible here: a function written in one is a declaration as far as
683 /// the IR knows, and the definition turns up when the listing is read. That is gcc's contract
684 /// too, where the template goes into the output between `#APP` and `#NO_APP` and nothing
685 /// before the assembler looks inside it.
686 pub fn add_file_asm(&mut self, text: String) {
687 self.file_asm.push(text);
688 }
689
690 /// The text of each `asm` at file scope kept by [`Module::add_file_asm`], in the order they
691 /// were written.
692 #[must_use]
693 pub fn file_asms(&self) -> &[String] {
694 &self.file_asm
695 }
696
697 /// Keeps an option for the linker, written the way the linker reads it, such as
698 /// `/DEFAULTLIB:ws2_32.lib` for `#pragma comment(lib, "ws2_32")`. The object writer puts it in
699 /// `.drectve` on COFF, which is the only format with somewhere to put it.
700 pub fn add_linker_option(&mut self, option: String) {
701 self.linker_options.push(option);
702 }
703
704 /// The options kept by [`Module::add_linker_option`], in the order they were added.
705 #[must_use]
706 pub fn linker_options(&self) -> &[String] {
707 &self.linker_options
708 }
709
710 // Symbols.
711
712 /// Adds a function, which is a declaration if it has no blocks.
713 ///
714 /// # Panics
715 ///
716 /// Panics if the module already has a symbol of that name. Merging a declaration with a
717 /// definition is the frontend's job and it has the declarations to do it with; by the time
718 /// something is in the IR a name means one thing.
719 pub fn add_func(&mut self, func: Func) -> FuncId {
720 let id = Idx::from_usize(self.funcs.len());
721 self.claim(func.name, SymbolRef::Func(id));
722 self.funcs.push(func);
723 id
724 }
725
726 /// Adds a global variable, which is a declaration if it has no image.
727 ///
728 /// # Panics
729 ///
730 /// Panics if the module already has a symbol of that name.
731 pub fn add_global(&mut self, global: Global) -> GlobalId {
732 let id = Idx::from_usize(self.globals.len());
733 self.claim(global.name, SymbolRef::Global(id));
734 self.globals.push(global);
735 id
736 }
737
738 /// Adds an alias.
739 ///
740 /// The target is not resolved here, and it need not be in this module: an alias of
741 /// something in another object is a thing people write.
742 ///
743 /// # Panics
744 ///
745 /// Panics if the module already has a symbol of that name.
746 pub fn add_alias(&mut self, alias: Alias) -> AliasId {
747 let id = Idx::from_usize(self.aliases.len());
748 self.claim(alias.name, SymbolRef::Alias(id));
749 self.aliases.push(alias);
750 id
751 }
752
753 /// Adds an alias under a name the module so far only declared, which it then stands for.
754 ///
755 /// The declaration stays where it was and is still a declaration, so whatever refers to it
756 /// goes on naming the same symbol, and the symbol is now the alias. That is what an assembler
757 /// does with `.set f, g` below a C declaration of `f`.
758 ///
759 /// # Panics
760 ///
761 /// Panics if the module already defines that name.
762 pub fn add_alias_over(&mut self, alias: Alias) -> AliasId {
763 let declared = match self.symbols.remove(&alias.name) {
764 None => true,
765 Some(SymbolRef::Func(id)) => self[id].is_declaration(),
766 Some(SymbolRef::Global(id)) => self[id].is_declaration(),
767 Some(SymbolRef::Alias(_)) => false,
768 };
769 assert!(declared, "an alias can only take the place of a declaration");
770 self.add_alias(alias)
771 }
772
773 /// What that name refers to, or `None` if this module does not define or declare it.
774 #[must_use]
775 pub fn lookup(&self, name: Symbol) -> Option<SymbolRef> {
776 self.symbols.get(&name).copied()
777 }
778
779 /// Every function, in the order they were added.
780 pub fn funcs(&self) -> impl Iterator<Item = FuncId> + use<> {
781 (0..self.funcs.len()).map(Idx::from_usize)
782 }
783
784 /// Every global variable, in the order they were added.
785 pub fn globals(&self) -> impl Iterator<Item = GlobalId> + use<> {
786 (0..self.globals.len()).map(Idx::from_usize)
787 }
788
789 /// Every alias, in the order they were added.
790 pub fn aliases(&self) -> impl Iterator<Item = AliasId> + use<> {
791 (0..self.aliases.len()).map(Idx::from_usize)
792 }
793
794 fn claim(&mut self, name: Symbol, what: SymbolRef) {
795 assert!(
796 self.symbols.insert(name, what).is_none(),
797 "a module cannot have two symbols with the same name"
798 );
799 }
800
801 // Metadata.
802
803 /// Adds a metadata node and gives back the reference an instruction holds.
804 ///
805 /// The nodes live here rather than in a function because a TBAA tree is shared by every
806 /// memory operation in the module and duplicating it per function would make two accesses
807 /// to the same type look unrelated.
808 pub fn add_meta(&mut self, node: MetaNode) -> Meta {
809 self.metadata.push(node);
810 Idx::from_usize(self.metadata.len() - 1)
811 }
812
813 /// Every metadata node, in the order they were added.
814 pub fn metadata(&self) -> impl Iterator<Item = Meta> + use<> {
815 (0..self.metadata.len()).map(Idx::from_usize)
816 }
817
818 // Pools.
819
820 /// Records a run of data and gives back the list a global holds.
821 pub fn push_data(&mut self, data: &[Datum]) -> DataList {
822 let start = self.data.len();
823 self.data.extend_from_slice(data);
824 DataList::new(Idx::from_usize(start), Idx::from_usize(self.data.len()))
825 }
826
827 /// Records literal bytes and gives back the range a [`Datum::Bytes`] holds.
828 pub fn push_bytes(&mut self, bytes: &[u8]) -> ByteRange {
829 let start = self.bytes.len();
830 self.bytes.extend_from_slice(bytes);
831 ByteRange::new(Idx::from_usize(start), Idx::from_usize(self.bytes.len()))
832 }
833
834 /// Records a scalar value and gives back the index a [`Datum::Scalar`] holds.
835 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
836 self.imms.push(imm);
837 Idx::from_usize(self.imms.len() - 1)
838 }
839
840 /// Records a relocation and gives back the index a [`Datum::Addr`] holds.
841 pub fn add_reloc(&mut self, reloc: Reloc) -> Idx<Reloc> {
842 self.relocs.push(reloc);
843 Idx::from_usize(self.relocs.len() - 1)
844 }
845
846 /// Every relocation in the module, to be read or edited in place.
847 ///
848 /// A pool rather than a tree, so a pass that wants to rename what an initializer points at has
849 /// nothing to walk: the data lists hold indices into this and the symbol lives here. The one
850 /// pass that wants that is `rucc_safety::wrap`, which turns `&read` in a static initializer
851 /// into `&__rucc_wrap_read` so that a call through the pointer is a call the monitor modelled.
852 pub fn relocs_mut(&mut self) -> &mut [Reloc] {
853 &mut self.relocs
854 }
855
856 /// The same pool, to read. `rucc_safety::summary` walks it to find the names an initializer
857 /// mentions that the build has no wrapper for, which is a boundary it did not model.
858 #[must_use]
859 pub fn relocs(&self) -> &[Reloc] {
860 &self.relocs
861 }
862
863 /// How much is in it, for the `-fstats` output and for a test that wants to say a pass
864 /// deleted something without saying which.
865 #[must_use]
866 pub fn counts(&self) -> ModuleCounts {
867 ModuleCounts {
868 funcs: self.funcs.len(),
869 globals: self.globals.len(),
870 aliases: self.aliases.len(),
871 metadata: self.metadata.len(),
872 data_bytes: self.bytes.len(),
873 }
874 }
875}
876
877/// How much is in a module, from [`Module::counts`].
878#[derive(Debug, Clone, Copy, PartialEq, Eq)]
879pub struct ModuleCounts {
880 /// Functions, defined and declared.
881 pub funcs: usize,
882 /// Global variables, defined and declared.
883 pub globals: usize,
884 /// Aliases and ifuncs.
885 pub aliases: usize,
886 /// Metadata nodes.
887 pub metadata: usize,
888 /// Bytes in the byte pool, which is the bulk of what a module with large initializers
889 /// weighs.
890 pub data_bytes: usize,
891}
892
893impl Index<FuncId> for Module {
894 type Output = Func;
895
896 fn index(&self, id: FuncId) -> &Func {
897 &self.funcs[id.index()]
898 }
899}
900
901impl IndexMut<FuncId> for Module {
902 fn index_mut(&mut self, id: FuncId) -> &mut Func {
903 &mut self.funcs[id.index()]
904 }
905}
906
907impl Index<GlobalId> for Module {
908 type Output = Global;
909
910 fn index(&self, id: GlobalId) -> &Global {
911 &self.globals[id.index()]
912 }
913}
914
915impl IndexMut<GlobalId> for Module {
916 fn index_mut(&mut self, id: GlobalId) -> &mut Global {
917 &mut self.globals[id.index()]
918 }
919}
920
921impl Index<AliasId> for Module {
922 type Output = Alias;
923
924 fn index(&self, id: AliasId) -> &Alias {
925 &self.aliases[id.index()]
926 }
927}
928
929impl Index<Meta> for Module {
930 type Output = MetaNode;
931
932 fn index(&self, meta: Meta) -> &MetaNode {
933 &self.metadata[meta.index()]
934 }
935}
936
937impl Index<Idx<Imm>> for Module {
938 type Output = Imm;
939
940 fn index(&self, imm: Idx<Imm>) -> &Imm {
941 &self.imms[imm.index()]
942 }
943}
944
945impl Index<Idx<Reloc>> for Module {
946 type Output = Reloc;
947
948 fn index(&self, reloc: Idx<Reloc>) -> &Reloc {
949 &self.relocs[reloc.index()]
950 }
951}
952
953impl Index<DataList> for Module {
954 type Output = [Datum];
955
956 fn index(&self, list: DataList) -> &[Datum] {
957 &self.data[list.as_usize_range()]
958 }
959}
960
961impl Index<ByteRange> for Module {
962 type Output = [u8];
963
964 fn index(&self, range: ByteRange) -> &[u8] {
965 &self.bytes[range.as_usize_range()]
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use rucc_base::Interner;
972 use rucc_target::{Arch, Env, Os, Triple};
973
974 use super::*;
975 use crate::inst::Signature;
976
977 fn target(arch: Arch, os: Os, env: Env) -> TargetInfo {
978 TargetInfo::new(Triple::new(arch, os, env))
979 }
980
981 fn linux() -> TargetInfo {
982 target(Arch::X86_64, Os::Linux, Env::Gnu)
983 }
984
985 #[test]
986 fn a_datum_is_sixteen_bytes() {
987 // A global with a large initializer is a flat array of these, so this is the tripwire
988 // on somebody adding a field that doubles the weight of every one.
989 assert_eq!(size_of::<Datum>(), 16);
990 }
991
992 #[test]
993 fn the_layout_of_x86_64_linux_is_the_one_in_the_spec() {
994 let layout = DataLayout::for_target(&linux());
995 assert_eq!(layout.to_string(), "e-p:64:64-i64:64-f80:128-S128");
996 }
997
998 #[test]
999 fn only_x86_has_the_eighty_bit_format() {
1000 assert_eq!(DataLayout::for_target(&linux()).f80_align, Some(128));
1001 let arm = DataLayout::for_target(&target(Arch::Aarch64, Os::Linux, Env::Gnu));
1002 assert_eq!(arm.f80_align, None);
1003 assert_eq!(arm.to_string(), "e-p:64:64-i64:64-S128");
1004 }
1005
1006 #[test]
1007 fn a_layout_round_trips() {
1008 for triple in [
1009 Triple::new(Arch::X86_64, Os::Linux, Env::Gnu),
1010 Triple::new(Arch::X86_64, Os::Darwin, Env::None),
1011 Triple::new(Arch::Aarch64, Os::Darwin, Env::None),
1012 Triple::new(Arch::Riscv64, Os::Linux, Env::Musl),
1013 ] {
1014 let layout = DataLayout::for_target(&TargetInfo::new(triple));
1015 let text = layout.to_string();
1016 assert_eq!(DataLayout::parse(&text), Some(layout), "{text}");
1017 }
1018 }
1019
1020 #[test]
1021 fn a_layout_may_be_written_in_any_order() {
1022 let text = "S128-i64:64-f80:128-p:64:64-e";
1023 assert_eq!(DataLayout::parse(text), Some(DataLayout::for_target(&linux())));
1024 }
1025
1026 #[test]
1027 fn a_layout_needs_every_field_it_prints() {
1028 for text in ["", "e", "e-p:64:64-S128", "e-i64:64-S128", "e-p:64:64-i64:64"] {
1029 assert_eq!(DataLayout::parse(text), None, "{text}");
1030 }
1031 }
1032
1033 #[test]
1034 fn a_layout_refuses_a_second_spelling() {
1035 // Each of these would print back as something else, which breaks the round-trip.
1036 for text in ["e-p:64:064-i64:64-S128", "e-e-p:64:64-i64:64-S128", "e-p:64:64-i64:64-S128-x"]
1037 {
1038 assert_eq!(DataLayout::parse(text), None, "{text}");
1039 }
1040 }
1041
1042 #[test]
1043 fn a_module_finds_what_it_holds() {
1044 let mut names = Interner::new();
1045 let mut module = Module::new(names.intern("test.c"), &linux());
1046
1047 let counter = names.intern("counter");
1048 let sum = names.intern("sum");
1049 let total = names.intern("total");
1050
1051 let global = module.add_global(Global::new(counter, 4, 4));
1052 let func = module.add_func(Func::new(sum, Signature::new()));
1053 let alias = module.add_alias(Alias::new(total, counter));
1054
1055 assert_eq!(module.lookup(counter), Some(SymbolRef::Global(global)));
1056 assert_eq!(module.lookup(sum), Some(SymbolRef::Func(func)));
1057 assert_eq!(module.lookup(total), Some(SymbolRef::Alias(alias)));
1058 assert_eq!(module.lookup(names.intern("nothing")), None);
1059 assert_eq!(module[alias].target, counter);
1060 assert!(module[global].is_declaration());
1061 assert!(module[func].is_declaration());
1062 }
1063
1064 #[test]
1065 #[should_panic(expected = "two symbols with the same name")]
1066 fn a_name_means_one_thing() {
1067 let mut names = Interner::new();
1068 let mut module = Module::new(names.intern("test.c"), &linux());
1069 let name = names.intern("x");
1070 module.add_global(Global::new(name, 4, 4));
1071 module.add_func(Func::new(name, Signature::new()));
1072 }
1073
1074 #[test]
1075 fn an_initializer_adds_up_to_the_size() {
1076 let mut names = Interner::new();
1077 let mut module = Module::new(names.intern("test.c"), &linux());
1078
1079 // struct { int n; const char *name; char pad[6]; } = { 7, "hi", { 0 } };
1080 let text = names.intern("hi.str");
1081 let seven = module.add_imm(Imm::int(7, Type::int(32)));
1082 let bytes = module.push_bytes(b"hi\0");
1083 let addr = module.add_reloc(Reloc { symbol: text, addend: 0, size: 8 });
1084 let init = module.push_data(&[
1085 Datum::Scalar { ty: Type::int(32), value: seven },
1086 Datum::Zero(4),
1087 Datum::Addr(addr),
1088 // The six bytes of `pad` and the two the struct is tailed out with. Padding is
1089 // the frontend's arithmetic, and the image is what it came out as.
1090 Datum::Zero(8),
1091 ]);
1092
1093 let mut global = Global::new(names.intern("entry"), 24, 8);
1094 global.init = Some(init);
1095 global.constant = true;
1096 let id = module.add_global(global);
1097
1098 assert!(!module[id].is_declaration());
1099 let size: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
1100 assert_eq!(size, module[id].size);
1101 assert_eq!(&module[bytes], b"hi\0");
1102 assert_eq!(module[seven].unsigned(), 7);
1103 assert_eq!(module.counts().data_bytes, 3);
1104 }
1105
1106 #[test]
1107 fn a_scalar_datum_is_as_wide_as_its_type() {
1108 let mut names = Interner::new();
1109 let mut module = Module::new(names.intern("test.c"), &linux());
1110 let value = module.add_imm(Imm::int(0, Type::int(32)));
1111 assert_eq!(Datum::Scalar { ty: Type::int(32), value }.size(&module), 4);
1112 // Rounded up to whole bytes, one lane at a time.
1113 assert_eq!(Datum::Scalar { ty: Type::I1, value }.size(&module), 1);
1114 assert_eq!(Datum::Scalar { ty: Type::int(24), value }.size(&module), 3);
1115 assert_eq!(Datum::Scalar { ty: Type::vector(Type::int(8), 16), value }.size(&module), 16);
1116 }
1117
1118 #[test]
1119 fn the_names_round_trip() {
1120 for linkage in Linkage::all() {
1121 assert_eq!(Linkage::from_name(linkage.name()), Some(linkage));
1122 }
1123 for visibility in Visibility::all() {
1124 assert_eq!(Visibility::from_name(visibility.name()), Some(visibility));
1125 }
1126 for model in TlsModel::all() {
1127 assert_eq!(TlsModel::from_name(model.name()), Some(model));
1128 }
1129 for kind in [AliasKind::Alias, AliasKind::IFunc] {
1130 assert_eq!(AliasKind::from_name(kind.name()), Some(kind));
1131 }
1132 assert_eq!(Linkage::from_name("static"), None);
1133 assert_eq!(Visibility::from_name("internal"), None);
1134 }
1135
1136 #[test]
1137 fn only_internal_linkage_is_local() {
1138 for linkage in Linkage::all() {
1139 assert_eq!(linkage.is_local(), linkage == Linkage::Internal);
1140 assert_eq!(
1141 linkage.may_be_replaced(),
1142 !matches!(linkage, Linkage::External | Linkage::Internal)
1143 );
1144 }
1145 }
1146
1147 #[test]
1148 fn metadata_is_shared_by_the_whole_module() {
1149 let mut names = Interner::new();
1150 let mut module = Module::new(names.intern("test.c"), &linux());
1151 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1152 name: names.intern("omnipotent char"),
1153 parent: None,
1154 offset: 0,
1155 }));
1156 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1157 name: names.intern("int"),
1158 parent: Some(char_node),
1159 offset: 0,
1160 }));
1161 assert_eq!(module[int_node].parent(), Some(char_node));
1162 assert_eq!(module.metadata().count(), 2);
1163 }
1164}