Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

referencing actual arguments

Ray_R_1
Beginner
3,929 Views
The 10.0.027 compiler quide quotes
"Similarly, if any part of the actual argument is defined through a dummy argument, the actual argument can only be referenced through that dummy argument during execution of the procedure. For example, if the following statements are specified:
  MODULE MOD_A
REAL :: A, B, C, D
END MODULE MOD_A

PROGRAM TEST
USE MOD_A
CALL SUB_1 (B)
...
END PROGRAM TEST

SUBROUTINE SUB_1 (F)
USE MOD_A
...
WRITE (*,*) F
END SUBROUTINE SUB_1

Variable B must not be directly referenced during the execution of SUB_1 because it is being defined through dummy argument F. However, B can be indirectly referenced through F (and directly referenced when SUB_1 completes execution)."

why then does this program work?

MODULE MOD_A
REAL :: A, B, C, D
END MODULE MOD_A

PROGRAM TEST
USE MOD_A
b=1.0
CALL SUB_1 (B)
write(*,*)B
END PROGRAM TEST

SUBROUTINE SUB_1 (F)
USE MOD_A
B=2.0
WRITE (*,*) B,F
f=3.0
END SUBROUTINE SUB_1

2.000000 2.000000
3.000000
Press any key to continue . . .
should it not give an error? I have written code like subroutine SUB_1 and I am concerned that it is incorrect, even though it seems to work. Have I been getting away with something that is wrong?



0 Kudos
22 Replies
Steven_L_Intel1
Employee
3,490 Views
You've been lucky. The compiler's optimizer takes advantage of the standards prohibition against referencing an aliased argument if the routine causes the aliased variable to be redefined or become undefined. Sometimes it will seem to work, other times it will not. This is not something the compiler is required to detect, but I have seen many programs where failure to follow this rule causes wrong results.

Intel Fortran does have an option /assume:dummy_aliases, which lets the compiler know that such aliases may exist and therefore optimizations related to that are inhibited.
0 Kudos
Ray_R_1
Beginner
3,490 Views
Thanks for the quick reply. I'm may not of been so lucky, I have had programs that run just fine in debug mode, but not in release mode and could never pin down the problem, this may of been one of the causes. Does the same thing happen with a common block?

Program Main
real x,y
common /block1/ x
equivalence (x,y)
call sub(y)
End Program Main

Subroutine Sub(y)
real x,y
common /block1/ x
x=5
return
End Subroutine Sub
0 Kudos
Steven_L_Intel1
Employee
3,490 Views
You betcha, and I've seen MANY failures like this over the years, though as your subroutine doesn't also reference Y it's not really an example of this. As compilers get smarter, some programmer tricks become risky, and this is a major one. Another popular way to get bitten by this is to do something like this:

x = 5.0
call sub (x,x)
...
subroutine sub (x,y)
x = x + 1.0
print *, y
...

Now does it print 6 or 5? The answer is that it could print either one, or maybe even 42 (though that is unlikely).

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

Roberts,

Your code is "correct" and the output is what was directed by the code, however, the output may not be what you intended.

The documentation is grammatically incorrect: "can only" implies it would be impossible to do otherwise. As you have seen, this is not so. Replace "can only" with "should only". i.e. as a good programming practice directive and not a statement of fact.

As Steve points out, it is possible that a good compiler could detect this and issue a warning. But do not rely on this as SUB_1 might be compiled in a sperate assembly and not available during the compilation time of program TEST.

Steve, please comment on the following:

As a measure to protect against this (subroutine compiled separately), consider using an interface declaration for the subroutine that contains the ONLY clause to protect B

INTERFACE
SUBROUTINE SUB_1(F)
USE MOD_A, ONLY: B
REAL :: F
END SUBROUTINE SUB_1
END INTERFACE

From the above interface it is implicit that B, from within MOD_A, will be referenced by SUB_1. And therefore any code calling SUB_1 (and using the INTERFACE declaration) could warn you if B were to be used as a dummy argument to SUB_1. And it could do so without having access to the source code of SUB_1.

Note, you may still have a good reason to pass B in the call so I do not think the code should insist on not permitting B as a calling argument. I haven't tested the above interfaceto see if IVF does perform this warning service. I'll leave that up to you.

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
3,490 Views
Jim,

It is not sufficient that the variable be "referenced" in order to violate the stamdard here, it must also be stored to or have its definition status change. As this could be in conditional code, it would have to be a run-time check. Intel Fortran does not do this check.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

Well...,

I wouldn't want it to be an automaticruntime check as this would produce an Ab-End in a distributed runtime library. The programmer could write a sanity check, perhaps using LOC() to verify the arguments are safe to use, and if not take the appropriate action.

The real place for the warning is at compile time, as this is where the programming error is.

I suppose the ONLY construction could be expanded to include a different keyword for this purpose (NOARG?)

INTERFACE
SUBROUTINE FOO(F)
USE SomeMod, NOARG: A
REAL::F
END SUBROUTINE FOO
END INTERFACE

0 Kudos
Steven_L_Intel1
Employee
3,490 Views
But there's nothing wrong with passing A (in your example) as an argument. The trouble comes when a variable's storage is accessed two different ways in a routine AND it is modified.
0 Kudos
Ray_R_1
Beginner
3,490 Views
Is the problem that, when the variable is passed as an arugment it might get stuck in a register and therefore does not share the same memory location as the variable in the common or module while the subprogram is excuting, and are therefore independant? At the return from the subprogram the variable in the register can have a differnet value than the variable in the common and will overwrite the common variable on return?

If this is sort of what is happening, another question comes to mind. If I pass a variable (scalar, array, or array segment) to a subroutine and then use the %loc function in the subroutine to get the address of that variable, am I sure that I'm getting the address of the variable or could I be getting the address of a register or temp array?

Program MainPrg
real x
integer address
call sub1(x,address)
End Program MainPrg

Subroutine sub1(x,address)
real x
integer address
address=%loc(x)
End Subroutine sub1


0 Kudos
Steven_L_Intel1
Employee
3,490 Views
Yes, the compiler may choose to put a copy of one of the arguments in a register. If you use %loc, you'll always get the address of the argument as passed in, not any local copy the compiler may have made.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

A sanity check in your program could use %loc to test for improper argument usage.

Also, even if the argument is not registerized the code may have temporal issues where the compiler would not know if two variable names reference the same memory location(s) and the optimization removes seamingly removable statements. Assume F is dummy that references A

F=A+B
Y=A+Z

The optimization code would likely remember A for the second statementand not knowA wasmodified by F=.

Jim

0 Kudos
Steven_L_Intel1
Employee
3,490 Views
If your program wants to do such aliasing, compile with /assume:dummy_aliases and then you won't have to worry about it.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

>> If your program wants to do such aliasing, compile with /assume:dummy_aliases and then you won't have to worry about it.

That would be a partial fix since I would guess that the option effectively makes the dummies volatile (i.e. code always reaches into memory as opposed to keeping a copy in a register).

The reason this won't always work is that if you have multiple dummies the adverse interaction can span over multiple statements. To fix this would require some rather obtuse programming on the part of the compiler (e.g. a ring buffer for each variable). Additionally, the programmer may want or require the adverse interaction and the compiler wouldn't know the true intentions of the programmer.

These types of problems are particularly nasty to debug.

If the subroutine permits it, consider using pass by value or copy to local temps.

Jim Dempsey

0 Kudos
jarvusluntatintel
3,490 Views

Was there a change in the optimization during version 9.1 that relates to module variables also being referenced as dummies?

In my program, I used to pass everything as dummies. I eventually got tired of doing that and started referencing the variables through modules. It is likely that somewhere I am guitly of referencing the same varible in different ways. So when I saw this thread, I added the assume:dummy_aliases, but it didn't fix the problem, perhaps because of:

"The reason this won't always work is that if you have multiple dummies the adverse interaction can span over multiple statements"

which I could be guilty of that also. (Before this thread, it never crossed my mind that a dummy argument might be stored in a different part of memory than the same variable referenced directly in a use module statement.)

When using a version newer than 9.1.3291.2005 (including version 10) I get the following:

In debug mode, everything is fine. In release mode, about 1 in 20 data sets causes a memory crash of the, "send report to Microsoft, variety.

If I stick to 9.1.3291, or older versions, I can run things ok with both debug and release. I don't think it is a recently added bug in my code, because my new code works fine with the older versions. Additionally, my code from more than a year ago (which is widely distributed) has the 1-in-20 problem if compiled with newer versions.

Something seems to have changed with the compiler. However, my program is around 100,000 lines and it could certainly be a memory leak on my end that is only now being triggered.


Any ideas. Did something change with this optimization after 9.1.3291?

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

Fortran is by default "pass by reference" therefore arguments represent the address of the data and not a copy of the data as in a "pass by value" system such a C/C++.

Due to this, if you pass a global variable (or one who's scope is outside of the subroutine local variables) then it is possible for the the program to have multiple symbolic references (aliases) to the same storage locations. As long as you are aware of this and take proper actions you should not get into trouble. You can also have aliases to local variables in a call if you use the same variable multiple times in the argument list.

It is not up to the compiler to prohibit you from doing this as it may be perfectly valid for you to do so with your function/subroutine.

Thecommon reasons of a program running in Debug mode but not in Release mode are:

a) Execution with un-initialized variables (often related to implicit variables)
b) Linking in the wrong runtime library (different calling conventions)
c) Stack limits different between Debug and Release mode
d) different .DLLs being loaded.
e) optimization issues

The easiest reason to test might be e). The suggested strategy is to

a) Create a new configuration, call it DebugRelease and specify that it is to copy the configuration settings from Debug configuration.

b) Compile and test the DebugRelease configuration to make sure it runs the same as the Debug configuration.

c) Selectively enable optimizations (keeping debug info) and test for each project.

d) When project found that causes problems then disable optimizations on project and then progress selectively through each file (or groups of files in the project) enabling optimizations.

e) use c) and d) in a manner similar to a binary search (do half of files, then half of half, ...)

f) if you manage to optimize all the files (with debug info) and if the program still runs then start disabling runtime checks (using the c) d) method)

g) if you manage to remove all runtime checks and keep optimizations then look consider the possibility of problem in the release mode of the runtime libraries linked into the application (e.g. you are linking in the incorrect RTL)

Good luck bug hunting

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
3,490 Views
Optimization is always being improved. This can cause changes in behavior for an incorrect program. (I remember back in the 1980s people finding that putting variables in COMMON or using separate routines no longer inhibited optimization.)

The Fortran language has rules about what you can and cannot do with aliases. If you violate these rules, the results are unpredictable.
0 Kudos
jarvusluntatintel
3,490 Views

I appreciate the replies.

"it is possible for the the program to have multiple symbolic references (aliases) to the same storage locations. As long as you are aware of this and take proper actions you should not get into trouble."

That is what I thought, but from a previous post:

"The compiler's optimizer takes advantage of the standards prohibition against referencing an aliased argument if the routine causes the aliased variable to be redefined or become undefined."

The sort of thing that pops up in my code is like the following example:

MODULE MOD_A
REAL NodeArray(10)
END MODULE MOD_A

PROGRAM TEST
USE MOD_A
CALL SUB_1 (NodeArray(3))
...
END PROGRAM TEST

SUBROUTINE SUB_1 (Node)
REAL Node
...
CALL SUB_2 (Node)
END SUBROUTINE SUB_1

SUBROUTINE SUB_2 (Node)
USE MOD_A
REAL Node
...
Node = 5.0
NodeArray(3) = NodeArray(3) + 10.0
END SUBROUTINE SUB_2

I initially passed one element of an array (which could actually be an allocatablederived structure) down through some child subroutines. Later, in one of the child subroutines, I wanted to reference other elements of the array, so I added a use module statement back in, but left the dummy arguments in place. Is this problematic?

Regarding likely problems

a) Execution with un-initialized variables (often related to implicit variables)
b) Linking in the wrong runtime library (different calling conventions)
c) Stack limits different between Debug and Release mode
d) different .DLLs being loaded.
e) optimization issues

a) Caused me problems when I switched from CVF to Intel. I've put a lot of time into this one. However, neither a, b, nor c explain why my old code fails with a newer compilor, and my new code works with an old compilor. I'm not explicitly loading or linking any DLLs (static libraries, yes, DLLs no). So this would only be an issue if the compilors after 9.1.3291 load different DLLs.

e) seems to be the answer. I can start with a realease configuration and switch the Optimization setting from "maximise speed" to "disable" and the run time problems go away. Maximise speed is the /O2 setting? I'm running Intel under Visual Studio. What is the syntax to add the maximise speed setting to a single file?

Thanks once again.

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

To set properties for a single file in a project, right-click on file name in sloution explorer, the pick properties, optimizations, --- set what you want.

For your other problems (aliases)

I suggest you add conditional compilation code

!DEC$ IF DEFINED (_DEBUG)
if((%loc(Node(1)).ge.%loc(NodeArray(1))
& .and. ((%loc(Node(size(Node))).le.%loc(NodeArray(size(NodeArray))))
& call Bug()
!DEC$ ENDIF
Node = 5.0
NodeArray(3) = NodeArray(3) + 10.0

If you have several such places to test then consider creating a subroutine to verify no conflict of arguments.

Jim

0 Kudos
Steven_L_Intel1
Employee
3,490 Views
Offhand, I don't see a problem in the code example you posted. As long as you don't store into the same array element using two different names, you're ok. If you think your application has a problem with aliasing, use /assume:dummy-aliases - that's simpler than Jim's suggestion.

You may want to try a build with the Static Verifier enabled. Please see the 10.0 documentation for details on that. Some of the warnings it give you may be for things that are not problems, but it can point out real errors. I would suggest downloading and installing the just-released 10.1 version if you want to do this, as SV is improved in that release.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,490 Views

If you have several (many) instances where you want sanity checks then a good way to perform the test is to make a logical function that takes the derived type and the suspected dummy variable and makes the test.

subroutine aSub(A, B, C)
use MOD_FOO
real :: A, B, C
ASSERT((.NOT.WITHIN(FOO,A)))
ASSERT((.NOT.WITHIN(FOO,B)))
ASSERT((.NOT.WITHIN(FOO,C)))
! your code here
...
end subroutine aSub

Then using the preprocessor #define ASSERT according to your requirements (non-code expansion for Release, call to bounds testing function and error for Debug configuration).

You can also use ASSERT to test for NaN, valid reference address (e.g. is %LOC(A).EQ.0). All kinds of good tests (sanity checkes) in the debug version of the code. Good testing leads to good coding.

Hint, note in my prior post FOO is a module derived type variable of Type (TypeFOO) which represents all the variables within MOD_FOO. Consider adding barrier variables to the type.

type TypeFoo
sequence
#ifdef _DEBUG
CHARACTER(LEN=16) :: _BEGIN_
#endif
... ! declare TypeFoo variables
#ifdef _DEBUG
CHARACTER(LEN=1) :: _END_
#endif
end type TypeFoo

The length of _BEGIN_ was chosen as to not interfere with SSE alignment. This can be UNION'ed with the first variable(s) if you do not wish to take up space. The LEN of _END_ can be adjusted to make TypeFoo a multiple of the largest type or derived type within TypeFoo. You do not have to use _BEGIN_ and _END_. Whatever you use, be consistent with all the derived types that you wish to test for conflict of use.

Then in your #include header files

#define WITHIN(t,v) ((%loc(v) .ge. t%_BEGIN_) .and. (%loc(v) .le. t%_END_))

#ifdef _DEBUG
#define ASSERT(x) if(.not. (x)) Call AssertFailure(__FILE__, __LINE__)
#else
#define ASSERT(x)
#endif

Macro WITHIN always gets declared, macro ASSERT is declared or not depending on your needs. You can have your F90 source code free of the !DEC$ conditional assemblies and have clean looking code, yet at the same time the same code is suitable for Debug and Release configurations.

It is not unusual to have two different ASSERT's one for Debugging and one for Release. You may want to test in your release code at least through Beta and some of the earlier revisions. In your production run, when performance matters configure such as to make ASSERT insert no code.

Jim Dempsey

0 Kudos
jarvusluntatintel
3,381 Views


Steve

Sorry if I seem a bit slow on this (both in my response time and understanding).

"Offhand, I don't see a problem in the code example you posted. As long as you don't store into the same array element using two different names, you're ok. If you think your application has a problem with aliasing, use /assume:dummy-aliases - that's simpler than Jim's suggestion."

In my example

Node = 5
NodeArray(3) = NodeArray(3) + 10

Node is a real number that is an alias for the elment NodeArray(3). Isn't this storing into the same array element? Or do I not understand what storing into the same array element means?

I'm not sure what Jim means by the following:


">> If your program wants to do such aliasing, compile with /assume:dummy_aliases and then you won't have to worry about it.

That would be a partial fix since I would guess that the option effectively makes the dummies volatile (i.e. code always reaches into memory as opposed to keeping a copy in a register).

The reason this won't always work is that if you have multiple dummies the adverse interaction can span over multiple statements."


I had initially read this as "mulitple subroutines" not "multiple dummies". So I had thought thedummy_aliases wasn't going to help. However, if I had any problems with this, maybe the flag solved them. (But I guess I still wonder if my program might be guilty ofwhatever Jim is refering too.) In any event, it didn't solve the underlying problem.

I saw a post on the Static Verifier and did a search. It sounded like the first version had a memory problem with large programs and threw up a lot of warnings on things that were sloppy as opposed to problems. Do you know if the SV is going to be viable with a program that is around 100,000 lines and is broken into static libraries? Another reluctance I have is that I have code that was written in the 80's (common blocks and the like) and is very "sloppy" by modern programming style standards and I'm guessing that I will get many thousands of warning messages.

Jim, thanks for your detailed examples. I will chew over that when I get some time. However, I'm guessing that it would probably be as easy to strip out the dummy arguments and reference everything straight from my modules as to start populating everything with that many %loc statements.

Thanks for the info on setting flags. I didn't realize you could get a properties page for individual files. My experience with memory leaks is that you canmove things around (i.e.turn optimize off for individual files)and sometimes get the problem to disappear, but all you have done is tweaked thememory configuration andburied the underlying problem. So just becauseturning optimize off for some subroutinecausesa specificdata setto run ok,that doesn'tmean that the problem is really in that subroutine. Or am I being too pessimistic?

0 Kudos
Reply