Der Drei-Adress-Code (englisch Three-Address Code, kurz TAC) ist eine konkrete Form der Zwischendarstellung in Compilern. Seine Regel: Jede Anweisung enthält höchstens eine Operation und höchstens drei Adressen — zwei Quellwerte und ein Ziel. Der Name geht auf das klassische Lehrbuch „Dragon Book“ (Aho, Sethi, Ullman) zurück.

Das Grundmuster

Ein Ausdruck wie a + b * c wird in mehrere einfache Anweisungen zerlegt:

t1 = b * c
t2 = a + t1
t3 = t2

Die t1, t2 und weitere sind temporäre Variablen: Es dürfen beliebig viele sein, denn sie existieren nur in der Zwischendarstellung und werden später von der Registerallokation auf echte CPU-Register abgebildet. Der Abstrakte Syntaxbaum (AST) hält die Struktur des Codes als Baum; der Drei-Adress-Code hält sie als lineare Befehlsfolge — deshalb heißt er auch linearer Zwischencode.

Typische Anweisungsformen

  • Zuweisung mit Operation: x = y op z (binär) und x = op y (unär, zum Beispiel Negation)
  • Kopien: x = y
  • Sprünge: goto L und bedingt if x relop y goto L
  • Aufrufe: param p, call f, return x
  • Speicherzugriffe: Indexierung t = a[i], Adressen &x und Dereferenzierung *p

Diese wenigen Formen reichen aus, um jede Kontrollstruktur auszudrücken: if, while und for werden in bedingte und unbedingte Sprünge übersetzt.

Entstehung und Verwendung

Der Compiler erzeugt den Drei-Adress-Code aus dem AST, meist nach der semantischen Analyse, indem er die Baumstruktur in eine lineare Folge von Drei-Adress-Anweisungen überführt. Danach laufen die Optimierungen — etwa die Konstantenfaltung oder die Tote-Code-Eliminierung — typischerweise auf dieser Form, und die Codegenerierung übersetzt sie schließlich in Assemblercode beziehungsweise Maschinensprache.

TAC im Vergleich zu anderen IRs

Während Bytecode stackbasiert arbeitet (Operationen holen ihre Operanden von einem Stapel), verwendet TAC benannte temporäre Variablen — dadurch ist er für Optimierungen einfacher zu analysieren. Die verfeinerte Form SSA (Static Single Assignment) schreibt vor, dass jede Variable im Programmtext genau einmal zugewiesen wird; sie baut auf TAC auf und verwendet an Verzweigungs-Zusammenflüssen spezielle Phi-Funktionen. Praktische Compiler wie GCC und LLVM nutzen eigene IR-Formen (RTL beziehungsweise LLVM IR), die ähnlich linear arbeiten wie TAC.