Fortran 通用指令的各种形式包括:
!$PRAGMA keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
!$PRAGMA SUN keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
变量 keyword 标识特定的指令。此外也可以使用额外的参数或子选项。(某些指令要求使用额外的关键字 SUN,如上所示。)
通用指令使用以下语法:
在第一列中,使用以下任一注释指示符:c、C、! 或 *
对于 f95 自由格式,! 是唯一可识别的注释指示符 (!$PRAGMA)。本章中的示例采用 Fortran 95 自由格式。
后面七个字符为大写或小写的 $PRAGMA(字符之间没有空格)。
对于自由格式的源程序,使用 ! 注释指示符的指令可以出现在行中的任意位置。
请遵循以下约束:
与 Fortran 文本一样,在前八个字符之后,空格将被忽略,而且大写和小写字母等效。
由于它是注释,因此指令无法继续,但如果需要,您可以连续使用许多 !$PRAGMA 行。
如果注释符合以上语法,则它应该包含一个或多个编译器可识别的指令;如果不符合以上语法,系统会发出一条警告。
C 预处理程序 cpp 将扩展注释行或指令行中的宏符号定义;Fortran 预处理程序 fpp 不扩展注释行中的宏。fpp 会识别合法的 f95 指令,并允许在指令关键字外部进行有限的替换。但是,在处理需要关键字 SUN 的指令时要格外小心。cpp 会将小写的 sun 替换为预定义的值。另外,如果您定义了一个 cpp 宏 SUN,则它可能会与 SUN 指令关键字发生冲突。一般规则是,如果源文件将由 cpp 或 fpp 来处理,那么应以混合大小写来拼写这些 pragma,如下所示:
!$PRAGMA Sun UNROLL=3
Fortran 编译器可识别以下通用指令:
表 2–2 通用 Fortran 指令摘要
C 指令 |
!$PRAGMA C(list ) 将一系列外部函数的名称声明为 C 语言例程。 |
|
IGNORE_TKR 指令 |
!$PRAGMA IGNORE_TKR {name {, name} ...} 在解析特定调用时,编译器会忽略在通用过程接口中出现的指定哑元参数名称的类型、种类和等级。 |
|
UNROLL 指令 |
!$PRAGMA SUN UNROLL=n 建议编译器将下面的循环解开为指定的长度 n。 |
|
WEAK 指令 |
!$PRAGMA WEAK(name[ =name2]) 将 name 声明为弱符号,或者声明为 name2 的别名。 |
|
OPT 指令 |
!$PRAGMA SUN OPT=n 将子程序的优化级别设置为 n。 |
|
PIPELOOP 指令 |
!$PRAGMA SUN PIPELOOP=n 断言下面的循环中在间隔为 n 的迭代之间存在依赖性。 |
|
PREFETCH 指令 |
请求编译器为名称引用生成预取指令。(需要使用 -xprefetch 选项,缺省情况下启用该选项。编译时使用 —xprefetch=no 可以禁用预取指令。目标体系结构也必须支持预取指令,而且编译器优化级别必须大于 —xO2。) |
|
ASSUME 指令 |
!$PRAGMA [BEGIN} ASSUME (expression [ ,probability]) !$PRAGMA END ASSUME 断言编译器可假定程序中某些点处的条件为真。 |
C() 指令指定其参数为外部函数。它与 EXTERNAL 声明等效,但有一点例外,与普通外部名称不同,Fortran 编译器在这些参数名称的后面不附加下划线。有关详细信息,请参见《Fortran 编程指南》中的“C-Fortran 接口”一章。
在每个包含特定函数引用的子程序中,用于该函数的 C() 指令应该出现在对该函数的第一次引用之前。
示例-为 C 编译 ABC 和 XYZ:
EXTERNAL ABC, XYZ !$PRAGMA C(ABC, XYZ) |
此指令导致编译器在解析特定调用时,忽略在通用过程接口中出现的指定哑元参数名称的类型、种类和等级。
例如,在下面的过程接口中,指令指定 SRC 可以是任何数据类型,但 LEN 可以为 KIND=4 或 KIND=8。接口块为通用过程名称定义了两个特定过程。此示例以 Fortran 95 自由格式说明。
INTERFACE BLCKX SUBROUTINE BLCK_32(LEN,SRC) REAL SRC(1) !$PRAGMA IGNORE_TKR SRC INTEGER (KIND=4) LEN END SUBROUTINE SUBROUTINE BLCK_64(LEN,SRC) REAL SRC(1) !$PRAGMA IGNORE_TKR SRC INTEGER (KIND=8) LEN END SUBROUTINE END INTERFACE The subroutine call: INTEGER L REAL S(100) CALL BLCKX(L,S) |
在进行正常编译时,BLCKX 调用将调用 BLCK_32;在使用 -xtypemap=integer:64 进行编译时,将调用 BLCK_64。S 的实际类型并不能确定要调用哪个例程。对于基于参数类型、种类或等级来调用特定库例程的包装器来说,这可大大简化为其编写通用接口的工作。
请注意,无法在该指令中指定假定形状数组、Fortran 指针或可分配数组的哑元参数。如果未指定名称,则该指令将应用于过程的所有哑元参数,但假定形状数组、Fortran 指针或可分配数组的哑元参数除外。
UNROLL 指令要求您在 !$PRAGMA 后指定 SUN。
!$PRAGMA SUN UNROLL=n 指令指示编译器在其优化过程中将以下循环解开 n 次。(只有当编译器分析认为此类解开有必要时,它才会解开循环。)
n 是正整数。选项有:
如果 n=1,则优化器不得解开任何循环。
如果 n>1,则优化器可以解开循环 n 次。
如果实际解开了任何循环,那么可执行文件会变大。有关详细信息,请参见《Fortran 编程指南》中有关性能与优化的章节。
示例—解开循环两次:
!$PRAGMA SUN UNROLL=2 |
WEAK 指令定义一个符号,其优先级比以前定义的相同符号要低。此 pragma 主要用于源文件中以创建库。如果链接程序无法解析弱符号,它并不生成错误消息。
!$PRAGMA WEAK (name1 [=name2]) |
WEAK (name1) 将 name1 定义为弱符号。如果链接程序没有找到 name1 的定义,它不会生成错误消息。
WEAK (name1=name2) 将 name1 定义为弱符号以及 name2 的别名。
如果程序调用 name1,但没有对其进行定义,那么链接程序将使用库中的定义。但是,如果程序定义了自己的 name1 版本,那么将采用程序的定义,而不是使用库中 name1 的弱全局定义。如果程序直接调用 name2,则将使用库中的定义;重复的 name2 定义将导致错误。有关更多信息,请参见 Solaris《链接程序和库指南》。
OPT 指令设置子程序的优化级别,这将覆盖编译命令行中指定的级别。该指令必须紧挨在目标子程序前面出现,并且仅应用于该子程序。例如:
!$PRAGMA SUN OPT=2 SUBROUTINE smart(a,b,c,d,e) ...etc |
在使用指定 -O4 的 f95 命令编译以上内容时,该指令将覆盖此级别,并以 -O2 级别编译子例程。 除非该例程后面另有一个指令,否则下一个子程序将以 -O4 级别编译。
还必须使用 -xmaxopt[=n] 选项编译例程以便识别该指令。此编译器选项为 PRAGMA OPT 指令指定最大的优化值:如果 PRAGMA OPT 指定的优化级别大于 -xmaxopt 级别,则使用 -xmaxopt 级别。
PIPELOOP=n 指令要求您在 !$PRAGMA 后面指定 SUN。
此指令必须紧挨在 DO 循环前面出现。n 是正整数常量或零,它向优化器断言循环迭代之间是否存在依赖性。值零表示循环中没有迭代间的(即循环带有的)依赖性,优化器可以对循环执行任意管道处理。正值 n 表示,循环的第 I 次迭代与第 (I-n) 次迭代之间存在依赖性,每次最多只能对 n 个迭代进行管道处理。(如果未指定 n,则缺省为 0)
C We know that the value of K is such that there can be no C cross-iteration dependencies (E.g. K>N) !$PRAGMA SUN PIPELOOP=0 DO I=1,N A(I)=A(I+K) + D(I) B(I)=B(I) + A(I) END DO |
有关优化的更多信息,请参见《Fortran 编程指南》。
-xprefetch 选项标志(3.4.161 –xprefetch[= a[,a]])可启用一组 PREFETCH 指令,这些指令指示编译器在支持预取的处理器上为指定的数据元素生成预取指令。
!$PRAGMA SUN_PREFETCH_READ_ONCE(name) !$PRAGMA SUN_PREFETCH_READ_MANY(name) !$PRAGMA SUN_PREFETCH_WRITE_ONCE(name) !$PRAGMA SUN_PREFETCH_WRITE_MANY(name) |
有关预取指令的详细信息,另请参见《C 用户指南》或《SPARC Architecture Manual, Version 9》。
ASSUME 指令向编译器提供有关程序中某些点的条件的提示。这些断言可以帮助编译器设定其优化策略。程序员也可以在执行过程中使用这些指令检查程序的有效性。ASSUME 有两种格式。
“点断言”ASSUME 的语法是:
!$PRAGMA ASSUME (expression [,probability]) |
另外,“范围断言”ASSUME 的语法是:
!$PRAGMA BEGIN ASSUME [expression [, probability) block of statements !$PRAGMA END ASSUME |
请使用点断言格式来声明编译器可在程序中的该点采用的条件。而使用范围断言格式来声明在语句封闭范围内有效的条件。范围断言中的 BEGIN 和 END 对必须正确嵌套。
必需的 expression 是一个布尔表达式,该表达式可在程序中的该点求值,并且其中不包含用户定义的运算符或函数调用(下面列出的除外)。
可选的 probability 值是一个介于 0.0 和 1.0 之间的实数或者是整数 0 或 1,它给出表达式为真的可能性。probability 的值为 0.0(或 0)意味着永远不会为真;值为 1.0(或 1)则意味着始终为真。如果没有指定,则认为表达式很有可能为真,但并不一定为真。probability 为 0 或 1 以外其他值的断言是非必然断言。类似地,probability 正好为 0 或 1 的断言是必然断言。
例如,如果程序员知道 DO 循环的长度始终大于 10,000,则为编译器提供该提示可使之生成更好的代码。通常,以下循环在使用 ASSUME pragma 时比不使用时运行得要快。
!$PRAGMA BEGIN ASSUME(__tripcount().GE.10000,1) !! a big loop do i = j, n a(i) = a(j) + 1 end do !$PRAGMA END ASSUME |
有两个内部函数专用于 ASSUME 指令的表达式子句。(请注意,它们的名称前面有两个下划线。)
__branchexp() |
用于紧挨在分支转移语句(带有布尔控制表达式)前面的点断言。它与控制分支转移语句的布尔表达式生成相同的结果。 |
__tripcount() |
生成紧跟在指令后面的或指令所包含的循环的行程计数。在用于点断言时,指令后面的语句必须位于 DO 的第一行。在用于范围断言时,它应用于最外层的封闭循环。 |
在将来的版本中,特殊内部函数的列表可能会扩展。
可与 -xassume_control 编译器选项结合使用。(请参见3.4.111 –xassume_control[ =keywords])例如,在使用 -xassume_control=check 进行编译时,如果行程计数变为小于 10,000,则上述示例将生成一条警告。
如果使用 -xassume_control=retrospective 进行编译,则在程序终止时会生成一个摘要报告,指出所有断言是真还是假。有关 -xassume_control 的详细信息,请参见 f95 手册页。
另一个示例:
!$PRAGMA ASSUME(__tripcount.GT.0,1) do i=n0, nx |
如果使用 -xassume_control=check 编译上述示例,则在由于行程计数为零或负数而没有执行循环时,将会发出一条运行时警告。