rucc_sema/tast.rs
1//! The arenas of the typed tree, and everything that hangs off them.
2//!
3//! Design: `spec/03-architecture.md` section 3.3 and `spec/07-types-and-semantics.md` section
4//! 7.14.
5//!
6//! The same shape as the untyped tree and for the same reasons: flat vectors, four-byte
7//! indices, spans out of line, one owner per translation unit and one drop at the end of it.
8//! What is different is that a type is in the node rather than beside it, because every walk
9//! over this tree reads the type of every node it touches, which is exactly not true of spans.
10//!
11//! One [`Tast`] does not own the [`Types`](rucc_types::Types) its nodes point into. A type
12//! outlives the tree that mentions it, the two are built together and handed on together, and
13//! putting the table inside the tree would mean a pass that only wants to ask what a type is
14//! has to borrow the tree to do it.
15
16use std::fmt;
17use std::ops::Index;
18
19use rucc_base::float::Float;
20use rucc_base::{Idx, IdxRange, Symbol};
21use rucc_diag::Span;
22use rucc_lex::StringLiteral;
23use rucc_types::{TypeId, VlaId};
24
25use crate::asm::{Asm, AsmId, AsmOperand, AsmOperandList, FileAsm, LabelList, StrList};
26use crate::decl::{Decl, DeclId, DeclList, InitEntry};
27use crate::expr::{Expr, ExprId, ExprList};
28use crate::stmt::{Case, CaseId, Stmt, StmtId, StmtList};
29
30/// A folded constant, in the value table.
31pub type ConstId = Idx<Const>;
32
33/// A string literal, in the literal table.
34pub type StrId = Idx<StringLiteral>;
35
36/// A label, in the label table.
37pub type LabelId = Idx<Label>;
38
39/// The value of a constant expression, after folding.
40///
41/// Integers are held in a hundred and twenty eight bits whatever their type, which covers every
42/// integer type this compiler has including `__int128`. A `_BitInt(N)` wider than that is not
43/// representable here and is refused where it is written rather than silently truncated.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Const {
46 /// An integer, sign extended into the whole width from the type it has.
47 Int(i128),
48 /// A floating value, in the target's format rather than the host's.
49 Float(Float),
50 /// The address of an object, which is a number nobody knows until the link.
51 Address(Address),
52}
53
54/// An address constant: some object, and how far into it.
55///
56/// This is what `&x`, `a + 1` and `&s.field` fold to, and it is the reason folding hands back
57/// something richer than a number. The value is not known here and will not be known until the
58/// linker places the object, so what a static initializer needs is not the value but the pair
59/// that names it, which is what an object file's relocation records.
60///
61/// A pointer with no object behind it is not one of these. `(int *)4` folds to [`Const::Int`],
62/// because four is the whole answer and nothing has to be relocated.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct Address {
65 /// The object the address is into.
66 pub base: Base,
67 /// How many bytes into it, which a member or a subscript adds to and which may be outside
68 /// the object: `&a[10]` on an `int a[10]` is a valid address constant and is one past it.
69 pub offset: i128,
70}
71
72/// What an address constant is an address of.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum Base {
75 /// A declared object or function, which the linker knows by name.
76 Decl(DeclId),
77 /// A string literal, which has static storage duration and no name of its own.
78 Str(StrId),
79}
80
81/// A label, and the statement it names.
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct Label {
84 /// The name it was written with.
85 pub name: Symbol,
86 /// The statement it labels, absent for a label that was used and never defined, which is a
87 /// diagnostic rather than a reason to lose the reference.
88 pub stmt: Option<StmtId>,
89}
90
91/// One typed translation unit.
92#[derive(Default)]
93pub struct Tast {
94 exprs: Vec<Expr>,
95 expr_spans: Vec<Span>,
96 stmts: Vec<Stmt>,
97 stmt_spans: Vec<Span>,
98 decls: Vec<Decl>,
99 decl_spans: Vec<Span>,
100
101 consts: Vec<Const>,
102 strings: Vec<StringLiteral>,
103 labels: Vec<Label>,
104 vlas: Vec<ExprId>,
105 adjusted: Vec<(DeclId, TypeId)>,
106 asms: Vec<Asm>,
107 file_asms: Vec<FileAsm>,
108
109 expr_refs: Vec<ExprId>,
110 stmt_refs: Vec<StmtId>,
111 decl_refs: Vec<DeclId>,
112 str_refs: Vec<StrId>,
113 label_refs: Vec<LabelId>,
114 cases: Vec<Case>,
115 init_entries: Vec<InitEntry>,
116 asm_operands: Vec<AsmOperand>,
117
118 top_level: Vec<DeclId>,
119}
120
121impl Tast {
122 /// An empty tree.
123 #[must_use]
124 pub fn new() -> Tast {
125 Tast::default()
126 }
127
128 /// The objects and functions of the translation unit, in the order they were declared.
129 #[must_use]
130 pub fn top_level(&self) -> &[DeclId] {
131 &self.top_level
132 }
133
134 /// Adds a declaration at file scope.
135 pub fn add_top_level(&mut self, decl: DeclId) {
136 self.top_level.push(decl);
137 }
138
139 /// The `asm` written at file scope, in the order they were written.
140 ///
141 /// Beside [`Tast::top_level`] rather than in it, because one of these declares no object and
142 /// no function and so is not a [`Decl`]. What it is instead is a contribution to the object
143 /// file, which is a thing only the walk to the IR has anywhere to put.
144 #[must_use]
145 pub fn file_asms(&self) -> &[FileAsm] {
146 &self.file_asms
147 }
148
149 /// Adds an `asm` written at file scope.
150 pub fn add_file_asm(&mut self, asm: FileAsm) {
151 self.file_asms.push(asm);
152 }
153
154 /// Adds an expression, with the source it came from.
155 ///
156 /// # Panics
157 ///
158 /// Panics if the arena would exceed four billion nodes, which is not a translation unit
159 /// this compiler intends to accept.
160 pub fn expr(&mut self, expr: Expr, span: Span) -> ExprId {
161 let id = Idx::from_usize(self.exprs.len());
162 self.exprs.push(expr);
163 self.expr_spans.push(span);
164 id
165 }
166
167 /// Adds a statement, with the source it came from.
168 ///
169 /// # Panics
170 ///
171 /// Panics if the arena would exceed four billion nodes.
172 pub fn stmt(&mut self, stmt: Stmt, span: Span) -> StmtId {
173 let id = Idx::from_usize(self.stmts.len());
174 self.stmts.push(stmt);
175 self.stmt_spans.push(span);
176 id
177 }
178
179 /// Adds a declaration, with the source it came from.
180 ///
181 /// # Panics
182 ///
183 /// Panics if the arena would exceed four billion nodes.
184 pub fn decl(&mut self, decl: Decl, span: Span) -> DeclId {
185 let id = Idx::from_usize(self.decls.len());
186 self.decls.push(decl);
187 self.decl_spans.push(span);
188 id
189 }
190
191 /// Replaces a declaration, which is what a definition of something already declared does.
192 ///
193 /// # Panics
194 ///
195 /// Panics if `id` is not a declaration of this tree.
196 pub fn set_decl(&mut self, id: DeclId, decl: Decl) {
197 self.decls[id.index()] = decl;
198 }
199
200 /// Replaces a statement, which is what a `switch` does to the cases in its body.
201 ///
202 /// A `case` is checked before the table it is an entry of exists, since the table is a run
203 /// and the run is not known until the whole body has been walked. So the statement is written
204 /// with a placeholder entry and given its real one here.
205 ///
206 /// # Panics
207 ///
208 /// Panics if `id` is not a statement of this tree.
209 pub fn set_stmt(&mut self, id: StmtId, stmt: Stmt) {
210 self.stmts[id.index()] = stmt;
211 }
212
213 /// The source an expression came from.
214 #[must_use]
215 pub fn expr_span(&self, id: ExprId) -> Span {
216 self.expr_spans[id.index()]
217 }
218
219 /// The source a statement came from.
220 #[must_use]
221 pub fn stmt_span(&self, id: StmtId) -> Span {
222 self.stmt_spans[id.index()]
223 }
224
225 /// The source a declaration came from.
226 #[must_use]
227 pub fn decl_span(&self, id: DeclId) -> Span {
228 self.decl_spans[id.index()]
229 }
230
231 /// Records the size of one variable length array, and gives back its identity.
232 ///
233 /// The type table keeps a [`VlaId`] and nothing else, because two variable length arrays
234 /// written with the same element type are still distinct types and interning them together
235 /// would say they are not. The expression itself lives here, since it is evaluated once
236 /// where the declaration is reached and its value is what every `sizeof` of that type
237 /// afterwards answers with.
238 ///
239 /// # Panics
240 ///
241 /// Panics if the table would exceed four billion entries.
242 pub fn add_vla(&mut self, size: ExprId) -> VlaId {
243 let id = u32::try_from(self.vlas.len()).expect("too many variable length arrays");
244 self.vlas.push(size);
245 VlaId(id)
246 }
247
248 /// The size expression of one variable length array.
249 ///
250 /// # Panics
251 ///
252 /// Panics if `id` is not one of this tree's.
253 #[must_use]
254 pub fn vla_size(&self, id: VlaId) -> ExprId {
255 self.vlas[id.0 as usize]
256 }
257
258 /// Records the type a parameter was written as, where adjusting it to a pointer dropped a
259 /// length the program still has to evaluate.
260 ///
261 /// `int f(int a[i++])` declares a pointer, since C11 6.7.6.3p7 adjusts an array parameter to
262 /// one, and the adjustment takes the type away and not the expression: the size is evaluated
263 /// once on entry to the function, in the order the parameters were written, so `i++` happens
264 /// and the function sees the incremented value. Nothing needs the size for anything, because
265 /// the parameter is a pointer, so what is kept here is the type it was written as and the
266 /// walk over that type is what evaluates every length in it.
267 ///
268 /// Only the outermost length is ever lost this way. `int a[][n]` adjusts to `int (*)[n]` and
269 /// the `n` is still in the type the parameter has, which is why this is a handful of entries
270 /// in the whole tree and not one per parameter.
271 pub fn record_adjustment(&mut self, decl: DeclId, written: TypeId) {
272 self.adjusted.push((decl, written));
273 }
274
275 /// The type a parameter was written as, for the few that have one.
276 #[must_use]
277 pub fn adjusted_from(&self, decl: DeclId) -> Option<TypeId> {
278 self.adjusted.iter().find(|&&(at, _)| at == decl).map(|&(_, written)| written)
279 }
280
281 /// Records that a label names a statement, which is not known when the label is created
282 /// because a `goto` may come first.
283 ///
284 /// # Panics
285 ///
286 /// Panics if `id` is not a label of this tree.
287 pub fn define_label(&mut self, id: LabelId, stmt: StmtId) {
288 self.labels[id.index()].stmt = Some(stmt);
289 }
290
291 /// How many expressions, statements and declarations the tree holds.
292 #[must_use]
293 pub fn counts(&self) -> Counts {
294 Counts { exprs: self.exprs.len(), stmts: self.stmts.len(), decls: self.decls.len() }
295 }
296
297 /// Whether nothing has been checked into this tree.
298 #[must_use]
299 pub fn is_empty(&self) -> bool {
300 self.exprs.is_empty() && self.stmts.is_empty() && self.decls.is_empty()
301 }
302}
303
304/// How many nodes of each kind a typed tree holds.
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
306pub struct Counts {
307 /// Expressions.
308 pub exprs: usize,
309 /// Statements.
310 pub stmts: usize,
311 /// Declarations.
312 pub decls: usize,
313}
314
315impl fmt::Debug for Tast {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 // The same reasoning as the untyped tree: nobody wants a translation unit as a `{:?}`,
318 // and the thing they did want has a printer.
319 let counts = self.counts();
320 f.debug_struct("Tast")
321 .field("exprs", &counts.exprs)
322 .field("stmts", &counts.stmts)
323 .field("decls", &counts.decls)
324 .field("top_level", &self.top_level.len())
325 .finish()
326 }
327}
328
329/// Generates the read side of a table that holds one item per index.
330macro_rules! node_table {
331 ($id:ty => $item:ty, $field:ident) => {
332 impl Index<$id> for Tast {
333 type Output = $item;
334
335 #[inline]
336 fn index(&self, id: $id) -> &$item {
337 &self.$field[id.index()]
338 }
339 }
340 };
341}
342
343/// Generates both sides of a side table whose items are added one at a time.
344macro_rules! side_table {
345 (
346 $(#[$doc:meta])*
347 $add:ident, $id:ty => $item:ty, $field:ident
348 ) => {
349 impl Tast {
350 $(#[$doc])*
351 ///
352 /// # Panics
353 ///
354 /// Panics if the table would exceed four billion entries.
355 pub fn $add(&mut self, item: $item) -> $id {
356 let id = Idx::from_usize(self.$field.len());
357 self.$field.push(item);
358 id
359 }
360 }
361
362 node_table!($id => $item, $field);
363 };
364}
365
366/// Generates both sides of a table that is read in runs.
367macro_rules! list_table {
368 (
369 $(#[$doc:meta])*
370 $add:ident, $list:ty => $item:ty, $field:ident
371 ) => {
372 impl Tast {
373 $(#[$doc])*
374 ///
375 /// # Panics
376 ///
377 /// Panics if the table would exceed four billion entries.
378 pub fn $add(&mut self, items: &[$item]) -> $list {
379 let start = Idx::from_usize(self.$field.len());
380 self.$field.extend_from_slice(items);
381 let end = Idx::from_usize(self.$field.len());
382 IdxRange::new(start, end)
383 }
384 }
385
386 impl Index<$list> for Tast {
387 type Output = [$item];
388
389 #[inline]
390 fn index(&self, list: $list) -> &[$item] {
391 &self.$field[list.as_usize_range()]
392 }
393 }
394 };
395}
396
397node_table!(ExprId => Expr, exprs);
398node_table!(StmtId => Stmt, stmts);
399node_table!(DeclId => Decl, decls);
400node_table!(CaseId => Case, cases);
401
402side_table! {
403 /// Adds a folded constant.
404 add_const, ConstId => Const, consts
405}
406side_table! {
407 /// Adds a string literal.
408 add_string, StrId => StringLiteral, strings
409}
410side_table! {
411 /// Adds a label, which is not defined until the statement it names has been seen.
412 add_label, LabelId => Label, labels
413}
414side_table! {
415 /// Adds an assembly statement.
416 add_asm, AsmId => Asm, asms
417}
418
419list_table! {
420 /// Adds a run of expression references, which is what a call's arguments are.
421 add_expr_refs, ExprList => ExprId, expr_refs
422}
423list_table! {
424 /// Adds a run of statement references, which is what a block is.
425 add_stmt_refs, StmtList => StmtId, stmt_refs
426}
427list_table! {
428 /// Adds a run of declaration references, which is what a declaration statement is.
429 add_decl_refs, DeclList => DeclId, decl_refs
430}
431list_table! {
432 /// Adds a run of string literal references, which is what an `asm` clobber list is.
433 add_str_refs, StrList => StrId, str_refs
434}
435list_table! {
436 /// Adds a run of label references, which is what the labels of an `asm goto` are.
437 add_label_refs, LabelList => LabelId, label_refs
438}
439list_table! {
440 /// Adds the operands of one section of an `asm` statement.
441 add_asm_operands, AsmOperandList => AsmOperand, asm_operands
442}
443list_table! {
444 /// Adds the cases of one `switch`, in the order a jump table wants them.
445 add_cases, crate::stmt::CaseList => Case, cases
446}
447list_table! {
448 /// Adds the values one initializer stores.
449 add_init_entries, crate::decl::InitList => InitEntry, init_entries
450}
451
452#[cfg(test)]
453mod tests {
454 use rucc_ast::BinaryOp;
455 use rucc_types::{IntKind, Types};
456
457 use super::*;
458 use crate::decl::{DeclKind, Definition, Emission, Linkage, StorageDuration};
459 use crate::expr::{Category, Conversion, ExprKind};
460
461 /// The sizes are asserted rather than left to whoever adds the next variant.
462 ///
463 /// A node that grows costs the whole arena, and the day one does is a day somebody should
464 /// have to say so out loud rather than a day the walk over a large translation unit gets
465 /// slower for no reason anybody can point at.
466 ///
467 /// A case is the outlier at forty eight bytes, because two `i128` bounds want sixteen byte
468 /// alignment and nothing smaller holds a `switch` over `__int128`. It buys its size back by
469 /// being rare: one entry per `case` rather than one per node.
470 ///
471 /// A declaration went from thirty six bytes to forty four when it was given the parameter
472 /// list of a function definition, which is a field only a definition fills in and every
473 /// declaration pays for. The alternative was a side table keyed by declaration, and it was
474 /// not taken: a lookup per function in a table that is empty for almost every entry is
475 /// worse than eight bytes on a node there are far fewer of than there are expressions.
476 ///
477 /// It went from forty four to forty eight when `constexpr` made a declaration a named
478 /// constant. The four bytes are padding rather than the flag: the four one byte fields
479 /// already filled a word exactly, so the first bit added costs the whole next one. The same
480 /// reasoning as above applies, with the numbers even further apart, since a translation
481 /// unit has a handful of named constants and hundreds of thousands of expressions.
482 ///
483 /// It went from forty eight to fifty two when a declaration was given the assembler name it
484 /// renames the symbol to. That one is a whole four byte index rather than a bit, and it goes
485 /// on the node for the reason the parameter list does: the name a symbol is emitted under is
486 /// asked for once per definition and once per reference to one, and a side table would be a
487 /// lookup on every one of those to find nothing almost every time.
488 ///
489 /// Fifty two to fifty six for the symbol an `alias` makes the name a second spelling of, which
490 /// is the same kind of index and is here for a weaker reason: it is asked for once per
491 /// declaration and almost none of them have one. It sits beside the assembler name because the
492 /// two are the same question asked from opposite ends, and a side table for one of them would
493 /// be a table nothing else in the tree has a use for.
494 ///
495 /// Fifty six to sixty for whether control comes back from a call to the function. It is one
496 /// bit and it costs four bytes for the reason `constexpr` cost four: the one byte fields
497 /// filled two words exactly, so the first bit past them takes the whole of the next one. The
498 /// alternative here is not a side table, it is folding the five booleans on this node into a
499 /// bitset, which would give back these four bytes and the four `constexpr` took. That is worth
500 /// doing when there is a sixth, and it is not worth doing for the fifth: each of the five says
501 /// a different thing about a declaration and each carries a paragraph saying which, and a
502 /// bitset takes the paragraphs off the fields and puts them on a table of constants.
503 #[test]
504 fn the_nodes_are_the_size_they_are_meant_to_be() {
505 assert_eq!(size_of::<Expr>(), 24);
506 assert_eq!(size_of::<Stmt>(), 24);
507 assert_eq!(size_of::<Decl>(), 60);
508 assert_eq!(size_of::<Case>(), 48);
509 }
510
511 #[test]
512 fn a_tree_hands_back_what_was_put_into_it() {
513 let types = Types::new();
514 let int = types.int(IntKind::Int);
515 let mut tast = Tast::new();
516
517 let one = tast.add_const(Const::Int(1));
518 let left = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
519 let right = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
520 let sum = Expr::new(
521 ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right },
522 int,
523 Category::Rvalue,
524 );
525 let sum = tast.expr(sum, Span::new(0, 5));
526
527 assert_eq!(tast[left].ty, int);
528 assert_eq!(tast[sum].category, Category::Rvalue);
529 assert_eq!(tast.expr_span(sum), Span::new(0, 5));
530 assert_eq!(tast.counts().exprs, 3);
531 assert_eq!(tast[one], Const::Int(1));
532 }
533
534 #[test]
535 fn a_conversion_is_a_node_and_not_a_difference_between_two_types() {
536 let types = Types::new();
537 let char_type = types.int(IntKind::Char);
538 let int = types.int(IntKind::Int);
539 let mut tast = Tast::new();
540
541 let object = tast.decl(
542 Decl {
543 name: None,
544 ty: char_type,
545 kind: DeclKind::Object,
546 linkage: Linkage::None,
547 duration: StorageDuration::Automatic,
548 state: Definition::Defined,
549 alignment: None,
550 constant: false,
551 retained: false,
552 asm_label: None,
553 alias: None,
554 inline: Emission::Silent,
555 gnu_inline: false,
556 noreturn: false,
557 visibility: None,
558 init: None,
559 params: DeclList::EMPTY,
560 body: None,
561 },
562 Span::DUMMY,
563 );
564 let name =
565 tast.expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
566 let read = tast.expr(
567 Expr::new(
568 ExprKind::Convert { kind: Conversion::Lvalue, operand: name },
569 char_type,
570 Category::Rvalue,
571 ),
572 Span::DUMMY,
573 );
574 let promoted = tast.expr(
575 Expr::new(
576 ExprKind::Convert { kind: Conversion::Arithmetic, operand: read },
577 int,
578 Category::Rvalue,
579 ),
580 Span::DUMMY,
581 );
582
583 // Nothing downstream has to work out that a `char` met an `int` somewhere: the two
584 // steps that got it there are in the tree, in the order they happened.
585 assert_eq!(tast[promoted].ty, int);
586 let ExprKind::Convert { kind, operand } = tast[promoted].kind else { panic!("a convert") };
587 assert_eq!(kind, Conversion::Arithmetic);
588 assert_eq!(tast[operand].ty, char_type);
589 }
590
591 #[test]
592 fn a_run_comes_back_as_a_slice() {
593 let types = Types::new();
594 let int = types.int(IntKind::Int);
595 let mut tast = Tast::new();
596
597 let zero = tast.add_const(Const::Int(0));
598 let args: Vec<ExprId> = (0..3)
599 .map(|_| {
600 tast.expr(Expr::new(ExprKind::Const(zero), int, Category::Rvalue), Span::DUMMY)
601 })
602 .collect();
603 let list = tast.add_expr_refs(&args);
604
605 assert_eq!(&tast[list], args.as_slice());
606 }
607
608 #[test]
609 fn a_label_is_made_before_it_is_defined_because_a_goto_may_come_first() {
610 let mut tast = Tast::new();
611 let mut names = rucc_base::Interner::new();
612 let name = names.intern("done");
613
614 let label = tast.add_label(Label { name, stmt: None });
615 let jump = tast.stmt(Stmt::Goto(label), Span::DUMMY);
616 let target = tast.stmt(Stmt::Empty, Span::DUMMY);
617 tast.define_label(label, target);
618
619 assert_eq!(tast[jump], Stmt::Goto(label));
620 assert_eq!(tast[label].stmt, Some(target));
621 }
622}