2010/09/08(Wed)
○[pcc] ほんやく!
@その2
これの続きですよ。
前回に増していい加減な訳ですがクレームは/dev/nullへ。
NODE構造体 コンパイラの基本はツリー構造を使えちゅうことです、これはノード(NODE)という構造で表現されます。 このノードは二項(binary)、単項(unary)あるいは末端(leaf)であって、それぞれ異なる型を表現します。 全てのノードは演算子(op)あるいは型(type)で構成されとります。 ノードは一致するものがあれば命令(instruction)に変換されます、この処理は テーブルを検索することで行います。ノードからの出力は常にレジスタとみなされますが 入力ノードは蟻ノード(?)とみなされるかもしれません。 以下に一覧として中間ノードタイプをまとめました。フロントエンドはこれ以外のノードタイプをも 追加する方法を選ぶかもしれません。表にしたのは(ツリー)構造の各ノード種別に使用される要素です。 こいつらがフロントエンド(つまりpass1)かバックエンド(pass2)いずれかだけでしか有効でないなら 名前の後ろに(1)か(2)と書き加えてあります。 注意事項: n_op(演算子), n_type(型) そして n_qual(修飾子)はすべてのノードタイプに有効です。 n_name(ノード名称)は決してぬるぽにはなりません、使用しない場合は "" になるとです。 n_lval(左辺値)はその移植対象におけるもっとも大きな数値を保持できます。 n_dcon(浮動小数点数定数)は最低でもその移植対象のlong double型を表現できます。 全てのノードの変換結果は最後にはレジスタになると考えられます(?)。 末端ノード NAME メモリ上の位置の参照です、例えば変数にアクセスする場合など。 NAMEから取得したアドレスはICONになります。 n_lval - NAMEへのオフセットです、例えば a[3] とか n_sp (1) - NAMEのシンボルテーブル中のエントリになります n_name (2) - 名前の文字列を指すポインタです ICON 数値型の定数値です、素の数値にもアドレス値にもなります。 n_lval - 定数の値です n_sp (1) - アドレスの場合、シンボルテーブル中のエントリになります n_name (2) - シンボルの名前です FCON 浮動少数点数の定数値です。 n_dcon - 定数です REG レジスタのノードです、通常生成するのはレジスタアロケータの結果だけ ですが、しかしながらそのほかの場所でも生成されることがありえます。 それは例をあげれば、ターゲットがレジスタ引数を持つ場合なんかです。 n_rval - 数値でレジスタ変数になります OREG メモリ参照で、レジスタに対するオフセットになります。 通常はpass2のoreg()内でUMUL-PLUS-REG-ICONのチェインにより作られます。 ただし対象CPUがサポートしてる場合だけですが。 n_lval - 定数でレジスタからのオフセットになります n_rval - 数値でレジスタのインデックス順になります TEMP 一時ノードで、生成されるのはフロントエンドが値を レジスタでもメモリでもどっちにでも置けるようにするためです。 レジスタアロケータは後でそれらを可能であればレジスタに置こうと試みます。 n_rval - 数値で一時変数になります
力尽きた。
@ゆっくりによる pcc の内部解説
つーことでコードリーディングのお時間、ゆっくりの声でよんでね!
↑のドキュメントで触れられている
- 二項(binary)
- 単項(unary)
- 末端(leaf)
のノード分類ですが、実際にプログラム中では
以下の定数として宣言されています。
[ mip/manifest.h ]
46 /*
47 * Node types
48 */
49 #define LTYPE 02 /* leaf */
50 #define UTYPE 04 /* unary */
51 #define BITYPE 010 /* binary */
そして{末端,単項,二項}ノード種別についても以下同文。
ノード種類の説明は次回翻訳とあわせてです。
[ mip/node.h ]
147 /*
148 * Value nodes.
149 */
150 #define NAME 2
151 #define ICON 4
152 #define FCON 5
...
234 #define MAXOP 58
そして NODE は n_op フィールドにこのノード種別を保持します。
[ mip/node.h ]
62 typedef struct node {
63 struct node *next;
64 int n_op;
65 union {
ノード種別からノード分類へ変換するには、キバヤシよろしくノイズ除去に以下のマクロを使って
「ガラッ話は聞いた世界は滅亡するAA略」してください。
optype(p->n_op) == BITYPE
この optype はマクロで以下の場所で宣言されています。
[ mip/pass2.h ]
400 #define optype(o) (dope[o]&TYFLG)
このマクロ中で使われている dope は int 型の配列で以下のよに定義されとります。
他にも opst ちうのもありますが、これについては後述。
[ mip/pass2.h ]
360 extern int dope[]; /* a vector containing operator information */
361 extern char *opst[]; /* a vector containing names for ops */
配列の添字にはさっきのノード種別を使い、上限値は MAXOP となります。
このdope及びopstはフロントエンドが mkdope() を呼んだタイミングで初期化されます。
[ mip/common.c ]
319 int dope[DSIZE];
320 char *opst[DSIZE];
...
385 void
386 mkdope()
387 {
388 struct dopest *q;
389
390 for( q = indope; q->dopeop >= 0; ++q ){
391 dope[q->dopeop] = q->dopeval;
392 opst[q->dopeop] = q->opst;
393 }
394 }
初期化の為の元ソースは mip/common.c にテーブルが宣言されてる indope です。
[ mip/common.c ]
322 struct dopest {
323 int dopeop;
324 char opst[8];
325 int dopeval;
326 } indope[] = {
327 { NAME, "NAME", LTYPE, },
328 { REG, "REG", LTYPE, },
...
382 { -1, "", 0 },
383 };
ですので今回のINCR/DECRのpass2化のように種別を増やすなんてときに
弄るのはここになります。
opst は ccom -Xb などのデバックオプションでノードツリーをダンプする際に
ノードタイプを画面表示可能な文字列に変換する用に以下同文、例えば mip/reader.c では
664 if (rv == FFAIL && !q)
665 comperr("Cannot generate code, node %p op %s", p,opst[p->n_op]);
のように使われています、まぁ今どきのプログラムならNODE構造体にtoString()とかto_strみたいな
関数を用意するんでしょうが、なにせ30年前のコードですので以下略
これらのノード種別は pass2 で有効なものであって、言語(=フロントエンド)非依存です。
言語に依存するノード種別については C であれば cc/ccom/pass1.h に定義されています。
539 /*
540 * C compiler first pass extra defines.
541 */
542 #define QUALIFIER (MAXOP+1)
...
572 #define INCR (MAXOP+26)
573 #define DECR (MAXOP+27)
今回問題になっている INCR/DECR は今のところ言語依存の扱いですやね。
なので pass1.h に定義され、pass2に渡される前に別の形に書き換えられてしまう運命なのです。
話し戻してさっきの dope[] の添字にこの pass1 オンリーのノード種別を渡したりすると、たうぜん
array index out of bounds の悲劇が発生しますので、cc/ccom/trees.c には cdope() という
wrapper 関数が定義されていて、pass1ではこっちを使います。
2647 int
2648 cdope(int op)
2649 {
2650 if (op <= MAXOP)
2651 return dope[op];
2652 switch (op) {
...
2696 case INCR:
2697 case DECR:
2698 return BITYPE|ASGFLG;
2699 }
...
同様に opst にも wrapper が用意されています。
2601 char *
2602 copst(int op)
2603 {
2604 if (op <= MAXOP)
2605 return opst[op];
...
2632 SNAM(INCR,++)
2633 SNAM(DECR,--)
...
今日はここまで。