Sun Studio 12: Fortran プログラミングガイド

7.6.2 別名参照と -xalias オプション

別名参照は、同じ記憶領域アドレスが複数の名前で参照されるときに発生します。通常、これは、ポインタを使用している場合や、副プログラムへの実引数が、それら実引数間で、あるいは副プログラム内の COMMON 変数間でオーバーラップしている場合に起こります。たとえば、引数 X と Z は同じ記憶領域の位置を参照します。B と H も同様です。


  COMMON /INS/B(100)
  REAL S(100), T(100)
  ...
  CALL SUB(S,T,S,B,100)
  ...
  SUBROUTINE SUB(X,Y,Z,H,N)
  REAL X(N),Y(N),Z(N),H(N)
  COMMON /INS/B(100)
  ...

古い Fortran プログラムの多くは、このような別名での参照を、当時の Fortran 言語では利用できなかった、ある種の動的なメモリー管理の手段として利用していました。

別名での参照は、すべての移植可能なコードの中で避けるべきです。一部のプラットフォーム上では、-O2 よりも高い最適化レベルを使用してコンパイルすると、予測できない結果になることがあります。

f95 コンパイラでは、規格に準拠したプログラムのコンパイルを前提としています。Fortran の規格に厳密に準拠していないプログラムをコンパイルすると、コンパイラによる解析や最適化に支障を来す状況が生じることがあります。その状況によっては、誤った結果が得られる可能性があります。

たとえば、配列の境界を越えて添字を付ける、ポインタを使用する、または、大域的変数を直接使用しているときに副プログラムの引数としても渡すといったことが行われると、コンパイラの機能が制限されて、すべての状況で正しい最適なコードを生成できなくなる可能性があります。

プログラム内に別名参照が存在することが明らかな場合は、-xalias オプションを使用して、コンパイラが考慮すべきレベルを指定してください。適切な -xalias を指定しないで、-O2 よりも高い最適化レベルでコンパイルすると、場合によってはプログラムが正しく実行されなくなることがあります。

このオプションのフラグには、別名で参照する状況の種類を示すキーワードをコンマで区切って並べたリストを指定します。各キーワードには、別名参照が存在しないことを示す no% という接頭辞を付けることができます。

表 7–2 -xalias のキーワードとその意味

-xalias のキーワード

別名で参照する状況 

dummy

副プログラムの仮引数が互いに別名で参照し合ったり、大域的な変数を別名で参照することができます。 

no%dummy

Fortran 規格に準拠しています。実際の呼び出しで、仮引数が互いを別名で参照し合ったり、大域的な変数を別名で参照することはありません。これがデフォルトです。 

craypointer

プログラムは、任意の場所を指すことのできる Cray ポインタを使用します。これがデフォルトです。 

no%craypointer

Cray ポインタは、常に特定のメモリー領域を指しているか、使用されていません。 

ftnpointer

どの Fortran 95 ポインタも、型、種類、ランクに関係なく任意のターゲット変数を指すことができます。 

no%ftnpointer

Fortran 95 ポインタは規格の規則に従っています。これがデフォルトです。 

overindex

配列の参照時に添字の境界を越えることによって発生する、配列の境界を越えた添字付けには、4 つの状況があります。 プログラムの中でこれらが発生することを許可します。 

  • COMMON ブロック内の配列の要素を参照したときに、COMMON ブロックまたは等価なグループ内の要素が参照されることがあります。

  • COMMON ブロックまたは等価なグループの要素を実引数として副プログラムに渡すと、その COMMON ブロックまたは等価なグループの任意の要素にアクセスできるようになります。

  • 連続構造型の変数が COMMON ブロックとして扱われます。そのような変数の要素は、同じ変数のほかの要素を別名で参照することができます。

  • 配列の参照がその配列内にあっても、配列の添字の各境界が越えられることがあります。

    overindex は、配列構文、WHERE 文、FORALL 文には適用されません。これらの構文で配列の境界を越えた添字付けが発生する場合は、DO ループとして構文を書き直す必要があります。

no%overindex

配列の境界を越えることはありません。配列の参照がほかの変数を参照することもありません。これがデフォルトです。 

actual

コンパイラは、副プログラムの実引数を大域的な変数として扱います。副プログラムに引数を渡すと、Cray ポインタを使用した別名参照が行われる可能性があります。 

no%actual

副プログラムに引数を渡しても、そこから別名参照が行われることはありません。これがデフォルトです。 

次に、別名参照が行われる一般的な状況の例を示します。f95 コンパイラで高い最適化レベル (-O3 以上) でコンパイルする場合は、次に示す別名参照の状況がプログラムに含まれないようにし、-xalias=no%keyword を使用してコンパイルした方が、より良いコードを生成できます。

場合によっては、生成したコードによって正しい結果が得られるようにするために、-xalias=keyword を使用してコンパイルする必要があることがあります。

7.6.2.1 仮引数や大域的な変数による別名参照

次の例の場合は、-xalias=dummy を使用してコンパイルする必要があります。


parameter (n=100)
integer a(n)
common /qq/z(n)
call sub(a,a,z,n)
...
subroutine sub(a,b,c,n)
integer a(n), b(n)
common /qq/z(n)
a(2:n) = b(1:n-1)
c(2:n) = z(1:n-1)
コンパイラは、仮変数や共通の変数がオーバーラップする可能性があることを前提と
する必要があります。

7.6.2.2 Cray ポインタによる別名参照

この例が有効なのは、-xalias=craypointer (デフォルト) を使用してコンパイルした場合だけです。


parameter (n=20)
integer a(n)
integer v1(*), v2(*)
pointer (p1,v1)
pointer (p2,v2)
p1 = loc(a)
p2 = loc(a)
a = (/ (i,i=1,n) /)
...
v1(2:n) = v2(1:n-1)
コンパイラは、これらの場所がオーバーラップしている可能性があることを前提とす
る必要があります。

次に、オーバーラップしない Cray ポインタの例を示します。この場合は、-xalias=no%craypointer を使用してコンパイルします。この方が、より良いパフォーマンスを期待できます。


parameter (n=10)
integer a(n+n)
integer v1(n), v2(n)
pointer (p1,v1)
pointer (p2,v2)
p1 = loc(a(1))
p2 = loc(a(n+1))
...
v1(:) = v2(:)
Cray ポインタは、オーバーラップしたメモリー領域を指していません。

7.6.2.3 Fortran 95 ポインタによる別名参照

次の例を、-xalias=ftnpointer を使用してコンパイルします。


parameter (n=20)
integer, pointer :: a(:)
integer, target :: t(n)
interface
  subroutine sub(a,b,n)
    integer, pointer :: a(:)
    integer, pointer :: b(:)
  end subroutine
end interface

a => t
a = (/ (i, i=1,n) /)
call sub(a,a,n)
....
end
subroutine sub(a,b,n)
 integer, pointer :: a(:)
 real, pointer :: b(:)
 integer i, mold

 forall (i=2:n)
   a(i) = transfer(b(i-1), mold)
コンパイラは、a と b がオーバーラップする可能性があることを前提とする必要があり
ます。

この例では、コンパイラは、a と b が別のデータ型のデータを指していてもオーバーラップする可能性があることを前提とする必要があります。これは、Fortran 規格に反しています。コンパイラは、このような状況を検出すると警告を出力します。

7.6.2.4 配列の境界を越えた添字付けによる別名参照

次の例を、-xalias=overindex を使用してコンパイルします。


integer a,z
common // a(100),z
z = 1
call sub(a)
print*, z
subroutine sub(x)
  integer x(10)
  x(101) = 2
コンパイラは、副プログラムの呼び出しが z への書き込みを引き起こす可能性がある
と想定する可能性があります。
-xalias=overindex を使用してコンパイルすると、プログラムは 1 ではなく 2 を
出力します。

配列の境界を越えた添字付けは古い FORTRAN 77 のプログラムの多くに含まれていますが、これらは避けるべきものです。多くの場合は、結果を予測することができません。正しい結果が得られることを確かめるには、プログラムをコンパイルし、-C (境界を越えている添字の検査) オプションを使用してテストします。これによって、配列の添字に問題があるかどうかを調べることができます。

一般に、overindex は古い FORTRAN 77 のプログラムにだけ使用することをお勧めします。-xalias=overindex は、配列、構文式、部分配列、WHERE 文、FORALL 文には適用されません。

正しいコードが生成されるように、Fortran 95 のプログラムを常に Fortran 規格の添字の規則に準拠させるようにしてください。たとえば、次の例では、配列構文式の中で不明確な添字付けが行われているため、常に、配列の境界を越えた添字付けによる誤った結果が得られます。


配列の境界を越えて添字が付けられるこの配列構文の例では、正しい結果が得られません。

   parameter (n=10)
   integer a(n),b(n)
   common /qq/a,b
   integer c(n)
   integer m, k
   a = (/ (i,i=1,n) /)
   b = a
   c(1) = 1
   c(2:n) = (/ (i,i=1,n-1) /)

   m = n
   k = n + n
C
C   a への参照は、実際には b を参照しています。
C  これは本来は b(2:n) = b(1:n-1) であるべきです。
C
   a(m+2:k) = b(1:n-1)

C  またはこれを逆に行います。
   a(k:m+2:-1) = b(n-1:1:-1)

ユーザーは直感的に、配列 b が今度は配列 c のようになることを期待しますが、実際
の結果は予測できません。

overindex フラグは配列構文式には適用されないため、xalias=overindex フラグを使用してもこの状況は修正されません。この例をコンパイルすることはできますが、生成されたコードからは正しい結果を得られません。この例を書き換えて、配列構文を等価な DO ループに置き換えると、-xalias=overindex を使用してコンパイルしたときにこのフラグが働くようになります。しかし、このようなプログラミングはそもそも避けるべきです。

7.6.2.5 実引数による別名参照

コンパイラは、局所変数がどのように使用されるかを予測して、副プログラムの呼び出しによって変更されない変数について仮説を立てます。次の例では、副プログラムで使用されているポインタが原因で、コンパイラによる最適化処理が正しく行われず、結果が予測できないものになっています。正しい結果が得られるようにするには、-xalias=actual フラグを使用してコンパイルする必要があります。


 program foo
      integer i
      call take_loc(i)
      i = 1
      print * , i
      call use_loc()
      print * , i
   end

   subroutine take_loc(i)
      integer i
      common /loc_comm/ loc_i
      loc_i = loc(i)
   end subroutine take_loc

   subroutine use_loc()
      integer vi1
      pointer (pi,vi)
      common /loc_comm/ loc_i
      pi = loc_i
      vi1 = 3
   end subroutine use_loc

take_loci のアドレスを取得して保存し、use_loc がそれを使用しています。これは、Fortran 規格に反しています。

-xalias=actual を使用してコンパイルすると、副プログラムへのすべての引数がそのコンパイル単位内の大域的な変数として見なされるべきであるとコンパイラに伝えられます。 このため、コンパイラは、実引数のように見える変数について仮説を立てるときに、より慎重になります。

Fortran 規格に反するこのようなプログラミングは避けるべきです。

7.6.2.6 -xalias のデフォルト

リストを付けないで -xalias を指定した場合は、そのプログラムが Fortran の別名参照の規則に反していないものと仮定されます。これは、別名参照の全キーワードに no% が付いていると仮定するのと同じことです。

-xalias を指定しないでコンパイルする場合のコンパイラのデフォルトは次のとおりです。

-xalias=no%dummy,craypointer,no%actual,no%overindex,no%ftnpointer

Cray ポインタを使用していても、Fortran の別名参照の規則に準拠している場合、つまり、不明確な状況でもポインタの参照によって別名参照が行われない場合は、-xalias を使用してコンパイルすることによって、より良い性能のコードが生成される可能性があります。