3アドレスコード: three-address code)とは、コンピュータ・プログラミング言語処理系などにおける中間表現などにおける形式の1パターンである。処理系においては、コンパイラ最適化などの処理を掛けるのに適している。2つの入力と1つの出力のアドレスメモリまたはレジスタ)を指定する形式であるため、3アドレスコードと呼ばれる。命令セットアーキテクチャにおける「3オペランド」形式の類推とも言える。

概要 編集

この形式における各命令は、形式的に4ツ組で表現すると (オペコード, ソース1, ソース2, デスティネーション) である。より直感的にプログラミング言語における代入と2項演算子による数式っぽく書くと、

 

あるいは、

 

といったようになる(ただし   は、なんらかの演算の2項演算子とする)。「左辺」「右辺」という用語の都合から、ここでは以後、後者の記法を使う。

ここで、a と b は、比較的に具体的な(目的コードに近い)場合は、即値か、レジスタや何らかのアドレスのメモリである。比較的に抽象的な(ソースコードに近い)場合は、定数か、ソースコード中の変数、あるいはソースコード中にはあらわれない、処理系が生成するテンポラリな中間変数である。また、x は、a や b とは違い、それを対象として代入できるもの(左辺値を持つもの)でなければならず、即値や定数にはならない。

ソースコード中に次のような、複数の演算で構成された式を含む代入文となっている、1個の文があったとする。

x := i + m * n;

これをそのまま1命令で表現することはできない[1]。そのため、次のような2つの命令に分解される。

 
 

オペランド数が違っていても「3アドレスコード」という呼び方をすることもある。3アドレスコードの要諦は、各命令が基本的に演算命令1個に相当する演算を表現して[2]いなければならずソースやデスティネーションが任意の式ではなく単純な1個の変数などでなければならない点(これは2アドレスコード等でも同様)と、例えば2アドレスコードではデスティネーションをソースの片方と共通としなければならない制限がある(そのオペランドにはメモリが使えずレジスタとするという、より強い制限がしばしば掛かる)のと異なり、任意の組合せが可能な点である。

他に、プロセッサ命令セットにおける3オペランド形式の場合、RISCプロセッサで、命令フォーマットから豊富なメモリアドレッシングを排して空いた空間を利用して、従来のCISCプロセッサに多かった2オペランド形式から、より最適化などに適した3オペランド形式としたRISCプロセッサが多いという経緯がある。また例えば、ゼロレジスタ(例えばDEC AlphaプロセッサのR31)を併用すると、

y := -x;

を、

 

とできる、などのように3オペランドに統一できる、といったようなことはRISCらしさと言われている一例である。

編集

int main(void)
{
    int i;
    int b[10];
    for (i = 0; i < 10; ++i) {
        b[i] = i*i; 
    }
}

上記のC言語プログラムを変換すると、次のようになる。

	i := 0                  ; 代入
L1:	if i < 10 goto L2       ; 条件付分岐
	goto L3                 ; 無条件分岐
L2:	t0 := i*i
	t1 := &b                ; 演算対象のアドレス
	t2 := i << 2            ; bは整数型配列なのでオフセットはi*4(=i<<2)バイト
	t3 := t1 + t2           ; t3 には b[i] のアドレスが設定される
	*t3 := t0               ; ポインタを通して格納する
	i := i + 1
	goto L1
	L3:

編集

  1. ^ しばしばある、x += m * n のようなソースコードと、積和演算命令に対応した処理系、といったような場合には違ってくるが、ここでは一般的な場合を考えることとする。
  2. ^ ここではロード・ストア命令は考えない。

外部リンク 編集