Skip to main content

rucc_lower/
unit.rs

1//! The module level of the walk: what a translation unit's declarations become.
2//!
3//! Design: `spec/08-ir.md` section 8.9.
4//!
5//! One typed tree becomes one [`Module`]. A file-scope object becomes a global with an image
6//! built from its initializer, a function becomes a [`Func`] whose body is built by
7//! [`body`](mod@crate::body), and a string literal becomes an unnamed constant global that
8//! whatever mentioned it points at.
9//!
10//! # What an image is
11//!
12//! An initializer arrives here already flattened: one entry per scalar that is stored, each
13//! with the byte offset it goes at, with every designator and every nested brace already
14//! resolved. So building the image is a walk over the entries in offset order, filling the gaps
15//! between them with zeros, and the only thing that has to be worked out per entry is whether
16//! the value is a number, a run of bytes from a string literal, or the address of something the
17//! linker has to place.
18//!
19//! # Names
20//!
21//! An object with linkage is known by the name it was written with, and there is nothing to
22//! invent. A `static` inside a function has no linkage and still needs a name in the object
23//! file, so it gets `name.N`, which is what gcc does and is why two functions may each have a
24//! `static int count;` without colliding. A string literal has no name at all and gets
25//! `.Lstr.N`, whose leading dot keeps it out of the symbol table on every target that has the
26//! convention.
27
28use std::cmp::Ordering;
29use std::collections::{HashMap, HashSet};
30
31use rucc_base::{Interner, Symbol};
32use rucc_diag::{Diagnostic, Span};
33use rucc_ir::{
34    DataList, Datum, Func, Global, Imm, Linkage as IrLinkage, Module, Reloc, Signature, TlsModel,
35};
36use rucc_sema::{
37    Base, Const, DeclId, DeclKind, Definition, Eval, ExprId, ExprKind, InitEntry, InitList,
38    Linkage, StorageDuration, StrId, Tast,
39};
40use rucc_target::TargetInfo;
41use rucc_types::{TypeId, TypeKind, Types};
42
43use crate::body;
44use crate::repr;
45
46/// Everything the walk reads, which is a checked translation unit and the target it is for.
47///
48/// The interner is mutable because the walk invents names the program never wrote: the label a
49/// string literal is emitted under, and the mangled name of a function-scope `static`.
50#[derive(Debug)]
51pub struct Context<'a> {
52    /// The typed tree.
53    pub tast: &'a Tast,
54    /// The types it points into.
55    pub types: &'a Types,
56    /// What is being compiled for, which is where every width and every alignment comes from.
57    pub target: &'a TargetInfo,
58    /// The name table.
59    pub names: &'a mut Interner,
60}
61
62/// What the walk produced.
63#[derive(Debug)]
64pub struct Lowered {
65    /// The module, which is complete even when something was reported: a construct that is not
66    /// supported yet leaves the rest of the function around it intact.
67    pub module: Module,
68    /// What was reported, in the order it was found.
69    pub diagnostics: Vec<Diagnostic>,
70}
71
72/// Walks a checked translation unit and builds the IR for it.
73///
74/// `name` is the module's name, which is the file the tree came from.
75#[must_use]
76pub fn lower(name: &str, cx: Context<'_>) -> Lowered {
77    let Context { tast, types, target, names } = cx;
78    let module = Module::new(names.intern(name), target);
79    let mut unit = Unit {
80        tast,
81        types,
82        target,
83        names,
84        module,
85        diagnostics: Vec::new(),
86        strings: HashMap::new(),
87        statics: HashMap::new(),
88        done: HashSet::new(),
89    };
90    unit.run();
91    Lowered { module: unit.module, diagnostics: unit.diagnostics }
92}
93
94/// The walk over one translation unit, and everything it has built so far.
95pub(crate) struct Unit<'a> {
96    pub(crate) tast: &'a Tast,
97    pub(crate) types: &'a Types,
98    pub(crate) target: &'a TargetInfo,
99    pub(crate) names: &'a mut Interner,
100    pub(crate) module: Module,
101    pub(crate) diagnostics: Vec<Diagnostic>,
102    /// The global each string literal was emitted as, so that two mentions of one literal are
103    /// one object.
104    strings: HashMap<StrId, Symbol>,
105    /// The name each object with no linkage was given.
106    statics: HashMap<DeclId, Symbol>,
107    /// What has been emitted, because a redeclaration is the same declaration seen twice.
108    done: HashSet<DeclId>,
109}
110
111// The debug is by hand and short: a translation unit is not something anybody wants printed as
112// a `{:?}`, and the module has a printer of its own for when they do.
113impl std::fmt::Debug for Unit<'_> {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        f.debug_struct("Unit")
116            .field("module", &self.module.counts())
117            .field("diagnostics", &self.diagnostics.len())
118            .finish()
119    }
120}
121
122impl Unit<'_> {
123    /// Every declaration the file made, in the order it made them.
124    fn run(&mut self) {
125        for index in 0..self.tast.top_level().len() {
126            let decl = self.tast.top_level()[index];
127            if !self.done.insert(decl) {
128                continue;
129            }
130            match self.tast[decl].kind {
131                DeclKind::Function => self.function(decl),
132                DeclKind::Object => self.object(decl),
133            }
134        }
135    }
136
137    /// One object with static storage duration.
138    fn object(&mut self, decl: DeclId) {
139        let tast = self.tast;
140        let node = &tast[decl];
141        let (ty, state, init) = (node.ty, node.state, node.init);
142        let (linkage, duration, alignment) = (node.linkage, node.duration, node.alignment);
143        let span = tast.decl_span(decl);
144        if duration == StorageDuration::Automatic {
145            // A block-scope object with automatic storage is a slot or a value in the function
146            // that declares it, and the body is what makes it. Nothing is emitted here.
147            return;
148        }
149
150        let symbol = self.symbol_of(decl);
151        let size = repr::size_of(self.types, self.target, ty);
152        let align = alignment.unwrap_or_else(|| repr::align_of(self.types, self.target, ty));
153        let mut global = Global::new(symbol, size, align);
154        global.linkage = match linkage {
155            Linkage::External => IrLinkage::External,
156            Linkage::Internal | Linkage::None => IrLinkage::Internal,
157        };
158        global.tls = (duration == StorageDuration::Thread).then_some(TlsModel::GlobalDynamic);
159        global.constant = repr::is_read_only(self.types, ty);
160        global.init = match state {
161            // `extern int x;` and nothing else names an object another translation unit
162            // defines. The global is here so that a reference to it has something to resolve
163            // against, and it has no image, which is what makes it a declaration.
164            Definition::Declared => None,
165            Definition::Tentative => Some(self.zeros(size)),
166            Definition::Defined => Some(self.image(init, size, span)),
167        };
168        self.module.add_global(global);
169    }
170
171    /// One function, with its body when it has one.
172    fn function(&mut self, decl: DeclId) {
173        let tast = self.tast;
174        let node = &tast[decl];
175        let (ty, linkage, body) = (node.ty, node.linkage, node.body);
176        let span = tast.decl_span(decl);
177        let Some(name) = node.name else { return };
178        let Some(signature) = self.signature(ty, span) else { return };
179
180        let mut func = Func::new(name, signature);
181        func.linkage = match linkage {
182            Linkage::Internal | Linkage::None => IrLinkage::Internal,
183            Linkage::External => IrLinkage::External,
184        };
185        if body.is_some() {
186            body::lower(self, decl, &mut func);
187        }
188        self.module.add_func(func);
189    }
190
191    /// The IR signature of a C function type, and [`None`] for one the walk cannot make.
192    ///
193    /// A structure passed or returned by value is the whole of what is missing, and it is
194    /// missing because the answer is the target's rather than C's: the same declaration passes
195    /// a pair of registers on one target and a hidden pointer on another.
196    pub(crate) fn signature(&mut self, ty: TypeId, span: Span) -> Option<Signature> {
197        let canonical = self.types.canonical(ty);
198        let canonical = match self.types.kind(canonical) {
199            // A call goes through a pointer to a function, and the type in hand may be either.
200            TypeKind::Pointer(pointee) => self.types.canonical(pointee),
201            _ => canonical,
202        };
203        let TypeKind::Function(id) = self.types.kind(canonical) else {
204            self.unsupported("a call through something that is not a function", span);
205            return None;
206        };
207        let signature = self.types.signature(id);
208        let (ret, variadic) = (signature.ret, signature.variadic);
209        let prototyped = signature.prototyped;
210        let params = signature.params.clone();
211
212        let mut lowered = Signature::new();
213        // A function declared without a prototype takes what it is given, which is what a
214        // signature with no parameters and no end to them says. C23 removed these and this is
215        // what `int f();` means in every dialect before it.
216        lowered.variadic = variadic || !prototyped;
217        for param in params {
218            match repr::value_type(self.types, self.target, param) {
219                Some(ty) => lowered.params.push(ty),
220                None => {
221                    self.unsupported("passing a structure or a union by value", span);
222                    return None;
223                }
224            }
225        }
226        if !matches!(self.types.kind(self.types.canonical(ret)), TypeKind::Void) {
227            match repr::value_type(self.types, self.target, ret) {
228                Some(ty) => lowered.returns.push(ty),
229                None => {
230                    self.unsupported("returning a structure or a union by value", span);
231                    return None;
232                }
233            }
234        }
235        Some(lowered)
236    }
237
238    /// The image of an initializer: the entries in order, with the gaps between them zeroed.
239    pub(crate) fn image(&mut self, init: Option<InitList>, size: u64, span: Span) -> DataList {
240        let Some(init) = init else { return self.zeros(size) };
241        let entries: Vec<InitEntry> = self.tast[init].to_vec();
242        let mut data: Vec<Datum> = Vec::with_capacity(entries.len());
243        let mut at = 0;
244        for entry in entries {
245            if entry.bit_width != 0 {
246                self.unsupported("a bit-field with a static storage duration", span);
247                continue;
248            }
249            let room = size.saturating_sub(entry.offset);
250            let Some(datum) = self.datum(entry.value, room) else { continue };
251            match entry.offset.cmp(&at) {
252                Ordering::Greater => data.push(Datum::Zero(entry.offset - at)),
253                // Two entries at one offset is one designator writing over another, which is
254                // legal and which the image cannot express: the earlier bytes are already in
255                // the list. Nothing here is wrong enough to drop the rest of the image.
256                Ordering::Less => {
257                    self.unsupported("an initializer that writes over an earlier one", span);
258                    continue;
259                }
260                Ordering::Equal => {}
261            }
262            at = entry.offset + datum.size(&self.module);
263            data.push(datum);
264        }
265        if at < size {
266            // The tail of a partly initialized object, which C says is zero. So is the tail of
267            // an array the initializer did not fill, and so is every byte of padding.
268            data.push(Datum::Zero(size - at));
269        }
270        self.module.push_data(&data)
271    }
272
273    /// One entry of an image, given how many bytes are left in the object it goes in.
274    fn datum(&mut self, value: ExprId, room: u64) -> Option<Datum> {
275        let tast = self.tast;
276        let ty = tast[value].ty;
277        let span = tast.expr_span(value);
278        if let TypeKind::Array { .. } = self.types.kind(self.types.canonical(ty)) {
279            // An array in an initializer is a string literal initializing it, because that is
280            // the only way an array is ever a value. `char s[2] = "hi";` drops the terminator,
281            // which is the one case where the literal is longer than what it initializes.
282            let ExprKind::Str(id) = tast[value].kind else {
283                self.unsupported("this initializer", span);
284                return None;
285            };
286            let bytes = tast[id].bytes(self.target);
287            let take = bytes.len().min(usize::try_from(room).unwrap_or(usize::MAX));
288            return Some(Datum::Bytes(self.module.push_bytes(&bytes[..take])));
289        }
290
291        let size = repr::size_of(self.types, self.target, ty);
292        match self.fold(value)? {
293            Const::Int(number) => {
294                let ty = repr::value_type(self.types, self.target, ty)?;
295                let imm = self.module.add_imm(Imm::int(number, ty));
296                Some(Datum::Scalar { ty, value: imm })
297            }
298            Const::Float(number) => {
299                let ty = repr::value_type(self.types, self.target, ty)?;
300                let imm = self.module.add_imm(Imm::from_bits(number.to_bits()));
301                Some(Datum::Scalar { ty, value: imm })
302            }
303            Const::Address(address) => {
304                let symbol = match address.base {
305                    Base::Decl(decl) => self.symbol_of(decl),
306                    Base::Str(id) => self.string(id),
307                };
308                let addend = i64::try_from(address.offset).unwrap_or(0);
309                let size = u32::try_from(size).unwrap_or(0);
310                Some(Datum::Addr(self.module.add_reloc(Reloc { symbol, addend, size })))
311            }
312        }
313    }
314
315    /// An image of nothing but zeros, which is what a tentative definition has.
316    fn zeros(&mut self, size: u64) -> DataList {
317        if size == 0 {
318            return DataList::EMPTY;
319        }
320        self.module.push_data(&[Datum::Zero(size)])
321    }
322
323    /// The global a string literal is emitted as, making it the first time it is asked for.
324    pub(crate) fn string(&mut self, id: StrId) -> Symbol {
325        if let Some(&symbol) = self.strings.get(&id) {
326            return symbol;
327        }
328        let literal = &self.tast[id];
329        let bytes = literal.bytes(self.target);
330        let align = literal.encoding.element_width(self.target) / 8;
331        let symbol = self.names.intern(&format!(".Lstr.{}", self.strings.len()));
332
333        let mut global = Global::new(symbol, bytes.len() as u64, align.max(1));
334        global.linkage = IrLinkage::Internal;
335        // Not because the type says so, since a literal is an array of `char` and not of
336        // `const char`, but because writing to one is undefined and every target puts them
337        // somewhere read-only.
338        global.constant = true;
339        let range = self.module.push_bytes(&bytes);
340        global.init = Some(self.module.push_data(&[Datum::Bytes(range)]));
341        self.module.add_global(global);
342        self.strings.insert(id, symbol);
343        symbol
344    }
345
346    /// The name an object or a function is known by in the object file.
347    pub(crate) fn symbol_of(&mut self, decl: DeclId) -> Symbol {
348        let tast = self.tast;
349        let node = &tast[decl];
350        if node.linkage != Linkage::None {
351            return node.name.unwrap_or_else(|| self.names.intern(".Lanon"));
352        }
353        if let Some(&symbol) = self.statics.get(&decl) {
354            return symbol;
355        }
356        // A `static` in a function, or a compound literal with static storage duration. The
357        // number is what makes two of them in two functions two objects.
358        let base = match node.name {
359            Some(name) => self.names.resolve(name).to_string(),
360            None => ".Lanon".to_string(),
361        };
362        let symbol = self.names.intern(&format!("{base}.{}", self.statics.len()));
363        self.statics.insert(decl, symbol);
364        symbol
365    }
366
367    /// Emits the global for an object with static storage duration declared inside a function.
368    pub(crate) fn local_static(&mut self, decl: DeclId) {
369        if !self.done.insert(decl) {
370            return;
371        }
372        match self.tast[decl].kind {
373            // A function declared inside a body is a declaration of the function, not an
374            // object with static storage that happens to be one.
375            DeclKind::Function => self.function(decl),
376            DeclKind::Object => self.object(decl),
377        }
378    }
379
380    /// The value of a constant expression, reporting what folding it reported.
381    fn fold(&mut self, expr: ExprId) -> Option<Const> {
382        let mut eval = Eval::new(self.tast, self.types, self.target, self.names);
383        let folded = eval.constant(expr);
384        let reported = eval.finish();
385        self.diagnostics.extend(reported);
386        match folded {
387            Ok(value) => Some(value),
388            Err(stop) => {
389                if !stop.poisoned {
390                    let span = self.tast.expr_span(stop.at);
391                    self.unsupported("an initializer this compiler cannot fold", span);
392                }
393                None
394            }
395        }
396    }
397
398    /// Reports a construct the walk does not build IR for yet.
399    pub(crate) fn unsupported(&mut self, what: &str, span: Span) {
400        self.diagnostics.push(
401            Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
402        );
403    }
404}