Skip to main content

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/// Which link the module is being compiled for.
171///
172/// Everything this compiler writes is position independent, so this is not about whether there are
173/// absolute addresses in the text. It is about whether the link that reads the object puts every
174/// name in the same program. An executable is such a link and a shared library is not, and that
175/// decides whether a name is one another object may define or replace, which is the question
176/// [`Self::replaceable`] answers and the reason the field is carried this far down.
177///
178/// `-fPIC` and `-fPIE` on the command line. The expensive answer is the one that has to be asked
179/// for, which is gcc's arrangement.
180#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
181pub enum Pic {
182    /// The link puts every name in one program. `-fPIE` and the default.
183    #[default]
184    Executable,
185    /// The output may end up in a shared library. `-fPIC`.
186    Library,
187}
188
189impl Pic {
190    /// Whether another object may define or replace a name with that linkage and that visibility.
191    ///
192    /// Nothing is replaceable in an executable. The definition in the executable is the one the
193    /// whole program uses, and a reference to a variable some library defines is answered by
194    /// making room for it in the executable and copying it there, so even a name this file only
195    /// declares ends up somewhere this file could have measured the distance to.
196    ///
197    /// In a shared library the exported names are, which is the whole of what exporting means: the
198    /// dynamic linker looks a name up in load order and the first definition it finds is the one
199    /// everything in the process uses, so a library that reached its own copy from the instruction
200    /// pointer would be the one part of the program not using it. Hidden and protected names are
201    /// not, since one is not in the table to be looked up and the other says a reference from
202    /// inside binds to the definition inside. `static` is not, for the reason it is never anything.
203    #[must_use]
204    pub const fn replaceable(self, linkage: Linkage, visibility: Visibility) -> bool {
205        match self {
206            Self::Executable => false,
207            Self::Library => match visibility {
208                Visibility::Hidden | Visibility::Protected => false,
209                Visibility::Default => !matches!(linkage, Linkage::Internal),
210            },
211        }
212    }
213}
214
215/// How a thread-local variable is reached.
216///
217/// The models are ordered from the most general to the fastest, and a model may always be
218/// replaced by a more general one. The frontend picks from the storage class and the
219/// visibility, `-ftls-model=` overrides it, and the linker may relax a general one into a
220/// faster one when it turns out the definition is in the executable.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
222pub enum TlsModel {
223    /// Works for any variable in any object, at the cost of a call to `__tls_get_addr`.
224    #[default]
225    GlobalDynamic,
226    /// One call to `__tls_get_addr` for several variables that are known to share a module.
227    LocalDynamic,
228    /// The offset is loaded from the GOT. Needs the variable to be in a module loaded at
229    /// program start rather than by `dlopen`.
230    InitialExec,
231    /// The offset is a link-time constant. Only for a variable in the executable itself.
232    LocalExec,
233}
234
235impl TlsModel {
236    /// The spelling in the textual form.
237    #[must_use]
238    pub const fn name(self) -> &'static str {
239        match self {
240            Self::GlobalDynamic => "global_dynamic",
241            Self::LocalDynamic => "local_dynamic",
242            Self::InitialExec => "initial_exec",
243            Self::LocalExec => "local_exec",
244        }
245    }
246
247    /// The model that spelling names.
248    #[must_use]
249    pub fn from_name(name: &str) -> Option<Self> {
250        Self::all().find(|model| model.name() == name)
251    }
252
253    /// Every model, from the most general to the fastest.
254    pub fn all() -> impl Iterator<Item = Self> {
255        [Self::GlobalDynamic, Self::LocalDynamic, Self::InitialExec, Self::LocalExec].into_iter()
256    }
257}
258
259/// One piece of a global's initial image.
260///
261/// Sixteen bytes, so an initializer built out of them is a flat array and a table of a
262/// million bytes is one of these rather than a million.
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum Datum {
265    /// That many zero bytes. What `.bss` is made of, and what the tail of a partly
266    /// initialized array is.
267    Zero(u64),
268    /// Those literal bytes, from the module's byte pool. String literals and anything the
269    /// frontend has already laid out.
270    Bytes(ByteRange),
271    /// One scalar of that IR type, from the module's immediate pool. An integer holds its
272    /// value and a float holds its bit pattern, both target-independently: which byte comes
273    /// first is decided by the datalayout when the object file is written, not here.
274    Scalar {
275        /// The type of the scalar, which gives its width.
276        ty: Type,
277        /// Its value, in the module's immediate pool.
278        value: Idx<Imm>,
279    },
280    /// The address of another symbol, from the module's relocation pool. `&x` in an
281    /// initializer, which the linker fills in.
282    Addr(Idx<Reloc>),
283    /// How far another symbol is from where this is written, from the same pool. `.long
284    /// target - .` in an `asm` at file scope, which is what a table of places in a program
285    /// holds when the table and the places are both in it: the distance fits in four bytes
286    /// where an address takes eight, and it is the same number wherever the image is loaded,
287    /// so nothing has to be written into it at startup.
288    Away(Idx<Reloc>),
289}
290
291impl Datum {
292    /// How many bytes it contributes to the image.
293    ///
294    /// The module is an argument because four of the five kinds keep what they are made of in
295    /// one of its pools, and a datum on its own is four words that mean nothing without it.
296    #[must_use]
297    pub fn size(self, module: &Module) -> u64 {
298        match self {
299            Self::Zero(bytes) => bytes,
300            Self::Bytes(range) => range.len() as u64,
301            // Rounded up, so that an `i1` in an image is a byte and a `_BitInt(24)` is three.
302            Self::Scalar { ty, .. } => u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes()),
303            Self::Addr(reloc) | Self::Away(reloc) => u64::from(module[reloc].size),
304        }
305    }
306}
307
308/// The address of a symbol, written into a global's image by the linker.
309#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310pub struct Reloc {
311    /// The symbol whose address this is.
312    pub symbol: Symbol,
313    /// What to add to that address. `&array[2]` is the address of `array` plus eight.
314    pub addend: i64,
315    /// How many bytes the address occupies, which is the pointer width except where a target
316    /// has a smaller relocation for it.
317    pub size: u32,
318}
319
320/// A global variable.
321///
322/// A size and an alignment and an image, which is what the object writer needs. `init` is
323/// `None` for a declaration of something defined in another object, which is the only thing
324/// that distinguishes the two.
325#[derive(Debug, Clone)]
326pub struct Global {
327    /// The name it is reached by.
328    pub name: Symbol,
329    /// Its size in bytes, which the image must add up to.
330    pub size: u64,
331    /// Its required alignment in bytes, always a power of two.
332    pub align: u32,
333    /// How the linker sees it.
334    pub linkage: Linkage,
335    /// How the dynamic linker sees it.
336    pub visibility: Visibility,
337    /// The model to reach it by if it is thread-local, and `None` if it is not.
338    pub tls: Option<TlsModel>,
339    /// Whether writing through a pointer to it is undefined, which is what puts it in
340    /// `.rodata` rather than `.data`.
341    pub constant: bool,
342    /// The section to put it in, from `__attribute__((section(...)))`, or `None` to let the
343    /// object writer choose from the other fields.
344    pub section: Option<Symbol>,
345    /// Its initial image, or `None` if it is only declared here.
346    pub init: Option<DataList>,
347}
348
349impl Global {
350    /// A definition-less global of that size and alignment, external and not thread-local.
351    #[must_use]
352    pub fn new(name: Symbol, size: u64, align: u32) -> Self {
353        Self {
354            name,
355            size,
356            align,
357            linkage: Linkage::External,
358            visibility: Visibility::Default,
359            tls: None,
360            constant: false,
361            section: None,
362            init: None,
363        }
364    }
365
366    /// Whether this only says the variable exists somewhere.
367    #[must_use]
368    pub fn is_declaration(&self) -> bool {
369        self.init.is_none()
370    }
371}
372
373/// What an alias resolves to at link time.
374#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
375pub enum AliasKind {
376    /// A second name for a symbol in this same object, resolved by the assembler.
377    /// `__attribute__((alias("real")))`.
378    #[default]
379    Alias,
380    /// A name resolved once at program start by calling a resolver function in this object,
381    /// which picks an implementation from what the processor turns out to support.
382    /// `__attribute__((ifunc("resolver")))`, which is how glibc dispatches `memcpy`.
383    IFunc,
384}
385
386impl AliasKind {
387    /// The spelling in the textual form.
388    #[must_use]
389    pub const fn name(self) -> &'static str {
390        match self {
391            Self::Alias => "alias",
392            Self::IFunc => "ifunc",
393        }
394    }
395
396    /// The kind that spelling names.
397    #[must_use]
398    pub fn from_name(name: &str) -> Option<Self> {
399        match name {
400            "alias" => Some(Self::Alias),
401            "ifunc" => Some(Self::IFunc),
402            _ => None,
403        }
404    }
405}
406
407/// A second name for something else.
408#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409pub struct Alias {
410    /// The name being defined.
411    pub name: Symbol,
412    /// What it resolves to: the aliased symbol, or for an ifunc the resolver to call.
413    pub target: Symbol,
414    /// Which of those two it is.
415    pub kind: AliasKind,
416    /// How the linker sees the new name.
417    pub linkage: Linkage,
418    /// How the dynamic linker sees the new name.
419    pub visibility: Visibility,
420}
421
422impl Alias {
423    /// An external alias of `target`.
424    #[must_use]
425    pub fn new(name: Symbol, target: Symbol) -> Self {
426        Self {
427            name,
428            target,
429            kind: AliasKind::Alias,
430            linkage: Linkage::External,
431            visibility: Visibility::Default,
432        }
433    }
434}
435
436/// What a name in a module refers to.
437#[derive(Debug, Clone, Copy, PartialEq, Eq)]
438pub enum SymbolRef {
439    /// A function, defined or declared.
440    Func(FuncId),
441    /// A global variable, defined or declared.
442    Global(GlobalId),
443    /// An alias or an ifunc.
444    Alias(AliasId),
445}
446
447/// The layout facts a printed module carries so it can be compiled without the command line
448/// that produced it.
449///
450/// A subset of the string LLVM writes, in the same syntax, because that syntax is what tools
451/// around the ecosystem already read. It says what the module was built assuming, and the
452/// verifier is what checks it against the target actually being compiled for: a module built
453/// for a 64-bit pointer cannot be finished for a 32-bit one, and finding that out here is
454/// better than finding it out as wrong output.
455#[derive(Debug, Clone, Copy, PartialEq, Eq)]
456pub struct DataLayout {
457    /// Whether the low byte of a scalar is stored first.
458    pub little_endian: bool,
459    /// The width of a pointer in bits.
460    pub pointer_bits: u32,
461    /// The alignment of a pointer in bits.
462    pub pointer_align: u32,
463    /// The alignment of a 64-bit integer in bits, which is the one integer alignment that
464    /// varies across the targets anybody still builds for.
465    pub i64_align: u32,
466    /// The alignment of the x87 eighty bit format in bits, and `None` on a target that does
467    /// not have it.
468    pub f80_align: Option<u32>,
469    /// The alignment the stack is kept at in bits, which is 128 on every target here.
470    pub stack_align: u32,
471}
472
473impl DataLayout {
474    /// The layout of that target.
475    ///
476    /// # Panics
477    ///
478    /// If the target aligns a `long long` to more than half a billion bytes, which no target
479    /// does. The alignment is a byte count here and a bit count in the IR, and the multiplication
480    /// between the two is the only arithmetic in this function.
481    #[must_use]
482    pub fn for_target(target: &TargetInfo) -> Self {
483        Self {
484            little_endian: target.little_endian,
485            pointer_bits: target.pointer_width,
486            pointer_align: target.pointer_width,
487            // Four on System V i386 and eight everywhere else, which is the one integer
488            // alignment that varies across the table and the reason this is a field. It changes
489            // the layout of every struct with a `long long` in it.
490            i64_align: u32::try_from(target.scalars.long_long_align * 8)
491                .expect("no integer alignment is four billion bits"),
492            f80_align: match target.long_double_format {
493                Format::X87Extended => Some(128),
494                _ => None,
495            },
496            stack_align: 128,
497        }
498    }
499
500    /// The layout back from the string [`Display`](fmt::Display) wrote, or `None` if the
501    /// string is not one.
502    ///
503    /// The fields may come in any order, because a string written by hand will not have them
504    /// in ours. A string this crate printed round-trips byte for byte, which is what
505    /// `spec/03-architecture.md` asks of the textual form.
506    #[must_use]
507    pub fn parse(text: &str) -> Option<Self> {
508        let mut little_endian = None;
509        let mut pointer = None;
510        let mut i64_align = None;
511        let mut f80_align = None;
512        let mut stack_align = None;
513        for field in text.split('-') {
514            let seen = match field {
515                "e" => little_endian.replace(true).is_some(),
516                "E" => little_endian.replace(false).is_some(),
517                _ if field.starts_with("p:") => {
518                    let (bits, align) = field[2..].split_once(':')?;
519                    pointer.replace((number(bits)?, number(align)?)).is_some()
520                }
521                _ if field.starts_with("i64:") => i64_align.replace(number(&field[4..])?).is_some(),
522                _ if field.starts_with("f80:") => f80_align.replace(number(&field[4..])?).is_some(),
523                _ if field.starts_with('S') => stack_align.replace(number(&field[1..])?).is_some(),
524                _ => return None,
525            };
526            if seen {
527                return None;
528            }
529        }
530        let (pointer_bits, pointer_align) = pointer?;
531        Some(Self {
532            little_endian: little_endian?,
533            pointer_bits,
534            pointer_align,
535            i64_align: i64_align?,
536            f80_align,
537            stack_align: stack_align?,
538        })
539    }
540}
541
542impl fmt::Display for DataLayout {
543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
544        write!(f, "{}", if self.little_endian { "e" } else { "E" })?;
545        write!(f, "-p:{}:{}", self.pointer_bits, self.pointer_align)?;
546        write!(f, "-i64:{}", self.i64_align)?;
547        if let Some(align) = self.f80_align {
548            write!(f, "-f80:{align}")?;
549        }
550        write!(f, "-S{}", self.stack_align)
551    }
552}
553
554/// A number in the textual form: digits, no sign, and no leading zero.
555///
556/// `p:64:064` would otherwise parse and then print back as `p:64:64`, which breaks the
557/// round-trip for no benefit to anybody.
558fn number(text: &str) -> Option<u32> {
559    if text.is_empty() || (text.len() > 1 && text.starts_with('0')) {
560        return None;
561    }
562    if !text.bytes().all(|byte| byte.is_ascii_digit()) {
563        return None;
564    }
565    text.parse().ok()
566}
567
568/// One translation unit, or after LTO the several that were linked into one.
569#[derive(Debug)]
570pub struct Module {
571    /// What it is called, which is the source file name for a module from the frontend. It
572    /// appears in the textual form and in the debug info and nothing branches on it.
573    pub name: Symbol,
574    /// The target it is for.
575    pub tuple: TargetTuple,
576    /// The layout it was built assuming.
577    pub datalayout: DataLayout,
578
579    funcs: Vec<Func>,
580    globals: Vec<Global>,
581    aliases: Vec<Alias>,
582    metadata: Vec<MetaNode>,
583
584    data: Vec<Datum>,
585    bytes: Vec<u8>,
586    imms: Vec<Imm>,
587    relocs: Vec<Reloc>,
588
589    symbols: HashMap<Symbol, SymbolRef>,
590}
591
592impl Module {
593    /// An empty module for that target.
594    #[must_use]
595    pub fn new(name: Symbol, target: &TargetInfo) -> Self {
596        Self {
597            name,
598            tuple: target.tuple,
599            datalayout: DataLayout::for_target(target),
600            funcs: Vec::new(),
601            globals: Vec::new(),
602            aliases: Vec::new(),
603            metadata: Vec::new(),
604            data: Vec::new(),
605            bytes: Vec::new(),
606            imms: Vec::new(),
607            relocs: Vec::new(),
608            symbols: HashMap::new(),
609        }
610    }
611
612    // Symbols.
613
614    /// Adds a function, which is a declaration if it has no blocks.
615    ///
616    /// # Panics
617    ///
618    /// Panics if the module already has a symbol of that name. Merging a declaration with a
619    /// definition is the frontend's job and it has the declarations to do it with; by the time
620    /// something is in the IR a name means one thing.
621    pub fn add_func(&mut self, func: Func) -> FuncId {
622        let id = Idx::from_usize(self.funcs.len());
623        self.claim(func.name, SymbolRef::Func(id));
624        self.funcs.push(func);
625        id
626    }
627
628    /// Adds a global variable, which is a declaration if it has no image.
629    ///
630    /// # Panics
631    ///
632    /// Panics if the module already has a symbol of that name.
633    pub fn add_global(&mut self, global: Global) -> GlobalId {
634        let id = Idx::from_usize(self.globals.len());
635        self.claim(global.name, SymbolRef::Global(id));
636        self.globals.push(global);
637        id
638    }
639
640    /// Adds an alias.
641    ///
642    /// The target is not resolved here, and it need not be in this module: an alias of
643    /// something in another object is a thing people write.
644    ///
645    /// # Panics
646    ///
647    /// Panics if the module already has a symbol of that name.
648    pub fn add_alias(&mut self, alias: Alias) -> AliasId {
649        let id = Idx::from_usize(self.aliases.len());
650        self.claim(alias.name, SymbolRef::Alias(id));
651        self.aliases.push(alias);
652        id
653    }
654
655    /// What that name refers to, or `None` if this module does not define or declare it.
656    #[must_use]
657    pub fn lookup(&self, name: Symbol) -> Option<SymbolRef> {
658        self.symbols.get(&name).copied()
659    }
660
661    /// Every function, in the order they were added.
662    pub fn funcs(&self) -> impl Iterator<Item = FuncId> + use<> {
663        (0..self.funcs.len()).map(Idx::from_usize)
664    }
665
666    /// Every global variable, in the order they were added.
667    pub fn globals(&self) -> impl Iterator<Item = GlobalId> + use<> {
668        (0..self.globals.len()).map(Idx::from_usize)
669    }
670
671    /// Every alias, in the order they were added.
672    pub fn aliases(&self) -> impl Iterator<Item = AliasId> + use<> {
673        (0..self.aliases.len()).map(Idx::from_usize)
674    }
675
676    fn claim(&mut self, name: Symbol, what: SymbolRef) {
677        assert!(
678            self.symbols.insert(name, what).is_none(),
679            "a module cannot have two symbols with the same name"
680        );
681    }
682
683    // Metadata.
684
685    /// Adds a metadata node and gives back the reference an instruction holds.
686    ///
687    /// The nodes live here rather than in a function because a TBAA tree is shared by every
688    /// memory operation in the module and duplicating it per function would make two accesses
689    /// to the same type look unrelated.
690    pub fn add_meta(&mut self, node: MetaNode) -> Meta {
691        self.metadata.push(node);
692        Idx::from_usize(self.metadata.len() - 1)
693    }
694
695    /// Every metadata node, in the order they were added.
696    pub fn metadata(&self) -> impl Iterator<Item = Meta> + use<> {
697        (0..self.metadata.len()).map(Idx::from_usize)
698    }
699
700    // Pools.
701
702    /// Records a run of data and gives back the list a global holds.
703    pub fn push_data(&mut self, data: &[Datum]) -> DataList {
704        let start = self.data.len();
705        self.data.extend_from_slice(data);
706        DataList::new(Idx::from_usize(start), Idx::from_usize(self.data.len()))
707    }
708
709    /// Records literal bytes and gives back the range a [`Datum::Bytes`] holds.
710    pub fn push_bytes(&mut self, bytes: &[u8]) -> ByteRange {
711        let start = self.bytes.len();
712        self.bytes.extend_from_slice(bytes);
713        ByteRange::new(Idx::from_usize(start), Idx::from_usize(self.bytes.len()))
714    }
715
716    /// Records a scalar value and gives back the index a [`Datum::Scalar`] holds.
717    pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
718        self.imms.push(imm);
719        Idx::from_usize(self.imms.len() - 1)
720    }
721
722    /// Records a relocation and gives back the index a [`Datum::Addr`] holds.
723    pub fn add_reloc(&mut self, reloc: Reloc) -> Idx<Reloc> {
724        self.relocs.push(reloc);
725        Idx::from_usize(self.relocs.len() - 1)
726    }
727
728    /// Every relocation in the module, to be read or edited in place.
729    ///
730    /// A pool rather than a tree, so a pass that wants to rename what an initializer points at has
731    /// nothing to walk: the data lists hold indices into this and the symbol lives here. The one
732    /// pass that wants that is `rucc_safety::wrap`, which turns `&read` in a static initializer
733    /// into `&__rucc_wrap_read` so that a call through the pointer is a call the monitor modelled.
734    pub fn relocs_mut(&mut self) -> &mut [Reloc] {
735        &mut self.relocs
736    }
737
738    /// The same pool, to read. `rucc_safety::summary` walks it to find the names an initializer
739    /// mentions that the build has no wrapper for, which is a boundary it did not model.
740    #[must_use]
741    pub fn relocs(&self) -> &[Reloc] {
742        &self.relocs
743    }
744
745    /// How much is in it, for the `-fstats` output and for a test that wants to say a pass
746    /// deleted something without saying which.
747    #[must_use]
748    pub fn counts(&self) -> ModuleCounts {
749        ModuleCounts {
750            funcs: self.funcs.len(),
751            globals: self.globals.len(),
752            aliases: self.aliases.len(),
753            metadata: self.metadata.len(),
754            data_bytes: self.bytes.len(),
755        }
756    }
757}
758
759/// How much is in a module, from [`Module::counts`].
760#[derive(Debug, Clone, Copy, PartialEq, Eq)]
761pub struct ModuleCounts {
762    /// Functions, defined and declared.
763    pub funcs: usize,
764    /// Global variables, defined and declared.
765    pub globals: usize,
766    /// Aliases and ifuncs.
767    pub aliases: usize,
768    /// Metadata nodes.
769    pub metadata: usize,
770    /// Bytes in the byte pool, which is the bulk of what a module with large initializers
771    /// weighs.
772    pub data_bytes: usize,
773}
774
775impl Index<FuncId> for Module {
776    type Output = Func;
777
778    fn index(&self, id: FuncId) -> &Func {
779        &self.funcs[id.index()]
780    }
781}
782
783impl IndexMut<FuncId> for Module {
784    fn index_mut(&mut self, id: FuncId) -> &mut Func {
785        &mut self.funcs[id.index()]
786    }
787}
788
789impl Index<GlobalId> for Module {
790    type Output = Global;
791
792    fn index(&self, id: GlobalId) -> &Global {
793        &self.globals[id.index()]
794    }
795}
796
797impl IndexMut<GlobalId> for Module {
798    fn index_mut(&mut self, id: GlobalId) -> &mut Global {
799        &mut self.globals[id.index()]
800    }
801}
802
803impl Index<AliasId> for Module {
804    type Output = Alias;
805
806    fn index(&self, id: AliasId) -> &Alias {
807        &self.aliases[id.index()]
808    }
809}
810
811impl Index<Meta> for Module {
812    type Output = MetaNode;
813
814    fn index(&self, meta: Meta) -> &MetaNode {
815        &self.metadata[meta.index()]
816    }
817}
818
819impl Index<Idx<Imm>> for Module {
820    type Output = Imm;
821
822    fn index(&self, imm: Idx<Imm>) -> &Imm {
823        &self.imms[imm.index()]
824    }
825}
826
827impl Index<Idx<Reloc>> for Module {
828    type Output = Reloc;
829
830    fn index(&self, reloc: Idx<Reloc>) -> &Reloc {
831        &self.relocs[reloc.index()]
832    }
833}
834
835impl Index<DataList> for Module {
836    type Output = [Datum];
837
838    fn index(&self, list: DataList) -> &[Datum] {
839        &self.data[list.as_usize_range()]
840    }
841}
842
843impl Index<ByteRange> for Module {
844    type Output = [u8];
845
846    fn index(&self, range: ByteRange) -> &[u8] {
847        &self.bytes[range.as_usize_range()]
848    }
849}
850
851#[cfg(test)]
852mod tests {
853    use rucc_base::Interner;
854    use rucc_target::{Arch, Env, Os, Triple};
855
856    use super::*;
857    use crate::inst::Signature;
858
859    fn target(arch: Arch, os: Os, env: Env) -> TargetInfo {
860        TargetInfo::new(Triple::new(arch, os, env))
861    }
862
863    fn linux() -> TargetInfo {
864        target(Arch::X86_64, Os::Linux, Env::Gnu)
865    }
866
867    #[test]
868    fn a_datum_is_sixteen_bytes() {
869        // A global with a large initializer is a flat array of these, so this is the tripwire
870        // on somebody adding a field that doubles the weight of every one.
871        assert_eq!(size_of::<Datum>(), 16);
872    }
873
874    #[test]
875    fn the_layout_of_x86_64_linux_is_the_one_in_the_spec() {
876        let layout = DataLayout::for_target(&linux());
877        assert_eq!(layout.to_string(), "e-p:64:64-i64:64-f80:128-S128");
878    }
879
880    #[test]
881    fn only_x86_has_the_eighty_bit_format() {
882        assert_eq!(DataLayout::for_target(&linux()).f80_align, Some(128));
883        let arm = DataLayout::for_target(&target(Arch::Aarch64, Os::Linux, Env::Gnu));
884        assert_eq!(arm.f80_align, None);
885        assert_eq!(arm.to_string(), "e-p:64:64-i64:64-S128");
886    }
887
888    #[test]
889    fn a_layout_round_trips() {
890        for triple in [
891            Triple::new(Arch::X86_64, Os::Linux, Env::Gnu),
892            Triple::new(Arch::X86_64, Os::Darwin, Env::None),
893            Triple::new(Arch::Aarch64, Os::Darwin, Env::None),
894            Triple::new(Arch::Riscv64, Os::Linux, Env::Musl),
895        ] {
896            let layout = DataLayout::for_target(&TargetInfo::new(triple));
897            let text = layout.to_string();
898            assert_eq!(DataLayout::parse(&text), Some(layout), "{text}");
899        }
900    }
901
902    #[test]
903    fn a_layout_may_be_written_in_any_order() {
904        let text = "S128-i64:64-f80:128-p:64:64-e";
905        assert_eq!(DataLayout::parse(text), Some(DataLayout::for_target(&linux())));
906    }
907
908    #[test]
909    fn a_layout_needs_every_field_it_prints() {
910        for text in ["", "e", "e-p:64:64-S128", "e-i64:64-S128", "e-p:64:64-i64:64"] {
911            assert_eq!(DataLayout::parse(text), None, "{text}");
912        }
913    }
914
915    #[test]
916    fn a_layout_refuses_a_second_spelling() {
917        // Each of these would print back as something else, which breaks the round-trip.
918        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"]
919        {
920            assert_eq!(DataLayout::parse(text), None, "{text}");
921        }
922    }
923
924    #[test]
925    fn a_module_finds_what_it_holds() {
926        let mut names = Interner::new();
927        let mut module = Module::new(names.intern("test.c"), &linux());
928
929        let counter = names.intern("counter");
930        let sum = names.intern("sum");
931        let total = names.intern("total");
932
933        let global = module.add_global(Global::new(counter, 4, 4));
934        let func = module.add_func(Func::new(sum, Signature::new()));
935        let alias = module.add_alias(Alias::new(total, counter));
936
937        assert_eq!(module.lookup(counter), Some(SymbolRef::Global(global)));
938        assert_eq!(module.lookup(sum), Some(SymbolRef::Func(func)));
939        assert_eq!(module.lookup(total), Some(SymbolRef::Alias(alias)));
940        assert_eq!(module.lookup(names.intern("nothing")), None);
941        assert_eq!(module[alias].target, counter);
942        assert!(module[global].is_declaration());
943        assert!(module[func].is_declaration());
944    }
945
946    #[test]
947    #[should_panic(expected = "two symbols with the same name")]
948    fn a_name_means_one_thing() {
949        let mut names = Interner::new();
950        let mut module = Module::new(names.intern("test.c"), &linux());
951        let name = names.intern("x");
952        module.add_global(Global::new(name, 4, 4));
953        module.add_func(Func::new(name, Signature::new()));
954    }
955
956    #[test]
957    fn an_initializer_adds_up_to_the_size() {
958        let mut names = Interner::new();
959        let mut module = Module::new(names.intern("test.c"), &linux());
960
961        // struct { int n; const char *name; char pad[6]; } = { 7, "hi", { 0 } };
962        let text = names.intern("hi.str");
963        let seven = module.add_imm(Imm::int(7, Type::int(32)));
964        let bytes = module.push_bytes(b"hi\0");
965        let addr = module.add_reloc(Reloc { symbol: text, addend: 0, size: 8 });
966        let init = module.push_data(&[
967            Datum::Scalar { ty: Type::int(32), value: seven },
968            Datum::Zero(4),
969            Datum::Addr(addr),
970            // The six bytes of `pad` and the two the struct is tailed out with. Padding is
971            // the frontend's arithmetic, and the image is what it came out as.
972            Datum::Zero(8),
973        ]);
974
975        let mut global = Global::new(names.intern("entry"), 24, 8);
976        global.init = Some(init);
977        global.constant = true;
978        let id = module.add_global(global);
979
980        assert!(!module[id].is_declaration());
981        let size: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
982        assert_eq!(size, module[id].size);
983        assert_eq!(&module[bytes], b"hi\0");
984        assert_eq!(module[seven].unsigned(), 7);
985        assert_eq!(module.counts().data_bytes, 3);
986    }
987
988    #[test]
989    fn a_scalar_datum_is_as_wide_as_its_type() {
990        let mut names = Interner::new();
991        let mut module = Module::new(names.intern("test.c"), &linux());
992        let value = module.add_imm(Imm::int(0, Type::int(32)));
993        assert_eq!(Datum::Scalar { ty: Type::int(32), value }.size(&module), 4);
994        // Rounded up to whole bytes, one lane at a time.
995        assert_eq!(Datum::Scalar { ty: Type::I1, value }.size(&module), 1);
996        assert_eq!(Datum::Scalar { ty: Type::int(24), value }.size(&module), 3);
997        assert_eq!(Datum::Scalar { ty: Type::vector(Type::int(8), 16), value }.size(&module), 16);
998    }
999
1000    #[test]
1001    fn the_names_round_trip() {
1002        for linkage in Linkage::all() {
1003            assert_eq!(Linkage::from_name(linkage.name()), Some(linkage));
1004        }
1005        for visibility in Visibility::all() {
1006            assert_eq!(Visibility::from_name(visibility.name()), Some(visibility));
1007        }
1008        for model in TlsModel::all() {
1009            assert_eq!(TlsModel::from_name(model.name()), Some(model));
1010        }
1011        for kind in [AliasKind::Alias, AliasKind::IFunc] {
1012            assert_eq!(AliasKind::from_name(kind.name()), Some(kind));
1013        }
1014        assert_eq!(Linkage::from_name("static"), None);
1015        assert_eq!(Visibility::from_name("internal"), None);
1016    }
1017
1018    #[test]
1019    fn only_internal_linkage_is_local() {
1020        for linkage in Linkage::all() {
1021            assert_eq!(linkage.is_local(), linkage == Linkage::Internal);
1022            assert_eq!(
1023                linkage.may_be_replaced(),
1024                !matches!(linkage, Linkage::External | Linkage::Internal)
1025            );
1026        }
1027    }
1028
1029    #[test]
1030    fn metadata_is_shared_by_the_whole_module() {
1031        let mut names = Interner::new();
1032        let mut module = Module::new(names.intern("test.c"), &linux());
1033        let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1034            name: names.intern("omnipotent char"),
1035            parent: None,
1036            offset: 0,
1037        }));
1038        let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1039            name: names.intern("int"),
1040            parent: Some(char_node),
1041            offset: 0,
1042        }));
1043        assert_eq!(module[int_node].parent(), Some(char_node));
1044        assert_eq!(module.metadata().count(), 2);
1045    }
1046}