第14回ASICデザインコンテスト/コンパイラ
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
[[第14回ASICデザインコンテスト]]
*コンパイラ [#v6c85284]
共同で読み解くSN/X付属コンパイラ。
----
#contents
**はじめに [#ld6495b8]
パルテノン研究会主催の第14回ASICデザインコンテスト参加予定者全員でSN/X付属コンパイラを読み解くプロジェクトです。~
全21種あるオペレータが、それぞれどんな引数をとり、どんな動作をするか記述しましょう。
IN, OUT, PRINTに関してはコンテストの課題で必要としていないので削除しました。
**Operatorについて [#wda309fd]
オペレータの動作の意味、いくつ引数を必要とするか、引数が持つ意味など。
***FDEFA [#b135c1b9]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--引数有りの関数を定義する。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 定義する関数内の処理式。
-備考
--引数有り関数なので引数と戻りアドレス値の2つのレジスタを待避する。
case FDEFA:
printf("\t bal \t $0, \t L%03d \n", lbl1 = lbl++); 最後列に追加記述していく
printf("foo: \n");
iinst("lda", 3, -2, 3); 第3引数の[-2]はレジスタを2つ待避していることを意味する
iinst("st", 2, 0, 3);
iinst("st", 1, 1, 3);
ex(p->opr.op[0], 1, 0); ここで関数内処理を再帰的に解析する
printf("fooexit: \n");
iinst("ld", 2, 0, 3);
iinst("lda", 3, 2, 3);
iinst("bal", 0, 0, 2);
printf("L%03d: \n", lbl1);
break;
-実際に書き出されるアセンブラの例~
(始めと終わりを無条件ジャンプで囲んでいるのは関数呼び出し命令([[FUNC>#l6c73f45]])以外で実行されないようにするため。)
| bal $0, L100
|foo:
| /* ex();を用いて関数内処理を再帰的に書き出す
|fooexit:
|L100:
***FDEF [#edaa26b6]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--引数無しの関数を定義する。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 定義する関数内の処理式。
-備考
--基本は上の[[FDEFA>#b135c1b9]]と同じ。引数無し関数なので戻りアドレス値の$2だけ待避している。
case FDEF:
printf("\t bal \t $0, \t L%03d \n", lbl1 = lbl++);
printf("foo: \n");
iinst("lda", 3, -1, 3);
iinst("st", 2, 0, 3);
ex(p->opr.op[0], 1, 0);
printf("fooexit: \n");
iinst("ld", 2, 0, 3);
iinst("lda", 3, 1, 3);
iinst("bal", 0, 0, 2);
printf("L%03d: \n", lbl1);
break;
-実際に書き出されるアセンブラの例~
([[FDEFA>#b135c1b9]]との違いは引数の有無だが違いはレジスタを待避する内部的な処理だけなのでアセンブラ的には見た目に違いはない。)
| bal $0, L100
|foo:
| /* ex();を用いて関数内処理を再帰的に書き出す */
|fooexit:
|L100:
***RETURN [#n01eabfd]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数外に処理結果を渡す(C言語ではおなじみですね)
-引数はいくつ必要か?
--0〜1個
-op[0]
--TypeCon,Id,Opr : 戻り値。
-備考
--引数無し(p->opr.nops==0)の時は戻り値無し。
--戻り値が数式等の場合再帰的に解析して書き出す。
case RETURN:
if (p->opr.nops > 0) {
ex(p->opr.op[0], 1, 0); 戻り値が存在するとき$1に結果が入る
}
printf("\t bal \t $0, \t fooexit \n"); bal命令で外(fooexit)に出る
break;
-実際に書き出されるアセンブラの例~
(関数内で呼ばれ、[[FDEF>#edaa26b6]]などで用意したfooexitに戻る。)
| /* 戻り値がある場合、ex();を用いて $1 に格納して渡す */
| bal $0, fooexit
***FOR [#a6d5e316]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--forループ文(定められた回数だけ反復制御を行う)の実行。
-引数はいくつ必要か?
--4個
-op[0]
--TypeOpr : カウンタの初期値処理。
-op[1]
--TypeOpr : ループの終了判定処理(定数ってコトは無いハズ)
-op[2]
--TypeOpr : ループ毎の処理(カウンタの更新など)
-op[3]
--TypeOpr : ループ内処理。
-備考
--「 for( op[0]; op[1]; op[2] ) op[3]; 」の形。
--書き出す順番が op[0], op[1], op[3], op[2] だがwhile文でやったと考えると分かりやすいか。
case FOR:
ex(p->opr.op[0], reg, pres); 初期値設定
printf("L%03d: \n", lbl1 = lbl++);
ex(p->opr.op[1], reg, pres); ループ終了判定
printf("\t bz \t $%d, \t L%03d \n", reg, lbl2 = lbl++);
ex(p->opr.op[3], reg, pres); ループ内処理
ex(p->opr.op[2], reg, pres); ループ毎の処理
printf("\t bal \t $0, \t L%03d \n", lbl1);
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
(終了判定式の値がゼロになるまで L100 以下の部分を繰り返す。)
| /* ループの初期値を求める記述を書き出す */
|L100:
| /* ループの終了判定値を求める記述(条件式)を書き出す */
| bz $[reg], L101
| /* ループ内処理を書き出す */
| /* ループ毎の処理の記述を書き出す */
| bal $0, L100
|L101:
***WHILE [#g06304e3]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--while文(条件式を前判定して反復制御を行う)の実行
-引数はいくつ必要か?
--2個
-op[0]
--TypeOpr(,Con,Id) : ループの終了判定処理。
-op[1]
--TypeOpr : ループ内処理。
-備考
--「 while( op[0] ) op[1]; 」の形。
case WHILE:
printf("L%03d: \n", lbl1 = lbl++); ループ開始位置設定
ex(p->opr.op[0], reg, pres);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl2 = lbl++); $[reg]==0で終了
ex(p->opr.op[1], reg, pres);
printf("\t bal \t $0, \t L%03d \n", lbl1); 開始位置に戻る
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
([[FOR>#a6d5e316]]より短く見えるがループ内処理でカウンタ設定やら何やらをしているハズ。)
|L100:
| /* ループの終了判定値を求める記述(条件式)を書き出す */
| bz $[reg], L101
| /* ループ内処理を書き出す */
| bal $0, L100
|L101:
***HALT [#wbdec22c]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--プロセッサの強制終了。
-引数はいくつ必要か?
--無し。
-備考
--大抵はアセンブラの最後に書かれる。
case HALT:
printf("\t hlt \n");
break;
-実際に書き出されるアセンブラの例~
(簡潔に終わりを記述。)
| hlt
***IF [#ge91e876]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--if文の実行
-引数はいくつ必要か?
--2〜3個
-op[0]
--TypeId,TypeOpr : 分岐を行うための条件
-op[1]
--TypeOpr : 条件分岐成立後の処理
-op[2]
--TypeOpr : 条件分岐不成立後の処理、省略可能。else文があるときの処理にあたる。
-備考
--「 if( op[0] ) op[1]; else op[2]; 」もしくは「if( op[0] ) op[1]; 」の形。
case IF:
ex(p->opr.op[0], reg, pres);
if (p->opr.nops > 2) { else文があるときはオペランドが3個ある
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++); bz命令で条件が成り立つか調べる
ex(p->opr.op[1], reg, pres); if文が成り立ったときの処理
printf("\t bal \t $0, \t L%03d \n", lbl2 = lbl++); if文が成立した場合、if文の終わりまでジャンプする
printf("L%03d: \n", lbl1); if文が成り立たなかったときelse文の処理をする
ex(p->opr.op[2], reg, pres); else文の処理
printf("L%03d: \n", lbl2); if文全体の終わり
}
else { else文が無いときの処理
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++); bz命令で条件が成り立つか調べる
ex(p->opr.op[1],reg, pres); if文が成り立ったときの処理
printf("L%03d: \n", lbl1); if文の終わり
}
break;
-実際に書き出されるアセンブラの例~
(書き出されるのは当然「if〜」か「if〜else〜」のどちらかだけである。)
| /* ex();で「if〜」か「if〜else〜」かの判定をする */
| [if - else - ]
|
| bz $[reg], L100
| /* if文が成り立つときの処理を書き出す */
| bal $0, L101
|L100:
| /* else文の処理を書き出す */
|L101
| [if - ]
|
| bz $[reg], L100
| /* if文が成り立つときの処理を書き出す */
|L100:
***MWT [#wbf18e11]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--配列(変数)への書き込みを行う
-引数はいくつ必要か?
--3個
-op[0]
--TypeOpr : 配列(変数)のインデックスの処理。
-op[1]
--TypeOpr : 配列(変数)に書き込む値の処理。
-op[2]
--TypeId : ストアするのがどの配列なのか(つまりどの変数か)の参照に使用。
-備考
--「 st $[(op[1])], [変数]( $[(op[0])] ) 」の形。逆に解りにくいかな。
--上の[変数]は「p->opr.op[2]->id.i+1」の値から対応する変数文字を判断する。
--……対応は[a:1]〜[z:26]でいいのかな。
case MWT:
ex(p->opr.op[0],reg, pres);
ex(p->opr.op[1],regx, 1);
iinst("st",
regx,
(p->opr.nops > 2) ? p->opr.op[2]->id.i+1 : 0,
reg );
break;
-実際に書き出されるアセンブラの例~
( $2 の値を A[$1] にストア。Aはop[2]から求める配列(変数) )
| /* ex();で書き込まれる配列のインデックス値をレジスタに($1とする) */
| /* ex();で配列に書き込まれる値をレジスタに($2とする) */
| /* iinst();でストアする配列(変数)に対応する値を求めて使う(Aとする) */
| st $2, A($1)
***'=' [#d1443c66]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--代入文(右辺から左辺へ)の実行。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId : 代入されるのがどの配列なのか(つまりどの変数か)の参照に使用。
-op[1]
--TypeOpr : 代入する値の処理。
-備考
--「 A[$0] = op[1]; 」の形。Aは代入される配列で op[0] から求める。
case '=':
ex(p->opr.op[1],reg, pres);
iinst("st", reg, p->opr.op[0]->id.i+1, 0);
break;
-実際に書き出されるアセンブラの例~
( A[$0] = op[1]; Aはop[0]から求める配列(変数) )
| /* ex();で配列に書き込まれる値をレジスタに($1とする) */
| /* iinst();で代入される配列(変数)に対応する値を求めて使う(Aとする) */
| st $1, A($0)
***UMINUS [#s1e19644]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--入力(子ノード)の値を負にして返す(単項演算子のマイナス。「a=-b+(-c)」とか)
-引数はいくつ必要か?
--1個
-op[0]
--TypeCon,Id : 入力値。コレの2の補数を取って出力にする。
-備考
--「 op[0] = ~(op[0]); 」の形か。
case UMINUS:
ex(p->opr.op[0], reg, pres);
rinst2("not", reg, reg); ビット反転し、
iinst("lda", reg, 1, reg); 1を追加する(2の補数)
break;
-実際に書き出されるアセンブラの例~
(入力値の負をとって返すだけなので何も書き出されはしない)
| /* ex();で対象の値をレジスタに($1とする) */
| /* rinst2();で not をとり $1 をビット反転 */
| /* iinst();で $1 に +1 して2の補数により負値を得る */
***MRD [#ia8f5e99]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--配列(変数)からの読み出しを行う
-引数はいくつ必要か?
--2個
-op[0]
--TypeOpr : 配列(変数)のインデックスの処理。あと出力もコレ(TypeId)。
-op[1]
--TypeId : 読み出すのがどの配列なのか(つまりどの変数か)の参照に使用。
-備考
--「 ld $[(op[0])], [変数]( $[(op[0])] ) 」の形。
case MRD:
ex(p->opr.op[0], reg, pres);
iinst("ld", reg,(p->opr.nops>1) ? p->opr.op[1]->id.i+1 : 0, reg);
break;
-実際に書き出されるアセンブラの例~
( A[$1] の値を $1 に入れる。Aはop[1]から求める配列(変数) )
| /* ex();で読み込む配列のインデックス値をレジスタに($1とする) */
| /* iinst();でロードする配列(変数)に対応する値を求めて使う(Aとする) */
| ld $1, A($1)
***ARGV [#cac12c88]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数の呼び出し時にメモリに格納してある引数をロードする。
-引数はいくつ必要か?
--無し。
-備考
--「 ld $[reg], [frametop+1]($3) 」の形。
--当然、引数有り関数(FDEFAで生成)の場合しか出てこない。
--frametop は引数のあるアドレスがスタックポインタ($3)からどれくらいかを示す。
case ARGV:
iinst("ld" ,reg, frametop+1, 3);
break;
-実際に書き出されるアセンブラの例~
(引数値をとってくるだけなので書き出されるアセンブラは無し)
| /* 読み出した値を入れるレジスタは ARGV が呼び出されたときには決まっている?($1とする) */
| /* iinst();で引数値を $1 に読み出す*/
***FUNC [#l6c73f45]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数の呼び出しを行う。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 引数処理。ARGV が来るのか?
-備考
--あくまで既にある関数を呼び出すだけ。関数生成は[[FDEF>#edaa26b6]],[[FDEFA>#b135c1b9]]が行う。
case FUNC:
if(pres) { presが真=レジスタ使用中なのでスタックに待避
iinst("lda", 3 ,-1 ,3);
iinst("st", regx, 0, 3);
frametop += 1;
}
ex(p->opr.op[0], 1, 0);
printf("\t bal \t $2, \t foo \n"); 関数に飛ぶ。
if(reg != 1) { スタックトップレジスタが$1で無い(この場合$2かな)とき、
iinst("lda", reg, 0, 1); $1の値を$2にコピー(関数返値の受け渡し処理)
}
if(pres) { 初めに待避したならもとに戻す。
iinst("ld", regx, 0, 3);
iinst("lda", 3, 1, 3);
frametop -= 1;
}
break;
-実際に書き出されるアセンブラの例~
(スタックトップレジスタ(STReg)とは次に使われる位置にあるレジスタ、でいいんだろか)
| /* レジスタ待避 */
| /* ex();で引数値をレジスタに($1のハズ) */
| bal $2, foo
| /* 関数[foo]の終了後ここに戻る */
| /* STReg が $1 で無いなら $1 の値をそっち(ここでは$2か)にコピー */
| lda $[STReg], 0($1)
| /* レジスタ復帰 */
**Operatorが上記以外に該当する場合 [#yf406048]
以下の前処理を行い「 op[0] (何らかのオペレータ) op[1] 」を
「 reg (何らかのオペレータ) regx 」と置き換えてある。
default:
ex(p->opr.op[0], reg, pres); op[0] の処理結果を reg に
if(pres) {
iinst("lda", 3, -1, 3); もしレジスタが使用中ならスタックに待避
iinst("st", regx, 0, 3);
frametop += 1;
}
ex(p->opr.op[1], regx, 1); op[1] の処理結果を regx に
switch(p->opr.oper)
{
/* (次からの処理) */
}
if(pres) { 処理が終わったらレジスタを復帰
iinst("ld", regx, 0, 3);
iinst("lda", 3, 1, 3);
frametop -= 1;
}
***'+' [#k6deaed5]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--加算を行う。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目、変数regに相当
-op[1]
--TypeCon, TypeId : 引数2個目、変数regxに相当
-備考
--「 op[0] = op[0] + op[1]; 」の処理をする。
C言語の「a=b+1」などは代入文「=」と加算「+」の組合せで行われている?
case '+':
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = $[reg] + $[regx]; )
| add $[reg], $[reg], $[regx]
***'-' [#a1264cfb]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--減算を行う。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目、変数regに相当
-op[1]
--TypeCon, TypeId : 引数2個目、変数regxに相当
-備考
--減算命令はnot、lda、add命令を組み合わせて行われる。regxの2の補数を生成してregに加算する。
case '-':
rinst2("not", regx, regx);
iinst("lda", regx, 1, regx);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = $[reg] + ( - $[regx] ); 負値を加算して減算とする)
| not $[regx], $[regx]
| lda $[reg], 1($[regx])
| add $[reg], $[reg], $[regx]
***'<' [#y202a279]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--値の比較を行う。a<b?の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目。regに相当し、演算子の左辺
-op[1]
--TypeCon, TypeId : 引数2個目。regxに相当し、演算子の右辺
-備考
--slt命令を用いて実行される。op[0] < op[1] のとき 1 を返し、そうでない場合は 0 を返す。
case '<':
rinst("slt", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] < $[regx] ) ? 1 : 0; )
| slt $[reg], $[reg], $[regx]
***'>' [#q466fe7d]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--値の比較を行う。a>b?の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目。regに相当し、演算子の左辺
-op[1]
--TypeCon, TypeId : 引数2個目。regxに相当し、演算子の右辺
-備考
--'<' の場合と逆なので、regとregxを入れ換えて実行する。
case '>':
rinst("slt", reg, regx, reg);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[regx] < $[reg] ) ? 1 : 0; )
| slt $[reg], $[regx], $[reg]
***GE[>=] [#xef030ae]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a>=b?( a が b 以上か) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a>=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a>=b? の b )
-備考
--[['<'>#y202a279]]の結果の0と1を入れ替えることで判定している。
--下の記述にはバグがあり正しい判定が出来ていないようです。
case GE:
rinst("slt", reg, reg, regx);
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
iinst("lda", regx, 1, 0);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( 保留1 )
| (o^-')b!
***LE[<=] [#udc0252d]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a<=b? ( a が b 以下か)の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a<=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a<=b? の b )
-備考
--[['>'>#q466fe7d]]の結果の0と1を入れ替えることで判定している。
--上の GE 同様にバグがあり正しい判定が出来ていないようです。
case LE:
rinst("slt", reg, regx, reg);
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
iinst("lda", regx, 1, 0);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( 保留2 )
| (o^-')b!
***NE[!=] [#s962586d]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a!=b?( a が b と等しくないか) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a!=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a!=b? の b )
-備考
--「 regx - reg != 0 ? 1 : 0 」として判定 ( regx - reg が 0 で無いなら 1 を、そうでないなら 0 を返し条件分岐 )
case NE:
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
rinst("add", reg, reg, regx);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++);
iinst("lda", reg, 1, 0); reg != regx のとき、reg = 1
printf("L%03d: \n", lbl1); reg != regx で無いときは既に reg = 0 なのでそのまま
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] != $[regx] ) ? 1 : 0; )
| not $[reg], $[reg]
| lda $[reg], 1($[reg])
| add $[reg], $[reg], $[regx]
| bz $[reg], L100
| lda $[reg], 1($0)
|L100:
***EQ[==] [#l69a71b6]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a==b?( a が b に等しいか) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a==b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a==b? の b )
-備考
--「 regx - reg == 0 ? 1 : 0 」として判定 ( regx - reg が 0 なら 1 を、そうでないなら 0 を返し条件分岐 )
case EQ:
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
rinst("add", reg, reg, regx);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++);
iinst("lda", reg, 0, 0); reg == regx で無いとき、reg = 0
printf("\t bal \t $0, \t L%03d \n", lbl2 = lbl++);
printf("L%03d: \n", lbl1);
iinst("lda", reg, 1, 0); reg == regx のとき、reg = 1
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] == $[regx] ) ? 1 : 0; )
| not $[reg], $[reg]
| lda $[reg], 1($[reg])
| add $[reg], $[reg], $[regx]
| bz $[reg], L100
| lda $[reg], 0($0) reg=0
| bal $0, L101
|L100:
| lda $[reg], 1($0) reg=1
|L101:
(o^-')b!
終了行:
[[第14回ASICデザインコンテスト]]
*コンパイラ [#v6c85284]
共同で読み解くSN/X付属コンパイラ。
----
#contents
**はじめに [#ld6495b8]
パルテノン研究会主催の第14回ASICデザインコンテスト参加予定者全員でSN/X付属コンパイラを読み解くプロジェクトです。~
全21種あるオペレータが、それぞれどんな引数をとり、どんな動作をするか記述しましょう。
IN, OUT, PRINTに関してはコンテストの課題で必要としていないので削除しました。
**Operatorについて [#wda309fd]
オペレータの動作の意味、いくつ引数を必要とするか、引数が持つ意味など。
***FDEFA [#b135c1b9]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--引数有りの関数を定義する。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 定義する関数内の処理式。
-備考
--引数有り関数なので引数と戻りアドレス値の2つのレジスタを待避する。
case FDEFA:
printf("\t bal \t $0, \t L%03d \n", lbl1 = lbl++); 最後列に追加記述していく
printf("foo: \n");
iinst("lda", 3, -2, 3); 第3引数の[-2]はレジスタを2つ待避していることを意味する
iinst("st", 2, 0, 3);
iinst("st", 1, 1, 3);
ex(p->opr.op[0], 1, 0); ここで関数内処理を再帰的に解析する
printf("fooexit: \n");
iinst("ld", 2, 0, 3);
iinst("lda", 3, 2, 3);
iinst("bal", 0, 0, 2);
printf("L%03d: \n", lbl1);
break;
-実際に書き出されるアセンブラの例~
(始めと終わりを無条件ジャンプで囲んでいるのは関数呼び出し命令([[FUNC>#l6c73f45]])以外で実行されないようにするため。)
| bal $0, L100
|foo:
| /* ex();を用いて関数内処理を再帰的に書き出す
|fooexit:
|L100:
***FDEF [#edaa26b6]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--引数無しの関数を定義する。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 定義する関数内の処理式。
-備考
--基本は上の[[FDEFA>#b135c1b9]]と同じ。引数無し関数なので戻りアドレス値の$2だけ待避している。
case FDEF:
printf("\t bal \t $0, \t L%03d \n", lbl1 = lbl++);
printf("foo: \n");
iinst("lda", 3, -1, 3);
iinst("st", 2, 0, 3);
ex(p->opr.op[0], 1, 0);
printf("fooexit: \n");
iinst("ld", 2, 0, 3);
iinst("lda", 3, 1, 3);
iinst("bal", 0, 0, 2);
printf("L%03d: \n", lbl1);
break;
-実際に書き出されるアセンブラの例~
([[FDEFA>#b135c1b9]]との違いは引数の有無だが違いはレジスタを待避する内部的な処理だけなのでアセンブラ的には見た目に違いはない。)
| bal $0, L100
|foo:
| /* ex();を用いて関数内処理を再帰的に書き出す */
|fooexit:
|L100:
***RETURN [#n01eabfd]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数外に処理結果を渡す(C言語ではおなじみですね)
-引数はいくつ必要か?
--0〜1個
-op[0]
--TypeCon,Id,Opr : 戻り値。
-備考
--引数無し(p->opr.nops==0)の時は戻り値無し。
--戻り値が数式等の場合再帰的に解析して書き出す。
case RETURN:
if (p->opr.nops > 0) {
ex(p->opr.op[0], 1, 0); 戻り値が存在するとき$1に結果が入る
}
printf("\t bal \t $0, \t fooexit \n"); bal命令で外(fooexit)に出る
break;
-実際に書き出されるアセンブラの例~
(関数内で呼ばれ、[[FDEF>#edaa26b6]]などで用意したfooexitに戻る。)
| /* 戻り値がある場合、ex();を用いて $1 に格納して渡す */
| bal $0, fooexit
***FOR [#a6d5e316]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--forループ文(定められた回数だけ反復制御を行う)の実行。
-引数はいくつ必要か?
--4個
-op[0]
--TypeOpr : カウンタの初期値処理。
-op[1]
--TypeOpr : ループの終了判定処理(定数ってコトは無いハズ)
-op[2]
--TypeOpr : ループ毎の処理(カウンタの更新など)
-op[3]
--TypeOpr : ループ内処理。
-備考
--「 for( op[0]; op[1]; op[2] ) op[3]; 」の形。
--書き出す順番が op[0], op[1], op[3], op[2] だがwhile文でやったと考えると分かりやすいか。
case FOR:
ex(p->opr.op[0], reg, pres); 初期値設定
printf("L%03d: \n", lbl1 = lbl++);
ex(p->opr.op[1], reg, pres); ループ終了判定
printf("\t bz \t $%d, \t L%03d \n", reg, lbl2 = lbl++);
ex(p->opr.op[3], reg, pres); ループ内処理
ex(p->opr.op[2], reg, pres); ループ毎の処理
printf("\t bal \t $0, \t L%03d \n", lbl1);
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
(終了判定式の値がゼロになるまで L100 以下の部分を繰り返す。)
| /* ループの初期値を求める記述を書き出す */
|L100:
| /* ループの終了判定値を求める記述(条件式)を書き出す */
| bz $[reg], L101
| /* ループ内処理を書き出す */
| /* ループ毎の処理の記述を書き出す */
| bal $0, L100
|L101:
***WHILE [#g06304e3]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--while文(条件式を前判定して反復制御を行う)の実行
-引数はいくつ必要か?
--2個
-op[0]
--TypeOpr(,Con,Id) : ループの終了判定処理。
-op[1]
--TypeOpr : ループ内処理。
-備考
--「 while( op[0] ) op[1]; 」の形。
case WHILE:
printf("L%03d: \n", lbl1 = lbl++); ループ開始位置設定
ex(p->opr.op[0], reg, pres);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl2 = lbl++); $[reg]==0で終了
ex(p->opr.op[1], reg, pres);
printf("\t bal \t $0, \t L%03d \n", lbl1); 開始位置に戻る
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
([[FOR>#a6d5e316]]より短く見えるがループ内処理でカウンタ設定やら何やらをしているハズ。)
|L100:
| /* ループの終了判定値を求める記述(条件式)を書き出す */
| bz $[reg], L101
| /* ループ内処理を書き出す */
| bal $0, L100
|L101:
***HALT [#wbdec22c]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--プロセッサの強制終了。
-引数はいくつ必要か?
--無し。
-備考
--大抵はアセンブラの最後に書かれる。
case HALT:
printf("\t hlt \n");
break;
-実際に書き出されるアセンブラの例~
(簡潔に終わりを記述。)
| hlt
***IF [#ge91e876]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--if文の実行
-引数はいくつ必要か?
--2〜3個
-op[0]
--TypeId,TypeOpr : 分岐を行うための条件
-op[1]
--TypeOpr : 条件分岐成立後の処理
-op[2]
--TypeOpr : 条件分岐不成立後の処理、省略可能。else文があるときの処理にあたる。
-備考
--「 if( op[0] ) op[1]; else op[2]; 」もしくは「if( op[0] ) op[1]; 」の形。
case IF:
ex(p->opr.op[0], reg, pres);
if (p->opr.nops > 2) { else文があるときはオペランドが3個ある
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++); bz命令で条件が成り立つか調べる
ex(p->opr.op[1], reg, pres); if文が成り立ったときの処理
printf("\t bal \t $0, \t L%03d \n", lbl2 = lbl++); if文が成立した場合、if文の終わりまでジャンプする
printf("L%03d: \n", lbl1); if文が成り立たなかったときelse文の処理をする
ex(p->opr.op[2], reg, pres); else文の処理
printf("L%03d: \n", lbl2); if文全体の終わり
}
else { else文が無いときの処理
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++); bz命令で条件が成り立つか調べる
ex(p->opr.op[1],reg, pres); if文が成り立ったときの処理
printf("L%03d: \n", lbl1); if文の終わり
}
break;
-実際に書き出されるアセンブラの例~
(書き出されるのは当然「if〜」か「if〜else〜」のどちらかだけである。)
| /* ex();で「if〜」か「if〜else〜」かの判定をする */
| [if - else - ]
|
| bz $[reg], L100
| /* if文が成り立つときの処理を書き出す */
| bal $0, L101
|L100:
| /* else文の処理を書き出す */
|L101
| [if - ]
|
| bz $[reg], L100
| /* if文が成り立つときの処理を書き出す */
|L100:
***MWT [#wbf18e11]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--配列(変数)への書き込みを行う
-引数はいくつ必要か?
--3個
-op[0]
--TypeOpr : 配列(変数)のインデックスの処理。
-op[1]
--TypeOpr : 配列(変数)に書き込む値の処理。
-op[2]
--TypeId : ストアするのがどの配列なのか(つまりどの変数か)の参照に使用。
-備考
--「 st $[(op[1])], [変数]( $[(op[0])] ) 」の形。逆に解りにくいかな。
--上の[変数]は「p->opr.op[2]->id.i+1」の値から対応する変数文字を判断する。
--……対応は[a:1]〜[z:26]でいいのかな。
case MWT:
ex(p->opr.op[0],reg, pres);
ex(p->opr.op[1],regx, 1);
iinst("st",
regx,
(p->opr.nops > 2) ? p->opr.op[2]->id.i+1 : 0,
reg );
break;
-実際に書き出されるアセンブラの例~
( $2 の値を A[$1] にストア。Aはop[2]から求める配列(変数) )
| /* ex();で書き込まれる配列のインデックス値をレジスタに($1とする) */
| /* ex();で配列に書き込まれる値をレジスタに($2とする) */
| /* iinst();でストアする配列(変数)に対応する値を求めて使う(Aとする) */
| st $2, A($1)
***'=' [#d1443c66]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--代入文(右辺から左辺へ)の実行。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId : 代入されるのがどの配列なのか(つまりどの変数か)の参照に使用。
-op[1]
--TypeOpr : 代入する値の処理。
-備考
--「 A[$0] = op[1]; 」の形。Aは代入される配列で op[0] から求める。
case '=':
ex(p->opr.op[1],reg, pres);
iinst("st", reg, p->opr.op[0]->id.i+1, 0);
break;
-実際に書き出されるアセンブラの例~
( A[$0] = op[1]; Aはop[0]から求める配列(変数) )
| /* ex();で配列に書き込まれる値をレジスタに($1とする) */
| /* iinst();で代入される配列(変数)に対応する値を求めて使う(Aとする) */
| st $1, A($0)
***UMINUS [#s1e19644]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--入力(子ノード)の値を負にして返す(単項演算子のマイナス。「a=-b+(-c)」とか)
-引数はいくつ必要か?
--1個
-op[0]
--TypeCon,Id : 入力値。コレの2の補数を取って出力にする。
-備考
--「 op[0] = ~(op[0]); 」の形か。
case UMINUS:
ex(p->opr.op[0], reg, pres);
rinst2("not", reg, reg); ビット反転し、
iinst("lda", reg, 1, reg); 1を追加する(2の補数)
break;
-実際に書き出されるアセンブラの例~
(入力値の負をとって返すだけなので何も書き出されはしない)
| /* ex();で対象の値をレジスタに($1とする) */
| /* rinst2();で not をとり $1 をビット反転 */
| /* iinst();で $1 に +1 して2の補数により負値を得る */
***MRD [#ia8f5e99]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--配列(変数)からの読み出しを行う
-引数はいくつ必要か?
--2個
-op[0]
--TypeOpr : 配列(変数)のインデックスの処理。あと出力もコレ(TypeId)。
-op[1]
--TypeId : 読み出すのがどの配列なのか(つまりどの変数か)の参照に使用。
-備考
--「 ld $[(op[0])], [変数]( $[(op[0])] ) 」の形。
case MRD:
ex(p->opr.op[0], reg, pres);
iinst("ld", reg,(p->opr.nops>1) ? p->opr.op[1]->id.i+1 : 0, reg);
break;
-実際に書き出されるアセンブラの例~
( A[$1] の値を $1 に入れる。Aはop[1]から求める配列(変数) )
| /* ex();で読み込む配列のインデックス値をレジスタに($1とする) */
| /* iinst();でロードする配列(変数)に対応する値を求めて使う(Aとする) */
| ld $1, A($1)
***ARGV [#cac12c88]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数の呼び出し時にメモリに格納してある引数をロードする。
-引数はいくつ必要か?
--無し。
-備考
--「 ld $[reg], [frametop+1]($3) 」の形。
--当然、引数有り関数(FDEFAで生成)の場合しか出てこない。
--frametop は引数のあるアドレスがスタックポインタ($3)からどれくらいかを示す。
case ARGV:
iinst("ld" ,reg, frametop+1, 3);
break;
-実際に書き出されるアセンブラの例~
(引数値をとってくるだけなので書き出されるアセンブラは無し)
| /* 読み出した値を入れるレジスタは ARGV が呼び出されたときには決まっている?($1とする) */
| /* iinst();で引数値を $1 に読み出す*/
***FUNC [#l6c73f45]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--関数の呼び出しを行う。
-引数はいくつ必要か?
--1個
-op[0]
--TypeOpr : 引数処理。ARGV が来るのか?
-備考
--あくまで既にある関数を呼び出すだけ。関数生成は[[FDEF>#edaa26b6]],[[FDEFA>#b135c1b9]]が行う。
case FUNC:
if(pres) { presが真=レジスタ使用中なのでスタックに待避
iinst("lda", 3 ,-1 ,3);
iinst("st", regx, 0, 3);
frametop += 1;
}
ex(p->opr.op[0], 1, 0);
printf("\t bal \t $2, \t foo \n"); 関数に飛ぶ。
if(reg != 1) { スタックトップレジスタが$1で無い(この場合$2かな)とき、
iinst("lda", reg, 0, 1); $1の値を$2にコピー(関数返値の受け渡し処理)
}
if(pres) { 初めに待避したならもとに戻す。
iinst("ld", regx, 0, 3);
iinst("lda", 3, 1, 3);
frametop -= 1;
}
break;
-実際に書き出されるアセンブラの例~
(スタックトップレジスタ(STReg)とは次に使われる位置にあるレジスタ、でいいんだろか)
| /* レジスタ待避 */
| /* ex();で引数値をレジスタに($1のハズ) */
| bal $2, foo
| /* 関数[foo]の終了後ここに戻る */
| /* STReg が $1 で無いなら $1 の値をそっち(ここでは$2か)にコピー */
| lda $[STReg], 0($1)
| /* レジスタ復帰 */
**Operatorが上記以外に該当する場合 [#yf406048]
以下の前処理を行い「 op[0] (何らかのオペレータ) op[1] 」を
「 reg (何らかのオペレータ) regx 」と置き換えてある。
default:
ex(p->opr.op[0], reg, pres); op[0] の処理結果を reg に
if(pres) {
iinst("lda", 3, -1, 3); もしレジスタが使用中ならスタックに待避
iinst("st", regx, 0, 3);
frametop += 1;
}
ex(p->opr.op[1], regx, 1); op[1] の処理結果を regx に
switch(p->opr.oper)
{
/* (次からの処理) */
}
if(pres) { 処理が終わったらレジスタを復帰
iinst("ld", regx, 0, 3);
iinst("lda", 3, 1, 3);
frametop -= 1;
}
***'+' [#k6deaed5]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--加算を行う。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目、変数regに相当
-op[1]
--TypeCon, TypeId : 引数2個目、変数regxに相当
-備考
--「 op[0] = op[0] + op[1]; 」の処理をする。
C言語の「a=b+1」などは代入文「=」と加算「+」の組合せで行われている?
case '+':
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = $[reg] + $[regx]; )
| add $[reg], $[reg], $[regx]
***'-' [#a1264cfb]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--減算を行う。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目、変数regに相当
-op[1]
--TypeCon, TypeId : 引数2個目、変数regxに相当
-備考
--減算命令はnot、lda、add命令を組み合わせて行われる。regxの2の補数を生成してregに加算する。
case '-':
rinst2("not", regx, regx);
iinst("lda", regx, 1, regx);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = $[reg] + ( - $[regx] ); 負値を加算して減算とする)
| not $[regx], $[regx]
| lda $[reg], 1($[regx])
| add $[reg], $[reg], $[regx]
***'<' [#y202a279]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--値の比較を行う。a<b?の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目。regに相当し、演算子の左辺
-op[1]
--TypeCon, TypeId : 引数2個目。regxに相当し、演算子の右辺
-備考
--slt命令を用いて実行される。op[0] < op[1] のとき 1 を返し、そうでない場合は 0 を返す。
case '<':
rinst("slt", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] < $[regx] ) ? 1 : 0; )
| slt $[reg], $[reg], $[regx]
***'>' [#q466fe7d]
-記述者名
--GroupD(五十嵐)
-何をするオペレータか?
--値の比較を行う。a>b?の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeCon, TypeId : 引数1個目。regに相当し、演算子の左辺
-op[1]
--TypeCon, TypeId : 引数2個目。regxに相当し、演算子の右辺
-備考
--'<' の場合と逆なので、regとregxを入れ換えて実行する。
case '>':
rinst("slt", reg, regx, reg);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[regx] < $[reg] ) ? 1 : 0; )
| slt $[reg], $[regx], $[reg]
***GE[>=] [#xef030ae]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a>=b?( a が b 以上か) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a>=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a>=b? の b )
-備考
--[['<'>#y202a279]]の結果の0と1を入れ替えることで判定している。
--下の記述にはバグがあり正しい判定が出来ていないようです。
case GE:
rinst("slt", reg, reg, regx);
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
iinst("lda", regx, 1, 0);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( 保留1 )
| (o^-')b!
***LE[<=] [#udc0252d]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a<=b? ( a が b 以下か)の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a<=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a<=b? の b )
-備考
--[['>'>#q466fe7d]]の結果の0と1を入れ替えることで判定している。
--上の GE 同様にバグがあり正しい判定が出来ていないようです。
case LE:
rinst("slt", reg, regx, reg);
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
iinst("lda", regx, 1, 0);
rinst("add", reg, reg, regx);
break;
-実際に書き出されるアセンブラの例~
( 保留2 )
| (o^-')b!
***NE[!=] [#s962586d]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a!=b?( a が b と等しくないか) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a!=b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a!=b? の b )
-備考
--「 regx - reg != 0 ? 1 : 0 」として判定 ( regx - reg が 0 で無いなら 1 を、そうでないなら 0 を返し条件分岐 )
case NE:
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
rinst("add", reg, reg, regx);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++);
iinst("lda", reg, 1, 0); reg != regx のとき、reg = 1
printf("L%03d: \n", lbl1); reg != regx で無いときは既に reg = 0 なのでそのまま
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] != $[regx] ) ? 1 : 0; )
| not $[reg], $[reg]
| lda $[reg], 1($[reg])
| add $[reg], $[reg], $[regx]
| bz $[reg], L100
| lda $[reg], 1($0)
|L100:
***EQ[==] [#l69a71b6]
-記述者名
--GroupB(吉田)
-何をするオペレータか?
--与えられた値の比較を行う。a==b?( a が b に等しいか) の判断をする。
-引数はいくつ必要か?
--2個
-op[0]
--TypeId,Con : reg に相当し、式の左辺に来る( a==b? の a )
-op[1]
--TypeId,Con : regx に相当し、式の右辺に来る( a==b? の b )
-備考
--「 regx - reg == 0 ? 1 : 0 」として判定 ( regx - reg が 0 なら 1 を、そうでないなら 0 を返し条件分岐 )
case EQ:
rinst2("not", reg, reg);
iinst("lda", reg, 1, reg);
rinst("add", reg, reg, regx);
printf("\t bz \t $%d, \t L%03d \n", reg, lbl1 = lbl++);
iinst("lda", reg, 0, 0); reg == regx で無いとき、reg = 0
printf("\t bal \t $0, \t L%03d \n", lbl2 = lbl++);
printf("L%03d: \n", lbl1);
iinst("lda", reg, 1, 0); reg == regx のとき、reg = 1
printf("L%03d: \n", lbl2);
break;
-実際に書き出されるアセンブラの例~
( $[reg] = ( $[reg] == $[regx] ) ? 1 : 0; )
| not $[reg], $[reg]
| lda $[reg], 1($[reg])
| add $[reg], $[reg], $[regx]
| bz $[reg], L100
| lda $[reg], 0($0) reg=0
| bal $0, L101
|L100:
| lda $[reg], 1($0) reg=1
|L101:
(o^-')b!
ページ名: