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