3アドレスコードとは
3アドレスコードは、コンパイラなどの
プログラミング言語処理系で用いられる
中間表現の一種です。この形式は、最適化処理を施すのに適しており、効率的なコード生成を可能にします。名前の通り、各命令は2つのソース(入力)アドレスと1つのデスティネーション(出力)アドレスを持ちます。これは、命令セットアーキテクチャにおける3オペランド形式と類似しています。
形式
3アドレスコードにおける各命令は、一般的に`(オペコード, ソース1, ソース2, デスティネーション)`の4つ組で表現されます。数式のように記述すると以下のようになります。
x ← a ⊕ b
ここで `⊕` は任意の二項演算子を表します。`a` と `b` は、具体的な実装においては即値、レジスタ、または
メモリアドレスであり、抽象的なレベルでは定数、変数、または一時的な中間変数です。`x` は代入の対象となる左辺値であり、即値や定数ではありません。
複数演算を含む式
ソースコード中に複数の演算を含む式がある場合、例えば `x := i + m n;` のような代入文は、3アドレスコードでは複数の命令に分解されます。
t ← m × n
x ← i + t
このように分解することで、各命令が単一の演算に対応し、ソースやデスティネーションは単純な変数に限定されます。これは、2アドレスコードのようにデスティネーションがソースと共通である必要がないため、より柔軟なコード生成が可能です。
他のコード形式との比較
2アドレスコードでは、デスティネーションがソースの片方と同一である必要があり、オペランドにメモリを使用できないなどの制限がありますが、3アドレスコードではこれらの制限がなく、任意の組み合わせが可能です。また、3オペランド形式は
RISCプロセッサで多く採用されており、命令フォーマットの効率化と最適化の促進に貢献しています。
ゼロレジスタの活用例
RISCプロセッサでは、ゼロレジスタ(例えばDEC AlphaのR31)を利用することで、例えば `y := -x;` という演算を以下のように3オペランド形式で表現できます。
y ← R31 - x
このように、3アドレスコードは、命令形式を統一し、より効率的なコード生成を可能にする点も、
RISCの特徴的な部分を担っています。
具体例
以下は、
C言語のプログラム例を3アドレスコードに変換したものです。
c
i := 0
L1: if i < 10 goto L2
goto L3
L2: t0 := i i
t1 := &b
t2 := i << 2
t3 := t1 + t2
*t3 := t0
i := i + 1
goto L1
L3:
この例では、配列へのアクセスや条件分岐など、より複雑な処理も3アドレスコードで表現できることがわかります。
まとめ
3アドレスコードは、コンパイラにおける
中間表現として、効率的な最適化とコード生成に不可欠な役割を果たします。2アドレスコードと比較して、より柔軟で汎用的な命令形式を提供し、様々な最適化技術を適用しやすいという利点があります。