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/// 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}