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