How does Fortran know the Inputs and Outputs of Subroutines

In summary, the routine square_cube takes three input arguments: an integer, an intent(in) to indicate whether the argument is an input or output, and a real(8) to hold the result of the computation. It also takes three output arguments: an intent(out) to
  • #36
Another note on the common block, it was used in cases where there were large numbers of variables where it became painful to pass them all in a subroutine call. Doing it the functional way i.e. Not the common block way would often take a lot more time to run the program. In other words common blocks could speed up computationally intense programs due to the register indirection used in subroutine calls.

When common block became hell was when you had the same common block referenced in two or more subroutines use different names for the same variables. Invariably someone would add a new variable in the middle of the block and forget to add it to all the other same named blocks they would use fortrans implicit datatyping convention meaning one block thought it was an integer and another thought it was a float.

Common blocks are a primitive version of the union+structs used in C code for low level data manipulation.
 
Technology news on Phys.org
  • #37
ecastro said:
The output is strange.
The one I posted in #34 or the one you didn't :smile: ?
I got everything correct for the first part of the program (before the apparent reflectance values). I am now able to produce a numerical value, but an incorrect one, way too large compared with the ones shown in the manual.
Value for what ? The integrated apparent reflectance 0.0330946 ?
It would seem that the main problem comes from the ISO subroutine; I replaced the 'continue's with 'break's, so the iteration breaks rather than continue to the next loop, which I have corrected.
This is hard to follow. There's a lot of loops in that routine. Concrete code ? Is this in fortran or in MATLAB ?
Re: Stack size
What do you mean by this? I am using the MatLab software, by the way.
The arguments for fortran subroutine calls all go on the stack, so if you are a dozen levels deep and the arguments are huge double precision arrays you can run out of stack space.
I am using the MatLab software, by the way.
Are you saying you ported some 2 MB of fortran to MATLAB 'by the way' ?

[edit]PS In the output I posted in #34 I found the 11.44 for Chi (surface Polarization) rather suspect. It's due to a dangerous bug: main.f reads in inhomo at label 30 and jumps to label 31 if it's > 0. idirec is not initialized in that case, but it is checked later on: after label 37 its uninitialized value (!) determines if ropq and ropu (surface Polarization Q and U) are initialized to 0 or not. And in the not case it continues to do things based on the also uninitialized value of ibrdf .
Basically you are in the other branch of the tree and don't care much (unless some xx variable gets a value you later break your neck over ) but it looks soooo ugly ! (Someone should alert the authors about this ?).
 
Last edited:
  • #38
BvU said:
The arguments for fortran subroutine calls all go on the stack, so if you are a dozen levels deep and the arguments are huge double precision arrays you can run out of stack space.
In the compilers and machine architecture I am familiar with, Fortran uses call by reference and static allocation for local storage. There is nothing to cause excessive stack utilization. However, my experience is decades old. Possibly there are re-entrant Fortran implementations today with stack-allocated local storage.
 
  • #39
Hi JB, I'm a VAX Fortran 77 native speaker too, but nowadays only have access to an Intel compiler on Windows. It appears to use the stack for actual arguments and the default size is only 1 MB. SInce upping it solved my problem I'm inlclined to believe the documentation.
 
  • Like
Likes jbriggs444
  • #40
BvU said:
The one I posted in #34 or the one you didn't :smile: ?

It is the 34th post.

BvU said:
Value for what ? The integrated apparent reflectance 0.0330946 ?

Yes, and also the apparent radiance. I have values reaching magnitudes of 10, if I remember correctly.

BvU said:
This is hard to follow. There's a lot of loops in that routine. Concrete code ? Is this in fortran or in MATLAB ?

Yes, I re-checked the code and there seems to loops in my MatLab code that are wrong. I will try to correct this first before sharing the code.

BvU said:
PS In the output I posted in #34 I found the 11.44 for Chi (surface Polarization) rather suspect. It's due to a dangerous bug: main.f reads in inhomo at label 30 and jumps to label 31 if it's > 0. idirec is not initialized in that case, but it is checked later on: after label 37 its uninitialized value (!) determines if ropq and ropu (surface Polarization Q and U) are initialized to 0 or not. And in the not case it continues to do things based on the also uninitialized value of ibrdf

I also encountered this 'check', the 'idirec' variable has no value. However, I have set to zero, and the values I had seemed correct.
 
  • #41
I have another question regarding this. The subroutine 'atmref' calls the 'opsol' subroutine with three of its outputs have the same name (the first call of the subroutine, for example), 'rolutd'. In the code of the 'opsol' subroutine, these three outputs are three different variables, so which one of these will have be considered 'rolutd'?
 
  • #42
ecastro said:
I have another question regarding this. The subroutine 'atmref' calls the 'opsol' subroutine with three of its outputs have the same name (the first call of the subroutine, for example), 'rolutd'. In the code of the 'opsol' subroutine, these three outputs are three different variables, so which one of these will have be considered 'rolutd'?
All three.

The preferred terminology for the variables passed in a procedure call is "actual parameters" (referring to what is placed into the parameter list by the caller) and "formal parameters" or "dummy parameters" (referring to the parameter names used by the subroutine).

If you use 'rolutd' three times as an actual parameter and if the corresponding formal parameters in the subroutine are, for instance, 'a', 'b' and 'c' then a, b and c will refer to the same block of storage. [Here I assumed call by reference. If rolutd is a scalar variable rather than an array and if the compiler uses copy-in/copy-back semantics for scalars then you would have three different [notional] memory locations populated with the same initial value].

Note that if the actual parameters are variables or array elements (as opposed to being a whole array) and if the subroutine modifies anyone of the corresponding formal parameters then the standard forbids you to use the same actual parameter more than once. Without having read the relevant section of the standard or the minds of its creators, the idea is to give maximum flexibility to the compiler writers when choosing a parameter passing mechanism. Call by reference, and call by value with copy-in/copy-back would give different results if the programmer did that. So the standard forbids the programmer to do that -- or declares that the results of doing so are "undefined" or that a program that depends on the results is "erroneous".

See the section on "aliasing of dummy arguments" in https://www.ibiblio.org/pub/languages/fortran/ch1-8.html
 
  • #43
I am a bit lost. So, for example I have a subroutine ABC with dummy parameters as (x, y, z) and more or less looks like this:

subroutine ABC (x, y, z)
...
x = 1
y = 2
z = 3
...
end

Then this subroutine is called in the main program,

call ABC (d, d, d)

Then, what is the value of 'd'?
 
  • #44
ecastro said:
I am a bit lost. So, for example I have a subroutine ABC with dummy parameters as (x, y, z) and more or less looks like this:

subroutine ABC (x, y, z)
...
x = 1
y = 2
z = 3
...
end

Then this subroutine is called in the main program,

call ABC (d, d, d)

Then, what is the value of 'd'?
Undefined. You have written an illegal program.
 
  • #45
Even if 'x', 'y', and 'z' are arrays in the subroutine?
 
  • #46
ecastro said:
Even if 'x', 'y', and 'z' are arrays in the subroutine?
How are you going to make sense of the statement: "x = 1" in such a case?
 
  • #47
ecastro said:
I am a bit lost. So, for example I have a subroutine ABC with dummy parameters as (x, y, z) and more or less looks like this:

subroutine ABC (x, y, z)
...
x = 1
y = 2
z = 3
...
end

Then this subroutine is called in the main program,

call ABC (d, d, d)

Then, what is the value of 'd'?

jbriggs444 said:
Undefined. You have written an illegal program.
I beg to differ. This is a perfectly valid call in Fortran, and the value of d after the call will be 3. All three variables, x, y and z, point to the same memory location and the instructions are expected to be executed in order :smile:

This no different from
Fortran:
equivalence (x, y), (x, z)
call ABC (x,y,z)

That said, if ABC does something else to/with x, y, and z, the outcome can become undefined.
 
Last edited by a moderator:
  • #48
DrClaude said:
I beg to differ. This is a perfectly valid call in Fortran, and the value of d affecter the call will be 3. All three variables, x, y and z, point to the same memory location and the instructions are expected to be executed in order :smile:
I am unable to locate any section in the standard which explicitly declares this usage improper. However, I would not want to trust that a compiler would produce that result without having tested it first.

Optimizing compilers can make unexpected choices. With separate compilation, the compiler for the subroutine may not be aware that its dummy parameters are aliases for the same actual parameter.

Edit: Found it.

https://www.fortran.com/F77_std/rjcnf0001-sh-15.html#sh-15.9.3.6

15.9.3.6 Restrictions on Association of Entities.
If a subprogram reference causes a dummy argument in the referenced subprogram to become associated with another dummy argument in the referenced subprogram, neither dummy argument may become defined during execution of that subprogram. For example, if a subroutine is headed by
SUBROUTINE XYZ (A,B)
and is referenced by
CALL XYZ (C,C)
then the dummy arguments A and B each become associated with the same actual argument C and therefore with each other. Neither A nor B may become defined during this execution of subroutine XYZ or by any procedures referenced by XYZ.
 
Last edited:
  • #49
I agree with @DrClaude. Inside the ABC routine, x is set to 1, causing d in the main program to be set to 1. Then y is set to 2, causing d to be reset to 2. Then z = set to 3, causing d to be reset again to 3.
 
  • #50
Mark44 said:
I agree with @DrClaude. Inside the ABC routine, x is set to 1, causing d in the main program to be set to 1. Then y is set to 2, causing d to be reset to 2. Then z = set to 3, causing d to be reset again to 3.
The standard disagrees.
 
  • #51
jbriggs444 said:
The standard disagrees.
Where?

Fortran 90 standard said:
2.3.4 Execution sequence

Execution of an executable program begins with the first executable construct of the main program. The execution of a main program or subprogram involves execution of the executable constructs within its scoping unit. When a procedure is invoked, execution begins with the first executable construct appearing after the invoked entry point. With the following exceptions, the effect of execution is as if the executable constructs are executed in the order in which they appear in the main program or subprogram until a STOP, RETURN, or END statement is executed. The exceptions are:

  1. (1) Execution of a branching statement (8.2) changes the execution sequence. These statements explicitly specify a new starting place for the execution sequence.
  2. (2) IF constructs, CASE constructs, and DO constructs contain an internal statement structure and execution of these constructs involves implicit (i.e., automatic) internal branching. See Section 8 for the detailed semantics of each of these constructs.
  3. (3) Alternate return and END=, ERR=, and EOR= specifiers may result in a branch.
Internal subprograms may precede the END statement of a main program or a subprogram. The execution sequence excludes all such definitions.
(emphasis added)
 
  • #54
I also stand corrected. Thanks!
 
  • #55
Here's how C deals with a very similar situation.
C:
// Test.c
#include <stdio.h>
void func(int *, int *, int *);

int main(void)
{
   int y = 1;
   func(&y, &y, &y);
   printf("y = %d", y);
   return 0;
}

void func(int * a, int * b, int * c)
{
   *a = 2;
   *b = 3;
   *c = 4;
}
Output: y = 4
 
  • #56
ecastro said:
I have another question regarding this. The subroutine 'atmref' calls the 'opsol' subroutine with three of its outputs have the same name (the first call of the subroutine, for example), 'rolutd'. In the code of the 'opsol' subroutine, these three outputs are three different variables, so which one of these will have be considered 'rolutd'?
I agree with @jbriggs444 that the standard doesn't allow it. But my fortran compiler (Intel visual fortran 2011) let's it pass and indeed, OSPOL (not opsol) is called twice as you describe. The effect -- IF the code to write to the rolut(m,l) is executed -- is as @DrClaude describes and the lookup table would get completely wrong values. a disastrous bug. However, in the example we use,
Fortran:
C Look up table generation
      do m=1,mu
      do l=1,nfilut(m)
      phimul=filut(m,l)*pi/180.
      rolut(m,l)=rolut(m,l)+delta0s*i3(m)*cos(is*(phimul+pi))
      rolutq(m,l)=rolutq(m,l)+delta0s*q3(m)*cos(is*(phimul+pi))
      rolutu(m,l)=rolutu(m,l)+delta0s*u3(m)*sin(is*(phimul+pi))
      enddo
      enddo
C end of look up table generation
nfilut(m) is all zeroes in all calls from atmref: it looks as if the reference table is not used for this set of inputs. Pretty inefficient (or a bug!) :rolleyes:

I can't oversee what the first two calls to ospol from atmref are supposed to do and if they should be the same as the third one, or instead need dummy arrays (of the proper size) as the last two arguments.
 
  • #57
@ecastro : my advice is to get into contact with the authors. If they published the stuff in 2015 that means there are still users and perhaps also developers working with/on this stuff. If I were one of them, I would be extremely grateful for this kind of detailed feedback. And perhaps they will also be interested in what you are trying to make !
 
  • #58
Thank you all for your help. I also wanted to contact the authors, but I wanted to confirm if it is a programming error or if I understand how Fortran works. I just found out that 'rolutd' is not used in 'atmref' (like a dummy output), that is maybe why the compiler let it pass?
 
  • #59
ecastro said:
Thank you all for your help. I also wanted to contact the authors, but I wanted to confirm if it is a programming error or if I understand how Fortran works. I just found out that 'rolutd' is not used in 'atmref' (like a dummy output), that is maybe why the compiler let it pass?
Compilers do not detect all standards violations. Their job is simpler than that -- to take compliant programs and generate object code that will produce compliant results.

In the development environment that I am familiar with, the Fortran compiler would (or could) do separate compilation of each subroutine. When compiling 'atmref', the compiler would have knowledge of where and how 'atmref' will be called. So the compiler would not generate a compile time error message for 'atmref'. When compiling the caller of 'atmref', the compiler would have no knowledge of the inner workings of 'atmref'. So the compiler would not generate a compile time error message when compiling the caller.

It would be possible for the compiler to build in run time checks to detect and flag attempts to modify aliased dummy parameters. But the standard does not require that this be done. The standard does not specify the required behavior when running non-compliant code.

However, it is also plausible that your re-examination of the code is correct, that there is no attempt to modify an aliased dummy variable and that this particular standards violation does not exist in the code.
 
  • Like
Likes ecastro
  • #60
I would just like to chip into contest the frequently asserted claim that "Fortran is a language where calls are intrinsically by-reference", by "referring" to a blog post by Steve Lionel a.k.a. Doctor Fortran (formerly?) of Intel. https://software.intel.com/en-us/blogs/2009/03/31/doctor-fortran-in-ive-come-here-for-an-argument

On a fundamental level, argument association is pretty simple. Although most people would tell you that Fortran uses "pass by reference", meaning that the address of the actual argument is used for the dummy argument, that's not quite true. The standard says that argument association is "usually similar to call by reference" and then adds "The actual mechanism by which this happens is determined by the processor." [Fortran 2003 Note 12.22]

There are some cases where what actually gets passed is not the original argument. The most common case for this is when a non-contiguous array pointer or a array slice is passed to an argument for which the compiler does not see an explicit interface specifying that the corresponding dummy argument is an assumed-shape array. Because the called routine is expecting a contiguous array, the compiler needs to make a copy of the actual argument, pass the copy, and then copy back any changes. The compiler can warn you about this at run-time if you use the option /check:arg_temp_created (Windows) or -check arg_temp_created (Linux/Mac OS). The compiler will generate a run-time test to see if the argument is actually contiguous and skip the copy if it is.

He goes on to give some more examples then emphasises the importance of explicitly declaring INTENT, but I think the point is clear.
 

Similar threads

Replies
3
Views
2K
Replies
25
Views
2K
Replies
8
Views
4K
Replies
4
Views
2K
Replies
5
Views
7K
Replies
10
Views
9K
Replies
5
Views
3K
Back
Top