Files
RustAst/docs/Optimization_Analysis.md
T
Michael Schimmel 8f7947bde1 feat: Add AST analysis pass for purity and recursion
This commit introduces a new AST analysis pass that identifies function
purity and recursion. This information is then used by the optimizer and
specializer to make more informed decisions, particularly regarding
inlining.

The `Analyzer` struct and its associated `Analysis` struct are
responsible for traversing the AST and collecting this data.

Key changes include:
- A new `analyzer` module is added to `ast::compiler`.
- `Analyzer::analyze` performs a two-pass traversal to collect
  global-to-lambda mappings and then analyze purity and recursion.
- The `Optimizer` and `Specializer` are updated to accept and utilize
  the `Analysis` data.
- Recursion checks in `Optimizer` and `Specializer` are replaced with
  checks against the pre-computed `Analysis.is_recursive` set.
- The `Environment` now stores and passes the `Analysis` results to the
  compiler stages.
2026-02-22 15:00:20 +01:00

3.3 KiB

Analyse der AST-Optimierungen und Spezialisierung

Datum: 23.05.2024 Tool: src/bin/ast.rs mit --dump

1. Motivation

Diese Analyse untersucht die Effektivität der Rust-Portierung des Optimizers. Ziel war es, die theoretischen Konzepte aus den Design-Dokumenten (wie "Closure Cracking" und "Beta-Reduction") empirisch am lebenden Objekt zu verifizieren.

2. Kern-Erkenntnisse

A. Aggressives Constant Folding (The "Zero-Cost" Promise)

Der Optimizer ist extrem aggressiv bei der Vorkalkulation von Werten. Ein komplexer Ausdruck wie:

(do
  (def MYPI 3.14)
  (def radius 10)
  (* MYPI (* radius radius)))

wird im optimierten AST zu einer einzigen Konstante reduziert: Constant: 314. Erstaunlich: Nicht nur die Arithmetik wird gefaltet, sondern auch die def-Bindungen werden komplett eliminiert (Dead Code Elimination), da sie nach dem Inlining nicht mehr benötigt werden.

B. Beta-Reduction & Lambda-Inlining

Aufrufe von Lambda-Literalen werden bereits zur Compile-Zeit "geknackt" (Cracking).

  • Beispiel: ((fn [x] (+ x 1)) 10) -> Constant: 11.
  • Verschachtelung: Selbst bei verschachtelten Lambdas wie ((fn [x] (fn [] x)) 10) erkennt der Optimizer die Konstante und reduziert den äußeren Aufruf auf ein Lambda, das lediglich Constant: 10 zurückgibt.

C. Die Rolle der Spezialisierung (Monomorphisierung)

Eine der wichtigsten architektonischen Erkenntnisse war das Verhalten des Specializer. Selbst wenn das Tool mit --no-opt aufgerufen wird, tauchen im Dump oft spezialisierte Closures auf.

  • Grund: Die Spezialisierung ist in Environment::link fest verdrahtet, da sie für die Korrektheit (z. B. Overload-Resolution von Operatoren wie +) notwendig ist.
  • Effekt: Ein globaler Aufruf von f mit einem Int wird zu einem Aufruf einer spezialisierten Version von f, die bereits auf Int optimiert wurde. Der "echte" Optimizer baut darauf auf und führt dann das Inlining durch.

3. Überraschungen & Kuriositäten

Der Rekursions-Schutz

Beim Testen einer rekursiven Funktion (def f (fn [x] (f x))) zeigte der Dump ein interessantes Muster:

  • Der Optimizer führt genau eine Ebene des Inlinings durch, bevor der inlining_stack greift.
  • Im Dump resultiert dies in einem "Callee", der selbst ein Lambda ist, welches wiederum die globale Funktion aufruft.
  • Dabei tauchten spezialisierte Typen wie Vector(Any, 1) oder Matrix(Any, [1, 1]) auf. Dies sind interne Artefakte der Monomorphisierung, die zeigen, wie das System versucht, Argumentlisten in einheitliche Tupel-Strukturen zu zwingen (Unification).

String-Folding

Der Optimizer beherrscht das Zusammenfügen von Strings via + bereits zur Compile-Zeit.

  • (+ "Hello, " "Rust!") wird im AST direkt zu "Hello, Rust!".
  • Dies ist besonders wertvoll für Makros, die Code-Teile oder Bezeichner generieren.

4. Fazit für die Entwicklung

Die Pipeline (Expand -> Bind -> Specialize -> Optimize -> TCO) ist robust. Die Entscheidung, die Spezialisierung vor den Optimizer zu schalten, erweist sich als goldrichtig, da der Optimizer so auf einem bereits typsicheren und "aufgecrackten" Baum operieren kann.

Das Tool ast.exe --dump bleibt das wichtigste Instrument, um sicherzustellen, dass neue Sprachfeatures nicht den "Fast Path" des Compilers verlassen.