なお、long double型はdouble型と同じサイズである。 また、前処理とライブラリはgccのものが使われることを仮定している。
以下では、ソースプログラムに記入するプラグマをソースプラグマ、 コンパイラが自動生成してHIRに入れるプラグマをHIR-pragma、 LIRに入れるプラグマをLIR-pragmaという。
#pragma item1 item2 ...
という形の独立の行として表現する。COINSでは、ソースプラグマは次の形の文として扱う。
プラグマ文 → "#pragma" kindName itemSeq
kindName → identifier // 種別を表す識別子。例:prof(プロファイル)
itemSeq → pragmaItem itemSeq | ε
pragmaItem → identifier | integerConstant | stringConstant
| ( itemSeq )
pragmaItemの識別子として、プログラムに現れる変数名、副プログラム名、ラベルを書く
と、それらは単なる識別子ではなく、変数名、副プログラム名、ラベルとしての識別子と
して扱う。
注: ループ並列化やSMP並列化で自動生成するOpenMP向けのプラグマは、OpenMPとして仕様 が標準化されているので、それに従って作る。
前記の形のソースプラグマから作られるHIR-pragmaの一般形は次の通りである。
Stmt -> ....
InfStmt
InfStmt -> ...
(IR.OP_INF kindName IrListOfPragmaItems )
// オペレータがIR.OP_INFの文
kindName -> String // プラグマの種類を表す文字列
IrListOfPragmaItems -> (listCode list_of_pragmaItems)
pragmaItem ->
Sym // Var, Subp, Label, IntConst, StringConstのノードに限定
| identifier // Symとは限らない識別子
| IrListOfPragmaItems // pragmaItemを要素とするIrList
アクセス方法:
InfStmtの情報はInfStmtのメソッドで次ぎのようにして参照する。ここでinfStmtは InfStmtのインスタンスを表すとする。
String getInfKind(): kindNameとりだし
IrList getInfList(kindName): IrListOfPragmaItemsとりだし
infStmt.getInfList(infStmt.getInfKind()).get(i):
IrListOfPragmaItemsの要素iのとりだし
pragmaItemの識別子として、プログラムの記号表に現れる記号を書いた場合、それは
InfStmtではStringではなくSymとして表されている。それをStringとして扱うかSym
として扱うかは、個々のプラグマの解釈時に判定し、SymをStringに変換するときはSym
のgetName()メソッドを使う。
(注:他のHIRの文とくらべてちょっと変則的なところがあるが、既存の(プラグマでな い)Inf情報との両立性と、プラグマ情報としてはHIRオブジェクトでないSymやString を指定可能とするためにそうなっている。)
(INFO LINE 行番号)
(INFO (DEFLABEL ラベル名) (LINE 行番号))
(INFO (JUMPx ラベル名))
(INFO BEGIN x) (INFO END x)x は PROLOGUE, EPILOGUE, ASM, CALL のいずれか。
(MEM I32 (FRAME I32 "a.1") &id ("a.1" 0))
(MEM I32 (FRAME I32 "a.1") &id 1)
#pragma DUMMY <inf DUMMY ( )> #pragma FUNC fn1 <inf FUNC ( fn1 )>
#pragma DUMMY (INFO DUMMY) #pragma FUNC fn1 (INFO FUNC fn1)
COINSのCコンパイラでasm文をサポートする。asm文は、アセンブラ言語の 命令列をCプログラムの途中に挿入できるようにするもので、 Cコンパイラが生成する目的コードの中に、asm文で書いた命令列が そのまま挿入される。
asm文の構文は以下のようなものである。
asm("#param descriptor-list\n"
"#clobber destroyed registers...\n"
"instruction 1\n"
...
"instruction n\n",
input arguments(any expression)...);
'#param'で始まる行には、パラメタに関する情報を順番に記述する。
パラメタは、レジスタにセットされるもの、static変数のアドレスに分けられる。
レジスタにセットされるパラメタは、入力だけか、出力だけ('w'を付ける)か、入出力('m'を付ける)か、 どんなレジスタにセットされるかを記述する。レジスタは、固定されたレジスタだけでなく、 レジスタクラスの名前を書くこともでき、コンパイラに適切なレジスタを選択させることができる。
'#clobber'には、破壊されるレジスタを記述する。これは固定されたレジスタなので、 作業用レジスタをコンパイラに選択させたい場合には使えない。その場合は、ダミーの変数 をパラメタに書くことになる。
パラメタは、命令部では%1, %2, ...のように引用する。%を出力したいときは%%と二つ続けて書く。
各部の構文は以下の通り。
descriptor-list:
descriptor
descriptor-list , descriptor
descriptor:
%register
%register-class-name /* input parameter, actual parameter is rvalue */
w%register
w%register-class-name /* output parameter, actual parameter is lvalue */
m%register
m%register-class-name /* input and output parameter, actual parameter is lvalue */
s /* actual parameter is static address */
register:
レジスタ名
register-class-name:
TMD中で定義されたレジスタクラス名(*reg-と最後の*を除く)
例1:
簡単な例であるが、z = x + y; と同じことをasmで行うには 次のように書けばよい。
asm("#param %I32,%I32,w%I32\n"
" mov %1,%3\n"
" add %2,%3\n",
x, y, z);
例2:
x++; と同じことをasmで行うには次のように書けばよい。
asm("#param m%I32\n"
" add 1,%1\n",
x);
* AsmStmt -> // * ( asmCode attr // Asm statement. * StringConstNode_ @ //String constant representing formal parameters * // and sequence of assembly language instructions. * HirList @ ) // List of l-value expressions (variable nodes, * // pointer expressions, etc.) and arithmetic * // expressions representing actual parameters.のように、
なお、ASM文は文として扱い、算術演算や代入操作の オペランドとしては扱わない。
例:int x, y, z;
int main()
{
int a, b, c;
a = 1;
b = a + 2;
z = -1;
asm("#param %I32, %I32, w%I32\n"
"mov %1,%3\n"
"add %2,%3\n",
a, b+1, z);
printf("a=%d b=%d z=%d \n", a, b, z);
return 0;
}
というソースに対するHIRは
(prog 1
<null 0 void>
<nullNode 2>
(subpDef 3 void
<subp 4 <SUBP <( )> false false int> main>
<null 0 void>
(labeldSt 5 void
(list 6
<labelDef 7 _lab1>)
(block 8 void line 2
(assign 9 int line 5
<var 10 int a>
<const 11 int 1>)
(assign 12 int line 6
<var 13 int b>
(add 14 int
<var 15 int a>
<const 16 int 2>))
(assign 17 int line 7
<var 18 int z>
(neg 19 int
<const 20 int 1>))
(asm 21 void line 8
<const 22 <VECT 46 0 char> "#param %I32, %I32, w%I32\nmov %1,%3\nadd %2,%3\n">
(list 23
<var 24 int a>
(add 25 int
<var 26 int b>
<const 27 int 1>)
<var 28 int z>))
(expStmt 29 int line 12
(call 30 int
(addr 31 <PTR <SUBP <( )> false true int>>
<subp 32 <SUBP <( )> false true int> printf>)
(list 33
(decay 34 <PTR char>
<const 35 <VECT 17 0 char> "a=%d b=%d z=%d n">)
<var 36 int a>
<var 37 int b>
<var 38 int z>)))
(return 39 int line 13
<const 40 int 0>)))))
となる。
asm("#param %I32, m%I32\n"
"add %1,%2\n",
i, j); /* j = j + i; */
に対するHIRは
(asm 17 void line 11
<const 18 <VECT 30 0 char> "#param %I32, m%I32\nadd %1,%2\n">
(list 19
<var 20 int i>
<var 21 int j>))
のようになる。
ASM文において、#paramでwというアクセス表示のついた実引数は値が設定され、 mというアクセス表示のついた実引数は値が参照されたあと設定され、 その他の実引数は値が参照されるものとして、データフロー解析される。
LIRでのasm文は、ASM命令という専用の命令で表現される。その構文は
(ASM <body-string> <in-list> <out-list> <inout-list>
&argtype <arg-list>
&clobber <clobber-list>)
である。<body-string> は、元のbody string (ソースのasm文の本体の中の文字列で表現されている部分)の内容から#paramと#clobber を除き、引数リストの順番をこの構文に合うように書き換えたquoted string (「"」で囲まれた文字列)である。
<in-list>, <out-list>, <inout-list> は、 REG式、STATIC式のいずれかのリストである。 STATIC式は#param で s と指定された引数であり、 それ以外の式は、必ずREG式でなくてはいけない。
inパラメタが複雑な式であれば、REG変数を新しく作り、式の値を事前にその変数に代入し 、inパラメタはREG式に置きかえられる。outパラメタも同様にREG変数を導入し、 ASMの直後にその変数から元のlvalueに代入するコードが追加される。
ただし、いきなりこの制限を満たすことは大変なので、 hir2lirでの変換では任意のL式を許す。
<arg-list> は、#paramの内容を引数リストの順に並べたもの(元の順とは異なる)。
例1:
asm("#param w%I32,%I32,%I32\n"
"#clobber %eax,%ebx\n" /* 実際には壊れないが、例として */
" mov %2,%1\n"
" add %3,%1\n",
z, x+1, y);
は、以下のようになる。
(ASM " mov %1,%3\n add %2,%3\n" ;番号が変わっているのに注意
((ADD I32 (MEM I32 (FRAME I32 "x") (INTCONST I32 1)) (MEM I32 (FRAME I32 "y"))) ;in-list
((MEM I32 (FRAME I32 "z"))) ;out-list
() ;inout-list
&argtype (%I32 %I32 w%I32) ;wはあってもなくてもいい sは必須
&clobber (%eax %ebx)) ;clobber
このフォーマットはさらに変換され、最終的に以下のようになる。
(SET I32 (REG I32 ".t1%") (ADD I32 (REG I32 "x%") (INTCONST I32 1)))
(PARALLEL
(ASM " mov %1,%3\n add %2,%3\n" ;body
((REG I32 ".t1%") (REG I32 "y%")) ;in-list
((REG I32 "z%")) ;out-list
() ) ;inout-list
(CLOBBER (REG I32 "%eax") (REG I32 "%ebx")))
このフォーマットでは、レジスタクラスの指定はASM命令からは消え、以下のようにSYMBOL中の変数の宣言部に移される。
("y%" REG I32 4 0 ®set *reg-I32*)
例2:
コンパイル時定数をアセンブラ部分で引用する場合(s型のパラメタ)。
int vec[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int foo(int i) {
int autov = i;
int x;
/* x = vec[i] + autov; */
asm("#param %I32,s,%I32,w%I32\n"
" movl %2(,%3,4),%4\n"
" addl (%1),%4\n",
&autov, vec, i, x);
return x;
}
このasm文は、hir2lir で変換されて以下のようなLIRになる。
(ASM " mov %2(,%3,4),%4\n add %1(%%ebp),%4\n"
((FRAME I32 "autov") (STATIC I32 "vec") (REG I32 "i%"))
((REG I32 "x%"))
()
&argtype (a s %I32 w%I32))
(ASM " movl %2(,%3,4),%4\n addl (%1),%4\n"
((FRAME I32 "autov.5") (STATIC I32 "vec") (MEM I32 (FRAME I32 "i.4")))
((MEM I32 (FRAME I32 "x.6"))) ()
&argtype (%I32 s %I32 %I32))
コンパイル後:
;i:%ebx, x:%eax, &autov:%edx leal -4(%ebp),%edx movl vec(,%ecx,4),%eax addl (%edx),%eax
java coins.driver.Driver オプション指定 ソースファイル名「オプション指定」についてはドライバの 「2.2. コンパイラ・ドライバの使い方」 を参照されたい。
「ソースファイル名」は".c"で終わるものでなければならない。
それらが使えず、代わりにものがある場合、たとえば、 アセンブラとして"gas"でなく"as"が使える場合は
-coins:assembler=asというオプション指定をすればよい。より詳しくは ドライバの 「COINSオプション」の「その他」の中の
-coins:preprocessor/assembler/linkerの記述を参照されたい。
printfなどの実行時ライブラリや、その関数定義などのあるヘッダファイル はgccのものを使うことを仮定しているので、それらが必要である。
ただし、引数の個数が不定な関数を実現するためのマクロについては、 x86, sparc, arm, x86_64用のものを
lang/c/include/stdarg.hとして用意してある(armとx86_64用はcoins-1.4.3以降)。
なお、最近のgccでは、ヘッダファイルの中でCOINSではサポートしていない 拡張機能を使っていて、COINSでコンパイルできない場合がある。 その場合は、一つの方法として、より簡単なヘッダファイルを使ってみるとよい。 その例が
lang/c/include/samples/assert.h, string.hとして用意してある(coins-1.4.3以降)。 もう一つの方法は、コンパイルコマンドの行に
-D_POSIX_C_SOURCE=1を入れてみることである。これでコンパイルできるようになる場合がある。
C言語には演算と代入の機能を併せ持つ複合代入演算子などがあるので、そのようなC言語の演算子を そのまま表現できる機能をHIRに追加したHIR-Cを定め、HIR-Cを経由してHIRに変換する方式とした。 また、HIRを作成する際に無駄を省くため、簡単な効率化機能も組み込んだ。したがって、 HIRへの変換過程は次のようになっている。
Cの前処理系にはgccの前処理系cppを利用する。また、実行時ライブラリもgccのものを利用することにし、 インタフェースをgccに合わせている。
-coins:hirOpt=fromcというオプション指定をすると、HIR生成時に次のような最適化を行う。
*&v --> v
-(-v) --> v、 他
v + 0 --> v v - 0 --> v v * 0 --> 0 v * 1 --> v, 他
!(a<b) --> a>=b
定数A 比較演算子 定数B --> true または false, 他
e1 && e2 でe1がtrue --> e1の副作用,e2
r = (a=1)>0 ? 1 : 0; --> r = (a=1) , 1; 他
到達不能の式文/return/goto --> 削除 ジャンプ距離が0のgoto --> 削除 参照されないラベル --> 削除 連続するラベル --> ラベルの統合 空のブロック --> 削除 式文 --> 副作用だけ残して他の部分を削除
if(true) THEN部; else ELSE部; --> { THEN部; goto LABEL_END; ELSE部; LABEL_END:; }, 他
while(false) BODY部;
--> { goto LABEL_BREAK; BODY部; LABEL_BREAK:; }
for(INIT部;false;STEP部) BODY部;
--> { INIT部; goto LABEL_BREAK; BODY部; STEP部; LABEL_BREAK:; }
do BODY部; while(false); --> BODY部;
他
詳しくは、COINSのソース・アーカイブのdoc-en/CfrontOpt.txtを参照されたい。
以下のものはFortran 77の規格の仕様には入っていないが、ベンチマークプログラム などでもよく使われているので、サポートしている。
java coins.driver.F77Driver オプション指定 ソースファイル名「オプション指定」についてはドライバの 「2.2 コンパイラ・ドライバの使い方」 を参照されたい。
「ソースファイル名」は".f"で終わるものでなければならない。ソースファイル名は "foo.f bar.f ..."のようにいくつか並べても良い。
なお、
-coins:printhirというオプションを指定すると、FortranプログラムをHIRに変換した後で、HIRの木と シンボルテーブルを出力する。
これは、coins.driver.F77Driverの中に、次のような記述があるからである。 このようにして、任意のオプションを追加することが出来る。
if (io.getCompileSpecification().getCoinsOptions().isSet("printhir")) {
hirRoot.programRoot.print(0);
io.printOut.print("¥n");
symRoot.symTable.printSymTableAllDetail(symRoot.symTableRoot);
symRoot.symTableConst.printSymTableDetail();
io.printOut.print("¥n");
}
その他に、実行時ライブラリとして"libf2c"が必要である。このライブラリは http://www.netlib.org/f2c/libf2c.zip をダウンロードして、その中にあるREADMEにしたがってインストールすれば良い。
UNIX系のOSの場合、解凍したlibf2cのディレクトリで
% cp makefile.u Makefile % makeを実行して得られる"libf2c.a"を適当なところにおいて、その置き場所を Fortran Driverに認識させれば良い。そのためには
libf2cLocation /directory/path/where/libf2c.a/existsのように、"libf2c.a"の置き場所を指定する行を含んだファイルを"settings"という名前で ドライバの 「2.3.6 ライブラリ・ディレクトリ」に書かれているところに置けば良い。
ライブラリ・ディレクトリは、特に指定しない場合は、
~/coins/
となっているので、そこに"settings"ファイルを置けば良い。
なお、cygwinの場合は、上記の手続きに少し修正が必要である。まず、上記のMakefileの中にある"a.out" を"a.exe"に変更する必要がある(2ヶ所)。また、cygwinでは、上記のライブラリ・ディレクトリを認識せず、 現在作業中のディレクトリを探すようであるので、"settings"ファイルは作業中のディレクトリに置いておく必要がある。
"libf2c.a"を使わなくても,linkerとしてgccの代わりにg77を使えばFortranプログラムのコンパイルと実行が可能になる. その場合は,上記の"settings"ファイルを取り除き,coins.driver.F77DriverのsetDefaultLinkerOptionsメソッドの 中の
options.add("-lf2c");
を取り除く必要がある.コンパイルするコマンドは,たとえば次のようにすればよい.
$ java -classpath coinsディレクトリ/classes coins.driver.F77Driver \
-coins:assembler=as,linker=g77,target=x86 reidai1.f
Fortran言語には、ENTRY文、assigned GOTO文など、最近の言語にはない ものとしてHIRには用意されてない機能がいくつかある。それらについては プログラム変換をしている。おおまかには、それと等値な働きをするCプログラムを 考えて、それのHIRに相当するものに変換している。
HIRへの変換過程は次のようになっている。
これらのモジュールはcoins.ffrontパッケージにある。そのディレクトリ内にある
詳しくは Fortran特有の機能に対する考慮、 Fortranコンパイラの設計、 を参照されたい。
PL0コンパイラのフロントエンドのJavaCCによる記述は http://coins-compiler.sourceforge.jp/advanceduse/ll/coinsCompilerPL0.html を参照されたい。
Simpleコンパイラのフロントエンドのjayによる記述は http://coins-compiler.sourceforge.jp/advanceduse/simple/coinsCompiler.html を参照されたい。