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 程序利用这种别名使用机制来提供当时程序语言中尚不具备的某种动态内存管理。

在所有可移植代码中避免别名使用。在某些平台上以及在用高于 -O2 的优化级别编译时,其结果可能是无法预料的。

f95 编译器会假定其编译的是符合标准的程序。不严格遵循 Fortran 标准的程序可能会引起二义性情况,从而干扰编译器的分析和优化策略。某些情况甚至能产生错误结果。

例如,数组索引越界、使用指针或将仍在直接使用的全局变量作为子程序参数传递,都可导致二义性情况,从而限制了编译器生成在所有情况下都正确的优化代码的能力。

如果知道程序的确包含一些明显的别名使用情况,可使用 -xalias 选项指定编译器的关注程度。在某些情况下,当以高于 -O2 的优化级别编译时,程序不会正确执行,除非指定了适当的 -xalias 选项。

此选项标志会获取一个以逗号分隔的、指示别名使用情况类型的关键字列表。可在每个关键字前冠以 no% 前缀,用以指示不存在的别名使用。

表 7–2 -xalias 关键字及其含义

-xalias= keyword

别名使用情况 

dummy

伪子程序参数既可以互为别名,也可以作为全局变量的别名。 

no%dummy

遵循 Fortran 标准,伪参数在实际调用中既不互为别名也不作为全局变量的别名。(这是缺省情况。) 

craypointer

程序使用可指向任何地址的 Cray 指针。(这是缺省情况。) 

no%craypointer

Cray 指针总是指向明确的内存区,或者不被使用。 

ftnpointer

任何 Fortran 95 指针都可以指向任一目标变量,而不管其为何类型、种类或等级。 

no%ftnpointer

Fortran 95 指针遵守标准规则。(这是缺省情况。) 

overindex

在数组引用中违反下标边界可造成四种索引越界情况,其中的任何一种或多种都可以在程序中出现: 

  • 对 COMMON 块中数组元素的引用可以引用 COMMON 块或等价组中的任何元素。

  • 作为子程序的实际参数传递 COMMON 块或等价组的某一元素,将允许对该 COMMON 块或等价组中的任何元素进行访问。

  • 会将序列派生类型的变量当作是 COMMON 块一样来看待,并且此种变量的元素可以作为该变量其他元素的别名。

  • 可以违反各个数组下标边界,即使数组引用仍在该数组内。

    overindex 不适用于数组语法、WHEREFORALL 语句。如果索引越界出现在这些构造中,应将它们改写为 DO 循环。

no%overindex

不违反数组边界。数组引用不引用其他变量。(这是缺省情况。) 

actual

编译器将子程序的实际参数视为全局变量。向子程序传递参数有可能通过 Cray 指针导致别名使用。 

no%actual

向子程序传递参数不会进一步造成别名使用。(这是缺省情况。) 

这里列举了别名使用情况的一些典型示例。在较高优化级别(-O3 及其以上级别)上,如果您的程序中不存在如下所示的别名使用弊病,并且您是用 -xalias=no%keyword 进行编译的,f95 编译器可以生成更好的代码。

在某些情况下,您需要使用 -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
编译器假设对 sub 的调用可以写入 z
用 -xalias=overindex 编译时,程序打印 2 而非 1

索引越界在很多传统 Fortran 77 程序中都会出现,应予以避免。在很多情况下,结果将无法预料。要确保正确性,应使用 -C(运行时数组边界检查)选项编译和测试程序,以标记任何数组下标问题。

一般而言,overindex 标志只能与传统 Fortran 77 程序一起使用。-xalias=overindex 不适用于数组语法表达式、数组段、WHEREFORALL 语句。

为确保生成代码的正确性,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   the reference to a is actually a reference into b
C   so this should really be  b(2:n) = b(1:n-1)
C
   a(m+2:k) = b(1:n-1)

C  or doing it in reverse
   a(k:m+2:-1) = b(n-1:1:-1)

从直观上,用户期望数组 b 现在与数组 c 相似,但结果是无法预料的

xalias=overindex 标志无助于此种情况,因为 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_loc 会获取 i 的地址,并将其保存起来。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 进行编译,结果可能会生成更好的优化代码。