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