Compaq KAP Fortran/OpenMP
for Tru64 UNIX
User Guide


Previous Contents Index


Chapter 6
KAP Assertions

6.1 Overview

KAP assertions enable the programmer to provide KAP with information about the program that would not normally be known at compilation time. Although many KAP users run the product without assertions, sometimes assertions can improve the optimization results. Use assertions only where speed is essential and you understand the application program well.

KAP does not guarantee that an assertion will have an effect. The information provided by the assertion will be noted, and if that information will help, it will be used.

To understand the process KAP uses in interpreting assertions, it is necessary to understand assumed dependencies. In the following loop, where X is an array, n and m are scalars, and nothing is known about the relationship between n and m , there are two types of dependencies:


DO 10 i=1,n 
      10   X(i) = X(i-1) + X(m) 

Between X(i) and X(i-1) there is a FORWARD dependence, and the distance is known to be one. Between X(i) and X(m) , KAP tries to find a relation, but cannot, because it does not know the value of m in relation to n . The second dependence is called an ASSUMED dependence, because it is assumed but cannot be proven to exist.

Assertions can be unsafe, because KAP cannot check the correctness of the information provided. If you specify an incorrect assertion, then the KAP generated code may give very different results than the original program. If unsafe assertions are the suspected cause of a misbehaving program, all assertions can be ignored (treated as comments) by using the -directives command-line switch without the a switch or the !*$* no assertions directive.

As with directives, an assertion placed before any comments or statements in the program is treated as a global assertion. That is, it is treated as if it were repeated at the top of each program unit in the file. Some assertions, for example, !*$* assert relation or !*$* assert permutation , include variable names. If these are specified as global assertions, the assertion is used in a program only when those variable names appear in common blocks or are dummy argument names to the subprogram. Global assertions cannot be used to make relational or permutation assertions about variables that are local to a subprogram.

Many assertions, like directives, are active until the end of the program or until overridden by another assertion. Other assertions are in effect only for the DO loops before which they appear. This type of assertion would apply to the next DO loop, but not to its nested loops. Other assertions are active within a program unit, regardless of where they appear in that program unit.

You can apply assertions collectively to a series of loops and arrays by enclosing them in a directive block. Because KAP treats the directive block as one loop, the assertions you want to be active on the loops inside the block must immediately precede the !*$* beginblock directive. Assertions immediately preceding directive blocks override previously set directives and assertions for the duration of the block, as follows:


 
 Program Unit A 
 
      !*$* directive 
     | 
     |  !*$* assertion 
     |  | !*$* beginblock 
     |  | loop 
     |  | array 
     |  | loop 
     |  ->!*$* endblock 
     | 
     -> End 

If you want to use different assertions for individual loops and arrays, enclose each loop or array in its own directive block.

Table 6-1 lists KAP assertions and their duration:

Table 6-1 KAP Assertions
Assertion Duration
!*$* assert [no]argument aliasing Section 6.2.1 until reset
!*$* assert [no]bounds violations Section 6.2.2 until reset
!*$* assert concurrent call Section 6.3.1 next loop
!*$* assert do (concurrent) Section 6.3.2 next loop
!*$* assert do (concurrent call) Section 6.3.3 next loop
!*$* assert do (serial) Section 6.3.4 see text
!*$* assert do prefer (concurrent) Section 6.3.5 next loop
!*$* assert do prefer (serial) Section 6.3.6 next loop
!*$* assert [no]equivalence hazard Section 6.2.3 until reset
!*$* assert [no]last value needed Section 6.2.4 until reset
!*$* assert permutation ( <name> ) Section 6.2.5 next loop
!*$* assert no recurrence ( <name> ) Section 6.2.6 next loop
!*$* assert relation (<name>.xx.<variable/constant>) Section 6.2.7 next loop
!*$* assert no sync Section 6.2.8 next loop
!*$* assert [no] temporaries for constant arguments Section 6.2.9 until reset

For the assertions listed with ( name ), ( name .xx. variable ), and ( name .xx. constant ), the following example illustrates the format of the information required:


!*$* assert permutation (ip) 
!*$* assert relation (n .gt. m) 
!*$* assert relation (n .gt. 0) 

6.2 Descriptions of KAP Assertions

The following sections describe some KAP assertions. Additional assertions are described in Section 6.3.

6.2.1 !*$* assert [no]argument aliasing

The !*$* assert no argument aliasing assertion allows KAP to make assumptions about subprogram arguments in a program. According to the Fortran 77 standard, multiple-aliasing of variables is allowed only if no aliases are modified. In the following code example, the subroutine violates the standard, because variable A is multiple-aliased in the subroutine through C and D , and variable X is multiple-aliased through X and E :


COMMON X,Y 
REAL A,B 
CALL SUB ( A, A, X ) 
... 
SUBROUTINE SUB ( C, D, E ) 
COMMON X, Y 
X = ... 
C = ... 
... 

If multiple-aliasing is used in a program, the !*$* assert argument aliasing assertion should be used. The command-line switch -assume=a acts like a global !*$* assert argument aliasing assertion. An argument aliasing assertion is active until reset, or until the end of the program unit.

6.2.2 !*$* assert [no]bounds violations

The !*$* assert bounds violations assertion indicates that array subscript bounds may be violated during execution. If the user has not violated array subscript bounds, this assertion should not be used. A bounds violations assertion is active until reset or until the end of the program. For formal parameters, KAP treats a declared last dimension of (1) the same as (*).

The -assume=b command-line switch acts like a global !*$* assert bounds violations assertion.

In the following example, the first loop nest is assumed to be standard-conforming, so the loops can both be optimized. The loops can be interchanged to improve memory referencing, because no A(I,J) will overwrite an A(I',J+1) . In the second nest, the assertion warns KAP that the loop limit of the first array index (I) may violate the declared array bounds. KAP is cautious and optimizes only the right array index.


  DO 100 I = 1,M 
     DO 100 J = 1,N 
       A(I,J) = A(I,J) + B (I,J) 
   100 CONTINUE 
      C 
       !*$*ASSERT BOUNDS VIOLATIONS 
          DO 200 I = 1,M 
           DO 200 J = 1,N 
            A(I,J) = A(I,J) + B (I,J) 
      200 CONTINUE 

Becomes:


         DO 2 J=1,N 
         DO 2 I=1,M 
           A(I,J) = A(I,J) + B (I,J) 
         2     CONTINUE 
         C 
         !*$* ASSERTBOUNDSVIOLATIONS 
              DO 4 I=1,M 
              DO 3 J=1,N 
                 A(I,J) = A(I,J) + B (I,J) 
          3    CONTINUE 
          4    CONTINUE 

Note

KAP always assumes that array references will be within the array itself, so the rightmost index will be safe to modify references to.

6.2.3 !*$* assert [no]equivalence hazard

The !*$* assert noequivalence hazard assertion tells KAP that equivalenced variables will not be used to refer to the same memory location inside one DO loop nest. Normally, equivalence statements allow different variable names to refer to the same storage location. The -assume=e command-line switch acts like a global !*$* assert equivalence hazard assertion. The equivalence hazard assertion is active until reset or until the end of the program.

In the following example, if arrays E and F are equivalenced, but you know that the overlapping sections will not be referenced in this loop, using !*$* assert noequivalence hazard allows KAP to optimize the loop:


EQUIVALENCE ( E(1), F(101) ) 
   !*$* ASSERT NO EQUIVALENCE HAZARD 
           DO 10 I = 1,N 
            E(I+1) = B(I) 
              C(I) = F(I) 
  10  CONTINUE 

6.2.4 !*$* assert [no]last value needed

Frequently when a scalar is assigned in a loop that is optimized, KAP uses a temporary variable within the optimized loop and assigns the last value to the original scalar if KAP believes that the scalar may be reused before it is assigned again. The !*$* assert nolast value needed assertion lets KAP assume that such last-value assignments are unnecessary. This assertion is active until reset or until the end of the program.

The -assume=l command-line switch acts like a global !*$* assert last value needed assertion.

At -optimize=2 and higher, KAP performs variable lifetime analysis to determine when last-value assignments are unnecessary. The !*$* assert nolast value needed assertion can be used on a loop-by-loop basis when you do not want to eliminate last-value assignments globally.

6.2.5 !*$* assert permutation

The !*$* assert permutation assertion provides KAP with sufficient information to allow KAP to generate optimized code for certain types of indirect addressing (INTEGER arrays used in subscripts). This assertion requires -optimize=4 or higher. The following example shows when it is unsafe to optimize a DO loop:


DO 100 I = 1,N 
          A(IP (I)) = A(IP (I)) + B(I) 
100  CONTINUE 

KAP cannot safely generate optimized code because it cannot tell if the different values in the index array IP overlap. If all values in I are distinct, the optimized code is correct; if some of the values are the same, the optimized code may be unsafe. You can tell KAP that the values in the index array IP are all distinct with the following assertion:


!*$*ASSERT PERMUTATION( IP ) 
         DO 100 I=1,N 
            A(IP (I)) = A(IP (I)) + B(I) 
100  CONTINUE 

With the addition of this assertion, KAP knows that it can safely generate optimized code for this loop. If at run time the values of IP are not distinct, the optimized code may generate incorrect results.

6.2.6 !*$* assert no recurrence

The !*$* assert no recurrence assertion asks KAP to ignore ALL data dependence conflicts due to the named variable in the following DO loop. KAP makes the final decision whether or not to ignore a data dependence conflict. The following example asks KAP to ignore all dependence arcs caused by the variable X in the loop:


!*$* ASSERT NO RECURRENCE ( X ) 
    DO 10 I=1,M,5 
        10    X(K) = X(K) + X(I) 

Not only does KAP ignore the assumed dependence, but also the real dependence caused by X(K) appearing on both sides of the assignment.

This assertion only applies to the next DO loop. It cannot be specified as a global assertion.

6.2.7 !*$* assert relation ( <name> .XX. <variable/constant>)

The !*$* assert relation (where XX=GT,LT,LE, etc) ( <name> .XX. <variable/constant> ) assertion indicates the relationship between two variables or between a variable and a (possibly signed) constant. When attempting to optimize a loop, KAP occasionally asks about such relationships, as in the following example:


DO 100 I = 4,N 
         A(I) = A(I+M) + B(I) 
     100  CONTINUE 

KAP generates this question in the source code section of the listing file:


Is "M .GE. N" in this loop? 

If M will always be greater than N at this point in the program, you can relay this information to KAP using an assertion.

Again, note that KAP cannot check the validity of assertions. With the assertion that M is greater than the loop upper bound, KAP can generate optimized code for this loop. If at run time M turned out to be less than N , the results produced by the optimized code would probably be different than the results produced by the original loop.

This assertion is in effect only for the DO loop before which it appears.

6.2.8 !*$* assert no sync

If a loop has been optimized by KAP and you note that KAP was cautious and added unnecessary synchronization code, as shown in the following example, the !*$* assert no sync assertion may improve run-time speedup.


DO 20 I = 1,N 
       A (100 + I) = I 
       C (100 + I) = A (I) 
       D (100 + I) = C (I) 
20  CONTINUE 

KAP standard optimization synchronizes the loop by breaking it up in order to ensure the assignments occur in a valid order. However, this maximizes DO loop overhead as follows:


   DO 2 I=1,N 
     A(I+100) = I 
  2   CONTINUE 
     DO 3 I=1,N 
           C(I+100) = A(I) 
  3  CONTINUE 
     DO 4 I=1,N 
     D(I+100) = C(I) 
  4  CONTINUE 

If the value of N will always be less than or equal to 100, you can eliminate the synchronization overhead by adding !*$* assert no sync as follows:


 !*$* ASSERT NO SYNC 
   DO 20 I=1,N 
      A(I+100) = I 
      C(I+100) = A(I) 
      D(I+100) = C(I) 
   20 CONTINUE 

6.2.9 !*$* assert [no] temporaries for constant arguments

Sometimes, KAP transformations are disabled when KAP is not sure about their effect on the rest of the program. For example, one possible transformation would turn:


   SUBROUTINE X(I,N) 
      IF (I .LT. N) I = N 
   END 

Into:


    SUBROUTINE X(I,N) 
       I = MAX(I,N) 
    END 

But, if the actual parameter for I were a constant, CALL X(1,N) , it would appear that the value of the constant 1 was being reassigned. (In some older versions of Fortran, the values of constants could be changed in this way.) Without additional information, KAP is cautious and performs no argument-changing transformations within the subroutine.

Most compilers automatically put constant actual arguments into temporary variables to protect against this case. The assertion !*$* assert temporaries for constant arguments or the command switch -assume=c (the default) inform KAP that constant parameters are protected. The assertion !*$* assert no temporaries for constant arguments directs KAP to avoid transformations that might change the values of constant parameters.

6.3 Parallel Processing Assertions that Guide Automatic Parallelization

The following sections describe assertions available in the multiprocessor version of KAP.

6.3.1 !*$* assert concurrent call

The !*$* assert concurrent call assertion tells KAP the subroutine calls and function references in the loop immediately following this assertion can execute in parallel. It causes KAP to ignore all potential data dependencies due to subroutine arguments. This assertion does not apply to nested or surrounding loops.

The following code example shows how !*$* assert concurrent call works:


!*$* ASSERT CONCURRENT CALL 
      DO 10 I=.. 
        ... 
          CALL S1 
        ... 
 10  CONTINUE 

You can use !*$* assert concurrent call to override default preprocessor action. In the following code example, !*$* assert concurrent call tells the compiler it is safe to make a parallel call to TEST. Another assertion, !*$* assert prefer do (concurrent) , tells the compiler to "prefer" a reordered loop nesting that causes parallel execution over the J loop.


!*$*    ASSERT CONCURRENT CALL 
!*$*    ASSERT PREFER DO (CONCURRENT) 
        DO J = 1,N,64 
           DO K = 1,N,64 
              DO I = 1,N,64 
                 CALL TEST('N','N', 
     &                      MIN0(64, N-I+1),MIN0(64,N-J+1),MIN0(64,N-K+1), 
     &                      1.0D0, 
     &                      A(I,K),N+1, 
     &                      B(K,J),N+1, 
     &                      1.0D0, 
     &                      C(I,J),N+1 
     &                     ) 
              ENDDO 
           ENDDO 
        ENDDO 

Caution

The !*$* assert concurrent call assertion tells KAP to assume that all external subroutine/function calls are thread-reentrant. This will override KAP default behavior, which is to assume that all external subroutines/functions are NOT thread-reentrant or thread-safe. If the external subroutine/functions are not thread-safe, and you use !*$* assert concurrent call , your program may not execute correctly. For example, local variables in subroutines are thread-safe only if they are stored as thread-specific data. For more information on thread-safe subroutines, see the Tru64 UNIX Guide to the POSIX Threads Library.

For more information about !*$* assert prefer do (concurrent) , see Section 6.3.5.

KAP does not generate parallel code if you use the -noconcurrent command-line switch.

6.3.2 !*$* assert do (concurrent)

The !*$* assert do (concurrent) assertion tells KAP to prefer to ignore assumed dependencies and to execute the DO loop immediately following this assertion in parallel.

This assertion says nothing about the concurrency threshold for the loop. This means KAP continues to honor the dependencies it finds. For example, in the following loop KAP ignores !*$* assert do (concurrent) because there is a dependence on A :


!*$* ASSERT DO (CONCURRENT) 
        DO 100 I = 4, N 
            A (I) = A (I-4) + B (I) 
    100 CONTINUE 

Strictly speaking, you could parallelize the loop enabling KAP to put the entire loop body inside a critical section. The parallelized loop would run more slowly than the original serial version, however.

The following code example shows how !*$* assert do (concurrent) tells KAP to ignore an assumed dependence, the I+M array index, and to make the stride-1 loop concurrent:


      program test 
      real a(100,100) 
      read *,m,n 
      read *,a 
!*$*assert do (concurrent) 
      do 11 i = 1,n 
        do 12 j = 1,n 
            a(i,j) = a(i+m,j) 
12    continue 
11    continue 
      end 

Using the assertion and processing with -unroll=1 and -conc , KAP generates the following code:


     PROGRAM TEST 
       REAL A(100,100) 
       READ *, M, N 
       READ *, A 
!*$* ASSERT DO( CONCURRENT ) 
!$OMP PARALLEL SHARED (N,A,M) PRIVATE (I,J) 
!$OMP DO 
       DO I=1,N 
        DO J=1,N 
         A(I,J) = A(I+M,J) 
        END DO 
       END DO 
!$OMP END DO NOWAIT 
!$OMP END PARALLEL 
      END 

Another example of when to use !*$* assert do (concurrent) follows. The DO loop can execute safely in parallel, given the value of M is greater than the value of N . Preceding the loop with !*$* assert do (concurrent) allows parallel execution to happen.


!*$*    ASSERT DO (CONCURRENT) 
        DO I = 1,N 
           X(I) = X(I+M) 
        ENDDO 

KAP does not generate parallel code if you use the -noconcurrent command-line switch.

6.3.3 !*$* assert do (concurrent call)

The !*$* assert do (concurrent call) assertion tells KAP to execute both the !*$* assert do (concurrent) and the !*$* assert concurrent call assertions in the immediately following loop.

6.3.4 !*$* assert do (serial)

The !*$* assert do (serial) assertion forces the loop immediately following this assertion to be serial. Additionally, it restricts optimization by forcing all enclosing loops to be serial. Inner loops and other loops inside the same enclosing loop nest, but not enclosing the serial loop, may be optimized. The following code example shows how !*$* assert do (serial) works:


 DO 100 I=1,N 
    DO 100 J = 1, N 
!*$* ASSERT DO (SERIAL) 
          DO 200 K = 1, N 
            X(I,J,K) = X(I,J,K) * Y(I,J) 
200      CONTINUE 
          DO 300 K = 1, N 
            X(I,J,K) = X(I,J,K) + Z(I,K) 
300      CONTINUE 
100      CONTINUE 

The assertion forces the DO 100 I loop, the DO 100 J loop, and the DO 200 K loop to be serial. The DO 300 K loop can still be parallelized. In this case, KAP does NOT distribute the I or J loops to try to get a larger optimizable loop.

The following loop code shows an example of when to use !*$* assert do (serial) . X and Y must not process in parallel because they are equivalenced and they overlap in memory. Using !*$* assert do (serial) stops parallel execution of the loop.


!*$*    ASSERT DO (SERIAL) 
        DO I = 1,N 
           X(I) = Y(I) 
        ENDDO 

See also the !*$* assert do prefer (concurrent) assertion, Section 6.3.5, and !*$* assert do prefer (serial) , Section 6.3.6.

6.3.5 !*$* assert do prefer (concurrent)

The !*$* assert do prefer (concurrent) assertion tells KAP to prefer parallel ordering for the DO loop immediately following this assertion. The following code example shows how the assertion works:


!*$* ASSERT DO PREFER (CONCURRENT) 
        DO 10 I = 1, M 
           DO 10 J = 1, N 
        10    X(I,J) = X(I,J) + Y(I,J) 

The assertion tells KAP to prefer any ordering where the DO 10 I loop is parallel.

The !*$* assert do prefer (concurrent) assertion does not mention anything about assumed dependencies. If KAP finds dependencies, it does not perform optimization.

This assertion is valid only for the DO loop it precedes. The -noconcurrent switch disables the generation of parallel code.

6.3.6 !*$* assert do prefer (serial)

The !*$* assert do prefer (serial) assertion tells KAP to prefer serial ordering for the DO loop immediately following this assertion. Unlike !*$* assert do (serial) , assert do prefer (serial) does not inhibit optimization of outer loops.

The following code example shows how !*$* assert do prefer (serial) works:


DO 100 i=1,N 
           DO 100 J = 1, N 
!*$* ASSERT DO PREFER (SERIAL) 
            DO 200 K = 1, N 
                X(I,J,K) = X(I,J,K) * Y(I,J) 
      200      CONTINUE 
               DO 300 K = 1, N 
               X(I,J,K) = X(I,J,K) + Z(I,K) 
      300      CONTINUE 
      100      CONTINUE 

The !*$* assert do prefer (serial) assertion allows optimization over the whole loop nest, while trying to keep the DO 200 K loop serial. Compare this example with the !*$* assert do (serial) example to see how the two assertions produce different results with identical loop structures.


Previous Next Contents Index