COINS FORTRAN Frontend

Fortran特有の機能に対する考慮


 coinsの目標は、いろんな言語に共通に使えるHIRを設計するということだから、 Fortranの機能で現在のHIRで素直に表現できないものは、素直に表現できるよう にHIRを変更すべきであるが、それがあまりにFortran特有の機能であって、現在 の他の普通のプログラム言語では考えられないものであるのならば、特有の表現 を通常の表現に変換して済ませるほうがよい。

 また、HIRは一つの言語であり、その言語のプログラムに対して、種々の最適 化やLIRへの変換が実現されているのであるから、HIRに新たな機能を追加すると きは、それをあまり乱さない考慮が必要である。

 Cについては、現在はソースプログラムをまずHIR-Cに変換し、次に、HIR( HIR-Cに対比するときはHIR-Baseと呼んでいる)に変換している。C特有の機能で、 HIR上での処理が複雑になるようなものは変換してしまって、HIRを単純で標準的 な形に保つためである。そこでFortranでも、最初はHIR-Fortranと呼ぶようなも のを考えようとした。しかし、HIR-FortranでFortranのプログラムを出来るだけ 忠実に表現しようとすれば、それはもはやHIRとは言えず、Fortran固有の表現に なってしまう。次に、現在のHIRで直接表現できないようなものはいったん HIR-Fortranとして表現しておいて、それからHIR-Baseに落とすことを考えた。 しかし、それは実現上の手段に過ぎず、HIR-Fortranとして独立した意味を持た ない。そこで、以下ではHIR-Fortranという言葉は使わないことにした。

 しかし、Fortranはきれいに設計された言語ではないので、単純にシーケンシ ャルにHIRに変換することは出来ない。そこで、まず抽象構文木に変換すること にした。ただし、一つの根からなる木ではなく、宣言情報やENTRY文などは別の 木にして、全体としてはそれらの林になるものとした。

 Fortranには、77、90、95があるが、90以降は仕様が大変大きくて、実現は困 難であり、実際にもあまり使われていないようなので、ここではFortran 77を対 象として考えることする。

 FortranでCと違っている、あるいは現在のHIRに直接入っていない主な点と、 coinsを使って実現する場合の案を以下に記す。

 基本的には、現在のHIRに変更は加えず、それに合わせたHIR木を生成すること にした。Fortran特有の機能には標準的な言語の機能と考えられないものが多い からである。COMPLEX型は標準的な機能としてHIRに追加することも考えられるが、 コードの最適化のことを考慮すると、COMPLEX型としての表現よりも、それを実 数型に分解した表現の方が最適化の機会が多くなる。したがって、COMPLEX型を HIRに追加することはせず、それは、実数部と虚数部を持ったstruct型として表 現することにした。

 HIRは当初から、少なくともCのコンパイラが作れること、という目標で仕様が 作られているから、Cで表現できるものはHIRで表現できる。そこで、以下では、 Fortran特有の機能をどのようなCの表現に変換するかの案を示す。実際にはその ようなC表現そのものを作るのでなく、それに相当するHIR表現を作る。その変換 の途中で、都合によりHIRの持つクラスのサブクラスとして、Fortran特有の機能 を表現するためのサブクラスを一時的に作ることはあるが、でき上がったHIR表 現にはそのようなサブクラスのオブジェクトは入っていない。

 なお字句解析のプログラムlexは手書きで、構文解析のプログラムはBerkeley yaccのjava版であるjayを使うことを考えている。

 以下で参考にしているf2cの内容はベル研のレポート

Computing Science Technical Report No. 149
A Fortran-to-C Converter
S. I. Feldman, D. M. Gay, M. W. Maimone, N. L. Schryer
Last updated March 22, 1995. Originally issued May 16, 1990.

による。

以下に、Fortran特有の機能とそれに対する対策を述べる。

(1) 字句解析が難しい

 72欄のカード形式、スペースの有無が自由、キーワードが無い、変数は宣言し なくてもいい、関数呼出しと配列要素が同じ形、など字句解析を難しくする仕様 がある。

 これについては、手書きの字句解析プログラムで、構文解析しやすいトークン 列に変換するようにした。OMNIのFortranフロントでもそのようにしており、そ れを参考にする。

 字句解析では以下の処理をする。

(2)ENTRY 文

サブプログラムが入口を複数もつ。例えば、次のプログラム

    SUBROUTINE SUB1(X, Y)
     ...
  10 ENTRY SUB2(X, Z)
     ...
    END

 は

    SUBROUTINE SUB1(X, Y)
    CALL SUB12(1, X, Y, dummy)
    END
  
    SUBROUTINE SUB2(X, Z)
    CALL SUB12(2, X, dummy, Z)
    END
  
    SUBROUTINE SUB1_(J_, X, Y, Z)
    IF ( J_ .EQ. 2 ) GO TO 10
     ...
  10
     ...
    END

 に変換する。実際には次のようなものに変換することにした。

    void SUB1(float *X, float *Y){
      int i_ = 1; float dummy_;
      SUB12(&i_, X, Y, &dummy_);
    }

    void SUB2(float *X, float *Z){
      int i_ = 2; float dummy_;
      SUB12(&i_, X, &dummy_, Z);
    }

    void SUB1_(int *J_, float *X, float *Y, float *Z){
      switch(*J_) {
      case 1: goto L_SUB1;
      case 2: goto L_SUB2;
   L_SUB1:
      ...
   L_SUB2:
      ...
    }

 FUNCTIONの場合はもう少し複雑になる。

    FUNCTION f(r)
    f = r
    ENTRY g(s)
    g = f + s
    END

 は

    float f(float *r){
     float _f, dummy_;
     int i_ = 1;
     _f = f_(&i_, r, &dummy_);
     return _f;
    }

    float g(float *s){
     float _g, dummy_;
     int i_ = 2;
     _g = f_(&i_, &dummy_, s);
     return _g;
    }

    float f_(int *i_, float *r, float *s){
     float _f_;
     switch(*i_){
     case 1: goto L_f;
     case 2: goto L_g;
     }
    L_f: _f_ = *r;
    L_g: _f_ = _f_ + *s;
     return _f_;
    }

に変換する。実は、Fortranの規格では、fとgの型が違ってもよいことになって いるが、それを実現するのは簡単ではない。f2cでも考えていないようなので (確認はしていません)、それは考えないことにする。実現するとしたら、次の ようにすることが考えられる。

    FUNCTION f(r)
    INTEGER f;
    f = r
    ENTRY g(s)
    g = f + s
    END

 は

    int f(float *r){
     int _f, dummy_;
     int i_ = 1;
     _f = (int)f_(&i_, r, &dummy_);
     return _f;
    }

    float g(float *s){
     float _g, dummy_;
     int i_ = 2;
     _g = (float)f_(&i_, &dummy_, s);
     return _g;
    }

    union{int, float} f_(int *i_, float *r, float *s){
     float float_; int int_;
     switch(*i_){
     case 1: goto L_f;
     case 2: goto L_g;
     }
    L_f: int_ = *r;
    L_g: float_ = int_ + *s;
     switch(*i){
     case 1: return int_;
     case 2: return float_;
     }
    }

 に変換する。

(3)CALL 文のalternate return specifier

 引数で指定された文番号のところにreturnすることが出来る。たとえば、次の ようなプログラムが書ける。

  CALL SUB(A, *100, *200)     SUBROUTINE SUB(X, *, *)
                   RETURN 2 
                   (ステートメント200にリターン)

 これは、次のようなプログラムに変換する。

  GO TO (100, 200) ,SUB(A)     INTEGER FUNCTION SUB(X)
                   SUB = 2
                   RETURN

 Cの表現では、次のようになる。

  switch(SUB(A)){        int SUB(X) {
  case 1: goto L_100;        return 2;
  case 2: goto L_200; 
  }                }

(4)statement function

 1つの文で関数を定義することが出来る。それを1つのサブプログラムとする ことも出来るが、ここでは、文関数呼出しはインライン展開することにする。

(5)ASSIGN文

   ASSIGN s TO i   sは文番号、iは整数変数

 は

   i = s 

 に変換する。これは、通常の整数の代入文である。次の(6)と合わせて機能 する。

(6)assigned GO TO 文

   GO TO i [ (s [,s]...)]    sは文番号、iは整数変数

 は

   switch(i){
   case s: goto L_s   (最初のsは整数定数。後ろのL_sはラベル)
   . . .
   }

 に変換する。

(7)computed GO TO 文

   GO TO (s [,s]...) i    sは文番号、iは整数変数

 は

   switch(i){
   case 1: goto L_s1
   case 2: goto L_s2
   . . .
   }

 に変換する。

(8)算術IF文

   IF (e) s1, s2, s3

   if (e < 0) goto L_s1
   if (e == 0) goto L_s2
   else goto L_s3

 に変換する。

(9)COMPLEX型

 複素数型はstruct型として表現することにする。複素数型に関する演算は実数 型の演算に分解する。たとえば、

   COMPLEX c, d
   c = c + d

 は

   struct complex { float re; float im } c, d;
   c.re = c.re+ d.re;
   c.im = c.im + d.im;

 に変換する。

 HIR へ変換中は ComplexExp というクラスで表現される。これは、Fortran フ ロントエンドで勝手に定義したクラスなので、HIR化したときにはこの表現が存 在しないようにしなければならない。

(10)expressionの評価

 ある部分を評価しなくてもexpressionの値が決まる場合、その部分を評価しな くてもよい。評価の順序は、mathematically equivalent expressionに変えて評 価してよい。ただし、カッコで囲まれたexpressionは一つのentityとして扱わね ばならない。

 HIRには、Fortranのこの規則を考慮してカッコで囲まれたexpressionであるこ とを表すencloseという演算子が用意されているから、それを使うことにする。

(11)**演算子

 べき乗演算子をHIRに追加することはしない。べき乗演算子は、一般には関数 呼出しに変換する。いくつかの定数整数べき乗は掛け算にインライン展開するこ とにする。

(12)COMMON

 COMMONブロックは RegionType で表現する。ReginType は coins の HIR で定 義されている型であり,アセンブラ記述として ".comm" ディレクティブとして 定義される.

[File1.f] ==============================
       subroutine Sub1()
       common /BlockA/, x,y,z
       ...
       end

[File2.f] ==============================
       subroutine Sub1()
       real ary(10)
       common /BlockA/, ary
       ...
       end

このとき,x, y, z と r(1), r(2), r(3) は同じ記憶領域を共有する.異なるフ ァイルをまたいで同じメモリ空間を共有する方法は C言語でそれに対応するもの がないので,COMMONブロックをサポートするために RegionType を用意した.

(13)EQIUVALENCE

 EQIUVALENCEはunionで表現する。

(14)多次元配列のメモリ配置

 たとえば、

   DIMENSION k(10, 30)
   ...k(3, 7)

 は

   int k[30][10];
   ... k[6][2]

 のSymとHIRに相当するものに変換する。

(15)SAVE文

   SAVE [a [,a]...]

 サブルーチンからリターンしても、ここにリストした変数の値は保持される。 そのために、SAVE文にある変数をstaticとする。

 ただし、基本的に Fortran の変数は C の static 変数で問題ないため、すべ て static 変数とする。

(16)引数について

 call by addressであるから(call by valueも許す処理系はあるが、規格( http://www.fortran.com/fortran/にある)には入っていない)、引数はポイン タ型に変換する。実引数が変数以外の式である場合はダミーの変数への代入文を 作り、その変数のアドレスを実引数とする。

(17)入出力文とFORMAT文

 open、close、backspace、rewindはライブラリ呼出しの形とし、FORMATは文字 列の引数として入出力のライブラリ(f2cのlibF77とlibI77)を呼び出す形とす る。入出力のリストとしてのループはループ文に展開する。

(18)配列の宣言

 配列の宣言が、どこからでもいい a(-10:10,-1:10) b(100:105)。サブプログ ラムでは仮引数の配列としてadjustable array 、assumed-size array(最後の 上限が*)などが使える。

 Fortranでの配列の宣言の情報(仮引数などを含んだ式による上下限の指定な ど)をすべて扱えるようにHIRのVector型を拡張する。

(19)文字列と部分文字列

 文字列の宣言でcharacter c*500、d*100という書き方をすると、cは長さ500の 文字列型である。文字列型を関数の戻り型とすることもできる。

 代入文

   c(1:200)=c(2:201) 

 で部分文字列の代入が出来る。

 Fortranの文字列は長さを持っているため、その情報を持っておかなければな らない。文字列は char の VectorType として表現されるため、その VectorType の長さが文字列の長さとして登録することができる。しかし、関数 引数として Fortran文字列を渡した場合、型として長さが渡せない場合がある。 そのため、関数引数に文字列があった場合、その関数定義に文字列長を表現する stringname_length という変数を付ける。これは f2c を参考にしている。

 文字列長の問題があったので、文字列自体の表現と長さの表現を同時に持つ FortranCharacterExp クラスを用意いた。ComplexExp 同様、これも HIR化した ときにはすべて文字列の表現(たとえば、decayした配列変数)と長さの表現の みになっていなければならない。

(20)data文やblock data文

 部分文字列やimplied-DOリストにも初期値設定できる。初期値としてn*cの形 でn個の定数を表現できる。

(21)implicit 宣言、ijklmn規則

 宣言なしでも変数の型が決まる。

(22)PARAMETER文

 定数名の宣言。

 配列のサイズに Parameter文で指定した定数が利用できるため、コンパイル時 にある程度計算可能にした。Complex も実数部だけ計算可能にした。

(23)PAUSE文、STOP文

 ライブラリ呼出しにする。PAUSEのデフォルトは何もしないライブラリ。STOP はプログラム終了。libf2c の関数に委譲する。

(24)記憶単位

(25)文字列定数

 Cでは、"abc"は長さ4の文字配列。Fortranでは'abc'は長さ3。

(26)入出力

 入出力関係はすべて libf2c のライブラリ関数呼び出しコードを生成するよう にする。そのため、COINS でFortran プログラムをコンパイル、リンクする場合 は libf2c も同時にリンクする必要がある。

COINS Project