Jon Ross-Perkins 42e2280150 Clean up singleton TypeId use (#6300) 6 mesiacov pred
..
check 09710d102f Separate binding insts for refs and values (#6235) 6 mesiacov pred
README.md 2658142f7b Add link for 'How we compile' talk (#5810) 9 mesiacov pred
adding_features.md 42e2280150 Clean up singleton TypeId use (#6300) 6 mesiacov pred
check.svg a24816a1f4 Move toolchain architecture to markdown (#4242) 1 rok pred
coalesce_generic_lowering.md 7198050573 Docs for specific coalescing. (#5886) 9 mesiacov pred
diagnostics.md e09bf82d36 Update links to the DiagnosticConsumers (#4580) 1 rok pred
driver.md a24816a1f4 Move toolchain architecture to markdown (#4242) 1 rok pred
idioms.md a65f4b89e2 Make ValueStore require a ValueT parameter (#5757) 10 mesiacov pred
lex.md a24816a1f4 Move toolchain architecture to markdown (#4242) 1 rok pred
lower.md 7198050573 Docs for specific coalescing. (#5886) 9 mesiacov pred
parse.md 87f0e9723f Rename `StateStackEntry` to `State` (#5256) 1 rok pred
parse.svg a24816a1f4 Move toolchain architecture to markdown (#4242) 1 rok pred

README.md

Toolchain architecture

Table of contents

Goals

The toolchain represents the production portion of Carbon. At a high level, the toolchain's top priorities are:

  • Correctness.
  • Quality of generated code, including performance.
  • Compilation performance.
  • Quality of diagnostics for incorrect or questionable code.

TODO: Add an expanded document that details the goals and priorities and link to it here.

High-level architecture

The main components are:

Design patterns

A few common design patterns are:

  • Distinct steps: Each step of processing produces an output structure, avoiding callbacks passing data between structures.

    • For example, the parser takes a Lex::TokenizedBuffer as input and produces a Parse::Tree as output.

    • Performance: It should yield better locality versus a callback approach.

    • Understandability: Each step has a clear input and output, versus callbacks which obscure the flow of data.

  • Vectorized storage: Data is stored in vectors and flyweights are passed around, avoiding more typical heap allocation with pointers.

    • For example, the parse tree is stored as a llvm::SmallVector<Parse::Tree::NodeImpl> indexed by Parse::Node which wraps an int32_t.

    • Performance: Vectorization both minimizes memory allocation overhead and enables better read caching because adjacent entries will be cached together.

  • Iterative processing: We rely on state stacks and iterative loops for parsing, avoiding recursive function calls.

    • For example, the parser has a Parse::State enum tracked in state_stack_, and loops in Parse::Tree::Parse.

    • Scalability: Complex code must not cause recursion issues. We have experience in Clang seeing stack frame recursion limits being hit in unexpected ways, and non-recursive approaches largely avoid that risk.

See also Idioms for abbreviations and more implementation techniques.

Adding features

We have a walkthrough for adding features.

Videos

Talks

These talks are focused on implementation details of the toolchain, and can be helpful for learning how the toolchain internals work.

2025

Implementation walkthroughs

These are recordings of implementing PRs.

  • PR #4173: Parsing extern library syntax (video)
  • PR #4149: Implementing syntactic merge checks (video)