- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Managing complex lecagy applications, it is very important that some extent of backward compatibility is provided by the new compilers, so that a current-standard-violating application can be compiled and run in the new environment, while its source is cleaned up and made compliant for "the next release" and a safer future life.
One of the cases I came across is calling a procedure using a constant value in place of an argument which is INTENT [IN]OUT in the semantics of the called procedure.
This violation of the current standard used to work in the past because a temporary copy of the constant was passed to the procedure, and any change done to it in the called procedure was then discarded.
This behavior is still available in several compilers, and in some of them it is still the default or even the unique one.
Other compilers have different default behaviors, possibly changeable using some compiler option.
In some cases the passed constant really "changes" its value, and its new (undesired) value, leads to difficult to find errors. (One can hardly understand that 1 is no longer 1 ....)
In other cases the constants are stored in "read-only" memory locations, and the case produces an "Access violation" or "Segmentation fault". This happens with the Intel Fortran compiler ( see here a post by Steve Lionel ), and (IMHO) is a rude way to tell you that your code is out of date.
In the Intel Fortran compiler, the "permissive" behavior should be safely provided by the "-assume protect_constants" switch, the task of which should be to tell
the compiler to pass constants in a stack temporary so that the called procedure can safely store to it, with the changes being discarded on return. ( see here )
Unfortunately, it seems not to work, at least in Linux version 10 and 11, and the "Segmentation fault" occurs even when compiling using that switch.
This means that old code might require a lot of fixes to work fine ...
Has anyone a different experience ? Did I miss something or make something wrong (apart using non-compliant code) ?
This is my simple test:
$ cat pca.f
PRINT *,'MAIN - start up 1=',1
CALL SUB1(1)
PRINT *,'MAIN - After call 1=',1
END
SUBROUTINE SUB1(A)
INTEGER A
PRINT *,'Entered SUB1 - A=', A
A=A*10
PRINT *,'Leaving SUB1 - A=', A
RETURN
END
$ ifort -o pca pca.f && pca
MAIN - start up 1= 1
Entered SUB1 - A= 1
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image PC Routine Line Source
pca 08049DB4 Unknown Unknown Unknown
pca 08049CC1 Unknown Unknown Unknown
libc.so.6 0050FDEC Unknown Unknown Unknown
pca 08049BB1 Unknown Unknown Unknown
$ ifort -assume protect_constants -o pca pca.f && pca
MAIN - start up 1= 1
Entered SUB1 - A= 1
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image PC Routine Line Source
pca 08049DB4 Unknown Unknown Unknown
pca 08049CC1 Unknown Unknown Unknown
libc.so.6 00126DEC Unknown Unknown Unknown
pca 08049BB1 Unknown Unknown Unknown
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The documentation for this switch is confusing. What you want is "-assume noprotect_constants". Protect in this context means "prevent modification of the argument", in other words, let the segv happen. noprotect_constants allows the modification to happen to a temp.
I'll work with the writers to see if we can make this clearer.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The options are confusing. The option needed is: -assume noprotect_constants
$ ifort -V -assume noprotect_constants -o pca pca.f && pca
Intel Fortran Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0 Build 20081105 Package ID: l_cprof_p_11.0.074
Copyright (C) 1985-2008 Intel Corporation. All rights reserved.
Intel Fortran 11.0-1558
GNU ld version 2.16.91.0.5 20051219 (SUSE Linux)
MAIN - start up 1= 1
Entered SUB1 - A= 1
Leaving SUB1 - A= 10
MAIN - After call 1= 1
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The documentation for this switch is confusing. What you want is "-assume noprotect_constants". Protect in this context means "prevent modification of the argument", in other words, let the segv happen. noprotect_constants allows the modification to happen to a temp.
The options are confusing. The option needed is: -assume noprotect_constants
$ ifort -V -assume noprotect_constants -o pca pca.f && pca
Thanks for your attention and answers.
If I had read thoroughly the documentation of -assume, actually I would have found the right answer without bothering forum & support. The documentation is confusing as you say, nevertheless the information is somehow clear, but not retrievable at a glance.
I would try to explain why I was not able to understand what I needed, not to justify my blindness, but just in case it could help the technical writers to release a more helpful product.
The first reason is that when compulsing a reference manual, one would like to find all the needed specific information in just a few lines concentrated in a single spot, otherwise one can stop reading too early and miss the relevant piece of information, as it happened to me.
The explanation of the compilation flags (let us focus on the -assume [no]protect_constants switch, but the pattern is the same for all of them) is split in three places, instead, and not so close to each other :
1) a general explanation :
[no]protect_constants Determines whether a constant actual argument or a copy of it is passed to a called routine.
The purpose of which seems just to loosely define the scope of the switch. In fact, in this, as in many other cases where the doc gives a compound two-fold "explanation", one can hardly guess which one of the two altertanives determines the desired action. We can say that data are there but the chance is wasted of bringing immediately usable information.
2) the default specification: (about 60 lines later)
protect_constants A constant actual argument is passed to a called routine. Any attempt to modify it results in an error.
3) a further description : (about other 60 lines later)
assume noprotect_constants
Tells the compiler to pass a copy of a constant actual argument. This copy can be modified by the called routine, even though the Fortran standard prohibits such modification. The calling routine does not see any modification to the constant.
I can see that the information is clear, but the way it is delivered requires a reading of all the page to retrieve the dispersed pieces of information.
Another reason is that my eyes and mind were biased by the behavior of other compilers I use, in which the "protection" has a subtly different meaning. For instance, the Lahey Compiler never prevents the modification of the constant argument in the called routine; then, this might result in a dirty and dangerous alteration of the constant which propagates in the rest of the program (a "1" read in the code can actually become any other value), unless you "protect" it using the --pca (protect constant argument) compilation flag, in this case the constant argument, as seen in the caller routine, will retain its original value.
The two standpoints seem similar but are completely different.
Both are legitimate, of course, but I do not like any of them, for the following reasons, simply based on common sense:
1) If I use a constant when calling a routine, it is absolutely certain that I do not want it is changed, and that I am not interested, in that specific call, in any possible different value returned in that argument. Then it would ALWAYS be correct that a temporary variable, which is given on input the value of the constant, is passed to the routine. For example, this is the default and (afaik) unique possible behavior in XLF compiler. Using this approach no error might ever occurr, regardless of any INTENT declared and/or implemented for the argument in the called routine.
2) The fact that to obtain the above mentioned behavior I have to use specific flags (when, as said, the intention of the programmer is absolutely clear) means for sure that not using them I get some error, which can also difficult to find. I am not a compilers theorist or writer, but I cannot see any advantage in having this "flexibility".
3) The fact that the flags to obtain the same behavior are named in opposite ways in the Lahey and Intel compilers
[ --pca (protect constant argument) lahey , -assume NOprotect_constants intel ], is then the icing on the cake ...
I am sorry for the excess of verbosity, and thank you again.
PS
@ Steve Lionel,
I do not know if it is still possible, but, after your clarification, I think you should edit your own post "Don't touch me there" and replace /assume:protect_constants with /assume:noprotect_constants in the closing note.
PPS.
However, to become compiler-option-independent as far as possible (who knows what future compilers will need...), I started a campaign of removal of as many constants as possible from any call statement in my code (which is really a crazy task !)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I edited the post - thanks. See, even I get confused!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I edited the post - thanks. See, even I get confused!
The real problem is that that flags, for the reasons I said, seems really useless !
Or perhaps, having a weak rationale behind, it turns out to be non-intuitive or even counter-intuitive, and this can generate misunderstanding and confusion.
I would like to go more in depth with the rationale behind this flag.
Using a constant as an actual argument for an INTENT OUT or INOUT dummy argument is said to be a violation of the current standard. Which is the paragraph where this issue is specifically dealt with in the standard ? Has the default behavior of the Intel compiler been designed to guard this issue or for other reasons ?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Of course, a program that modified a dummy argument associated with a constant or expression, or that is declared INTENT(IN) violates the language rules. In the case of INTENT(IN), assuming the actual routine had that attribute visible the compiler will disallow any assignments at compile time. And the compiler will complain if you pass a constant to an INTENT(OUT). But the language rules do not permit an error for INTENT(INOUT), as this just says that the argument MAY be modified.
But in the real world, people DO pass constants and expressions to routines that modify the argument. For efficiency, most compilers do "constant pooling" so that there is one memory copy of a constant (say, 3), per routine. If you pass 3 as an actual argument, the compiler passes the address of this shared copy. Should the called routine do something like ARG=ARG+1, then 3 will become 4 when used subsequently.
The most efficient way to deal with this is to put the constant pool in a read-only image section - typically the same one that compiled code uses. There's no overhead for this, but if the called routine tries to modify the value an error will be raised by the OS.
As noted in my article you cited, the first version of Digital Visual Fortran did not do this, so you could modify the value of 3. When we added the read-only protection in version 6, many users complained that their programs stopped working, as they had been inadvertently modifying constant arguments. So we added the switch to do what the compiler already did if you passed an expression - it made a stack copy of the constant and passed the address of the copy. That way any (non-standard) writes to the value would be discarded on exit. This costs some extra instructions, so is not the default.
When we became Intel Fortran in 2003, it turned out that the Intel code generator didn't know how to do constants in read-only memory, so we had to add that back in.
In the real world, it is not always a solution to tell a customer that their code is broken. They'll say that it works with compilerx X, Y and Z and what's wrong with Intel? This is why we sometimes offer a switch, such as -assume noprotect_constants, to allow their bad code to continue working.
The only behavior change in the Intel compiler was to support read-only constants, and that was several versions ago.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Of course, a program that modified a dummy argument associated with a constant or expression, or that is declared INTENT(IN) violates the language rules. In the case of INTENT(IN), assuming the actual routine had that attribute visible the compiler will disallow any assignments at compile time. And the compiler will complain if you pass a constant to an INTENT(OUT). But the language rules do not permit an error for INTENT(INOUT), as this just says that the argument MAY be modified.
It is clear that, when writing new code, the good programmer should adopt the syntax provided by the more recent standards, in order to get a more robust and reliable product. But when dealing with legacy code in FORTRAN 77, the INTENT can only be derived by the semantic of the routines code, a job I do not think compilers do or could do, being the calling and the caller procedure compiled independently.
But in the real world, people DO pass constants and expressions to routines that modify the argument.
This is only because the old compilers had by default the behavior now determined (in ifort) by -assume noprotect_constants, i.e. using a temporary memory location for them. Otherwise those programs would have never worked.
I came across this behavior the first time I ported my code to the Lahey Compiler, as I said. This kind of problem and the mechanism of the modification of the constants became then clear to me. And at that time I learned that the concept of "protection" of the constant argument against the modification done by the called procedure was implemented using a copy of it and, ALAS !, only using a specific switch ( --pca : protect constant arguments ).
The most efficient way to deal with this is to put the constant pool in a read-only image section - typically the same one that compiled code uses. There's no overhead for this, but if the called routine tries to modify the value an error will be raised by the OS.
As noted in my article you cited, the first version of Digital Visual Fortran did not do this, so you could modify the value of 3. When we added the read-only protection in version 6, many users complained that their programs stopped working, as they had been inadvertently modifying constant arguments. So we added the switch to do what the compiler already did if you passed an expression - it made a stack copy of the constant and passed the address of the copy. That way any (non-standard) writes to the value would be discarded on exit. This costs some extra instructions, so is not the default.
...In the real world, it is not always a solution to tell a customer that their code is broken. They'll say that it works with compilerx X, Y and Z and what's wrong with Intel? This is why we sometimes offer a switch, such as -assume noprotect_constants, to allow their bad code to continue working.
This is what I would disagree upon, being on the user's side of the barricade.
- Passing a constant argument to a subroutine which MAY modify that argument is not "inadvertently modifying constant arguments": it is just relying on a certain behavior of the compiler, i.e. working on a copy of the argument and discard it when returning. It was done consciously at that time and using the contemporary compilers.
- To me, a constant is just the most simple possible form of expression, and there is no reasonable motivation for a compiler not to deal with it as with more complex expressions, when used as an argument. I can understand "constant pooling", but why not limit it to the constants not used as arguments ?
- The "non-standard" writes would never occur if spending those extra instructions just when they are anyway needed to allow the program keep on working.
- Bad programs ? if you mean "out of date", ok, but they were not bad when were written. And legacy programs ar a big reality.
- Then, I really cannot understand why this was not assumed as the default, so that users would not have their program broken, nor spend time searching for errors caused by different behaviors of the compilers. Does that gain of efficency (???) worth the users' troubles ?
- Finally, the name of the switch was perhaps not the best choice, referring to "constants" in general and not to the "constant arguments" - but in this, of course, I am biased by the previous experience with the Lahey compiler ... , and is also a very secondary issue (which however mislead you too ....)
I repeat: this is just a user's point of view. Perhaps the compilers developers have different main targets.
I am just saying that I would have preferred -assume noprotect_constants being the default (or even the unique possible) behavior of Intel Compiler, as I would have preferred --pca being the default (or even the unique possible) behavior of Lahey Compiler : because I really cannot see any use in the opposite choice !
In case the opposite choice could allow a superior efficency of the compilation process, it might have been a "compiling-optimization flag, potentially changing the semantic of the program".
Thanks for your attention.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I know of many compilers over the years which allowed constants to be "modified" and the changes NOT discarded. As I mentioned, the default behavior works for correct programs and is higher performance.
I do frequently encounter user programs that "happened to work" on implementation X and the programmer therefore assumed it would work everywhere. That is not in itself a reason to slow down all applications because some users have written incorrect programs.
To my mind, it is a service to bring the error to the programmer;s attention, as we do, and then offer an alternative should they choose not to correct their program.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
While it is true that there is no guarantee that an INTENT(INOUT) argument will be modified, the language rules do require that the corresponding argument be definable, so an error message for an actual argument that is a constant or an expression is definitely permitted. Indeed, I suspect it is required when conformance reports are requested.
The case where a compile-time error is not possible (or, at least, possible only when compile-time analysis can be used to predict a run-time error) is for the case of unspecified intent (i.e., when no INTENT attribute is specified). Many people wrongly believe that unspecified intent is the same as INTENT(INOUT). They are similar, but the restrictions that apply to them are subltly different. An unspecified intent argument is required to be definable only in this cases where the argument is actually modified (i.e., a run-time restriction), whereas INTENT(INOUT) is required to be definable whether or not the argument is modified in this particular execution of the procedure.
[For those who do not speak standardese, "definable" is the standard's word for a variable which can be modified.]
-Kurt
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Passing a constant argument to a subroutine which MAY modify that argument is not "inadvertently modifying constant arguments": it is just relying on a certain behavior of the compiler, i.e. working on a copy of the argument and discard it when returning. It was done consciously at that time and using the contemporary compilers.
- To me, a constant is just the most simple possible form of expression, and there is no reasonable motivation for a compiler not to deal with it as with more complex expressions, when used as an argument. I can understand "constant pooling", but why not limit it to the constants not used as arguments ?
- The "non-standard" writes would never occur if spending those extra instructions just when they are anyway needed to allow the program keep on working.
I haven't slightest idea what this sentence is trying to say.
- Bad programs ? if you mean "out of date", ok, but they were not bad when were written. And legacy programs ar a big reality.
- Then, I really cannot understand why this was not assumed as the default, so that users would not have their program broken, nor spend time searching for errors caused by different behaviors of the compilers. Does that gain of efficency (???) worth the users' troubles ?
- Finally, the name of the switch was perhaps not the best choice, referring to "constants" in general and not to the "constant arguments" - but in this, of course, I am biased by the previous experience with the Lahey compiler ... , and is also a very secondary issue (which however mislead you too ....)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve said that -assume noprotect_constants was not the default because it costs extra instructions, adding efficiency reasons to standard compliance motivations.
I was just trying to say that, to make the program work, extra instructions are anyway needed, because the constant value must anyway be assigned to a variable. And, done by the compiler (using the appropriate flag) or because explicitely coded, perhaps it would make a limited difference on the efficiency of the resulting code.
But actually I do not have enough information to know if this is correct or not. It was just a guess.
On the other hand, I definitively agree with you that removing the non standard usage of constant arguments is the ultimate solution, and, as I said in one of my first post in this discussion, this is what I am doing.
Thanks again for your time.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page