# Fortran How does Fortran know the Inputs and Outputs of Subroutines

Tags:
1. Jan 23, 2017

### ecastro

I have trouble understanding how Fortran knows the inputs and outputs of a subroutine. I am studying a code written in Fortran, but I do not have a compiler, so I cannot check how it works.

Here is my problem; for example, I have a subroutine,

subroutine test(x, y, z, a, b, c)

The variables x, y, and z have values in the main program and are needed in the subroutine, while a, b, and c are the supposed outputs. What troubles me are:
1. Should all six variables be defined in the main program and in the subroutine (defined to be real, integer, complex, etc.)?
2. Is it alright if I define (defined to be real, integer, complex, etc.) a variable in the subroutine and not in the main program? How about vice versa? What are the consequences of this action?
3. If x, y, and z somehow receive a new value in the subroutine, will the previous values be replaced in the main program (x, y, and z become part of the output)?
4. If there's a variable named d in the subroutine, will I be able to call it in the main program even though it's not part of the output?

2. Jan 23, 2017

### BvU

Yes, but they can have different names - the names are symbolic and serve as references
The fortran parameter passing mechanism is mainly by reference: the address of a variable is passed to the subroutine.
yes. no consequences. In some compilers you can not be sure a variable in a subroutine is static (has the same value in a call as it had been given in a preceding call)
A good environment will protect you against such an access violation, but I'm not 100% sure.
 Ignore. Confusion with newer languages on my part. Vanadium corrects me in post #2 and Anorlunda elucidates in #3.
F90 has some features in this direction
No. Other routines (including the main program) do not know where to find d

Last edited: Jan 23, 2017
3. Jan 23, 2017

Staff Emeritus
Yes. This is a feature of the language.

4. Jan 23, 2017

### Staff: Mentor

In older FORTRANs, all arguments are by-reference and inherently input and/or output. That is very simple, but it lacks safeguards against errors.

Consider this routine
Code (Fortran):

SUBROUTINE BAZFAX(X,Y,Z)
INTEGER X,Y,Z
Z=0
X=X/Y
RETURN
END

It appears that:
X is input and output
Y is input
Z is output
It is also possible to use IF statements to make it conditional whether an argument is used as input, output or both.

Now think what might happen with these calls
Code (Fortran):

CALL BAZFAZ(1,2,3)
CALL BAZFAZ(X*2,Y*3,SQRT(Z))
CALL BAZFAX(X,X,X)

The first two calls have no arguments suitable for outputs.
The last call will crash with divide by zero because in the SUBROUTINE, Y and Z refer to the same memory address.

The point is that the caller must know how the subroutine is written and which arguments are used as outputs, inputs, or both. You can get into all sorts of trouble if you do it wrong. That is why other languages include safeguards to prohibit some of these kinds of errors.

EDIT: I neglected to mention that you can also declare the types of arguments to be different than the types supplied in the CALL. Even incompatible types. You can also have an inconsistent count of the number of arguments between the caller and the routine.

All these things are consequences of a very simple model. Arguments are merely addresses of a memory region to use for whatever you want. FORTRAN compilers compile programs and subprograms separately, without any prior knowledge of the other, nor any macro-data left behind about the signature. That was the optimum strategy when computer resources were severely limited, and when compiling programs was nearly as difficult as executing them.

Last edited: Jan 23, 2017
5. Jan 23, 2017

### Staff: Mentor

Newer versions of Fortran (Fortran 90?) use syntax in the declaration of subroutine and function parameters that indicates whether a parameter should be considered input, output, or both.
Code (Fortran):

subroutine square_cube(i, isquare, icube)
integer, intent(in)  :: i             ! input
integer, intent(out) :: isquare, icube ! output
isquare = i**2
icube   = i**3
end subroutine square_cube

program xx
implicit none
integer :: i, isq, icub
i = 4
call square_cube(i, isq, icub)
print*,"i,i^2,i^3=",i,isq,icub
end program xx

6. Jan 23, 2017

### BvU

Ha, I missed that in first reading. What's the goal: reverse engineering for technology, porting to a newer environment, ?
 and your source code dates from when, approximately ? Any knowlegde about the environment it was used in (IBM, DEC, CDC... ) ?

7. Jan 24, 2017

### ecastro

Thank you for your comments and precautions on the subroutines. I have another question, can a subroutine access a variable that is not included as an input but defined in the main program?

I think more like porting to a newer environment, a checking if the code I am writing is correct. The code was last updated May 2015, but I have no knowledge of its environment.

8. Jan 24, 2017

### BvU

2015 is very recent for a Fortran IV programmer

A subroutine does not have access to the adresses (variables) the main program uses (unless they are passed to the subroutine as arguments).

However:
The issue I'm about to adress now requires distinction between Fortran 90 and predecessors like Fortran 77.
One of the gizmos in older Fortran that has both been very useful AND a horrifiying source of errors is the COMMON block. If that doesn't occur in your code, then you're lucky; if it does, then that's a way for the routines (in which such a common block is declared) to all access the same variables.

In fortran 90 that role is taken over (in a more structured way) by modules

I hope you know what you are doing. Porting is error-prone and you may well end up with a program that is less efficient. You sure the investment in a compiler and learning some mixed-language procedure calling isn't much more effective and time-saving ?

Last edited: Jan 24, 2017
9. Jan 24, 2017

### Staff: Mentor

As BvU notes, this is possible to do using a COMMON block. However, it's considered very bad practice, in any programming language, for a function or subroutine to access variables other than its own local variables or those passed as parameters.

10. Jan 24, 2017

### BvU

Dear Mark, OP has indicated he has to deal with whatever is in there already !

Interesting case, isn't it ?

11. Jan 24, 2017

### jbriggs444

If one is a tricky sort with either faith or inside knowledge about the storage layout in the main program, one could declare a parameter in a subroutine as an array and proceed to use array references to access variables not appearing in the parameter list.

e.g.
Code (Text):

integer i, j, k
call sub ( j )
end
[...]
subroutine sub ( m )
integer m(3)
m(0) = 1        ! Modifies i (and may require you to have compiled the subroutine with array bounds checking disabled)
m(1) = 2        ! Modifies j
m(2) = 3        ! Modifies k
end

This is, of course, very bad practice. In addition to violating the expectations of all sane code readers, an optimizing compiler could render the approach useless, making memory allocation order unpredictable, moving variables into registers or optimizing them away altogether. One of the virtues (and one of the problems) associated with using common blocks is that memory allocation order is constrained and the compiler is not permitted to make such optimizations.

With DEC Fortran-77 (and presumably others), one could make games like this somewhat more kosher by using directives like %loc(variablename) to obtain the address of a variable. The resulting address could then be passed to a subroutine. The compiler was smart enough to use the appearance of %loc(x) as a hint that x was volatile and subject to modification behind the compiler's back -- so that optimizations for that variable would not be attempted.

12. Jan 24, 2017

### ecastro

Unfortunately, there are multiple common blocks in the subroutine. In my case, there is this:

common /common_name/ x, y, z

This is in both the main program and in the subroutine. If I understand correctly on how these work, you can access the variables if 'common_name' was called inside the subroutine. Does this mean if the variables 'x', 'y', and 'z' changed values inside the subroutine, they will look like outputs? That is, if these variables were called after the subroutine, they will have new values, right?

I am almost done with the code, the problem is my output from my code is not what I expect.

@jbriggs444 I don't understand your example. There is a subroutine named 'sub' which has a single input 'm' (from the main program, the input is 'j'). How does it modify 'i' and 'k'? Isn't the output of your subroutine just modifies the input?

13. Jan 24, 2017

### Staff: Mentor

Or one that produces output that you don't expect. As BvU said, porting is error-prone...

Fortran is a call-by-reference language. This means that any subroutine or function can modify its parameters. In contrast, C and other languages that stem from C (such as C++ and others) are call-by-value languages, whereby a function cannot modify its parameters.

14. Jan 24, 2017

### ecastro

This is problematic... So, it doesn't just change the input 'j' (presumably a single-valued integer value) to an array?

15. Jan 25, 2017

### Staff: Mentor

No. The main program (the caller of sub()) knows about i, j, and k. It (main) doesn't know about m. The subroutine, sub(), knows about m, but doesn't know about i, j, or k. By passing addresses, which is how it works in Fortran and other call-by-reference languages, a subroutine doesn't have to know the names of variables, but can still have read or write access to them.

The caller (main program) sends the address of j as the argument to sub() in the call. The subroutine treats the address that is sent as an array with three elements: m(1), m(2), and m(3).

The value stored in the address that m(1) represernts is the value stored in j. Likewise, the value stored in the address that m(2) represents is the value stored in k.m(0) represents the memory address that comes before (i.e., lower in memory) has the value stored in i. This is my understanding of jbriggs888's example, which I'm pretty sure is what he was driving at.

16. Jan 25, 2017

### BvU

I see. These variable addresses are known in all routines that have the common declared. Note I use the word address -- the variable names may differ from routine to routine. Not good practice, but it happens.

Your understanding is correct. Such variables 'look like' outputs and also like inputs. They are common (their 'scope' spans the routines that declare the common block). You have to be careful to check which routine is responsible for e,g, initializing them.

Painted yourself in a corner. If it's a big program you could spend a long time debugging. If you are unlucky, not just your code but the original code as well. Do you have output from the old program to compare with ? What's the language you are porting to ? Can you restrict your debugging to a limited set of source code and give concrete examples ? Is it all very confidential ? Did you use automatic code converting ?
(there's a lot of very, very experienced fortranners about who'd love to help you by digging into this out of pure sentimentality )
JB example is malignant. Be optimistic (you must be) and give the original programmers the benefit of the doubt.

17. Jan 25, 2017

### FactChecker

Yes.
It thinks that m is an array and it knows the starting address of the array. So it can modify values from that address and higher when the index of m increases. (The case of m(0) => i is special and only if certain compiler options have been set. Usually the lowest index of a FORTRAN array is 1.)

18. Jan 25, 2017

### jbriggs444

Yes.

For the compiler that I used to use in the 1980's (VAX Fortran on VMS) one would have needed to compile with options like:

Code (Text):

$fortran test /check=nobounds$ link test
\$ run test

By default, the compiler would build in run time array bounds checking so that an attempt to access array element zero (one cell before the array started at element one) would throw an error at run time. I no longer remember if it was smart enough to throw the error at compile time when the index was a knowable compile time constant.

While one could declare an array as, for instance,

Code (Text):

subroutine sub ( m )
int m(0:2)
m(0) = 1
m(1) = 2
m(2) = 3

this would not accomplish the goal of accessing a memory location prior to the start of the array. The parameter passed in the argument list is interpreted as the address of the first array element based on the way the array is declared in the subroutine. As declared above, the first element of m is at index zero. The memory location one cell prior would be m(-1) and would still trigger an array bounds error, by default, if access were attempted.

19. Jan 25, 2017

### Staff: Mentor

It is worse than you imagine.

1. First, FORTRAN also had a feature called EQUIVALENCE. That allowed the programmer to create an indefinite number of aliases for the same memory locations, either COMMON memory or stack memory.
2. Second, it was regular practice to use COMMON in ways to mimic the use of "heap" storage in other languages. In a language like C, one could use x=malloc(1000) to reserve 1000 bytes of memory, and free(x) to release the memory block. In FORTRAN we might say COMON/BAZFAZ/I,J,K in one subprogram, COMMON/BAZFAZ/X(3) in another subprogram, and COMMON/BAZFAZ/A,B(2) in a third subprogram. In all three cases, the same memory block is referenced. That allowed memory to be reused for different purposes in different phases of the program, just like heap storage in other languages.
3. Third, in the days when FORTRAN had no INCLUDE feature, and before the time when text editors had copy/paste, or when we could store source code in files, the COMMON declaration in each subprogram had to be retyped by hand. A single character typo, could be enough to misalign the COMMON declarations leading to bugs that were extraordinarily hard to find. The typos did not cause compilation errors. More than once I helped hapless programmers proof read their COMMON declarations with great tedium, character-by-character subprogram-by-subprogram. With punch cards, you could reliably duplicate cards one-at-a-time but making 200 duplicates of a 100 card long COMMON declaration was its own kind of tedium and still gave lots of opportunities for errors.
4. There was one context where "global COMMON" was not evil. I worked on real time simulators where programs executed 10 to 50 times per second, and could not be slowed down or halted temporarily for debugging purposes. We made 100% of the variables global. Even local temps were made global. That made it possible to make real time debuggers that could capture data for debug purposes from any part of any program without changing the real time performance. It worked remarkably well, streamlining debugging and interprogram communications. Imagine just pointing a D-A converter to a memory address, and then to an oscilloscope or a chart recorder without changing any program code. The evil of global data comes when programmers forget which data is local and which is global, but if 100% is global, there is no confusion.
The OP faced with the task of maintaining a very old program is in a bad spot. Because of call by ref, COMMON and EQUIVALENCE, you can not dependably break down the task into program modules. You may need to examine every line of code in every subprogram for possible hidden interactions with other subprograms.

The SUBROUTINE and FUNCTION features in FORTRAN provided code reuse and separate compilation, but they did not provide modularity.

Thinking back on the constraints we lived with, it sounds impossible that we could achieve what we did, but we did make things work correctly.

20. Jan 25, 2017

### FactChecker

That being said, there were many times when common blocks were the only realistic way to get things done.
One further complication was that the same position in a common block could be used under different names in different subroutines. And a subroutine that needed only the 58'th and 68'th variables might get them by: common /xxx/ dummy1(57), var1, dummy2(9), var2. (Or something like that. My memory is vague on it.)
I had to write some programs to track down and cross-reference all the usage of common block data elements in large programs.

Last edited: Jan 25, 2017
21. Jan 29, 2017

### ecastro

It is indeed a long program. The code is from NASA, so I trust that it works. The code is not that confidential, it is actually available here: http://6s.ltdri.org/pages/downloads.html, and there is an example output in the user manual. I am learning the physics of the code, and I guess if I were able to replicate the code, I understand it well.

I am actually looking every line of code, and I haven't seen the references of @anorlunda and @FactChecker. As far as I have seen, every time the commons are declared, they have the same set of names and every variable is present, as to how they were declared in the beginning of the main program.

I came asking here because there is a variable, 'roatm', (an array to be specific) that was only defined in the main program, and by following the inputs of the code manual, the array has no values but it is used to calculate a quantity, which returns a not-a-number value.

I am also curious if I understand this method of Fortran correctly,

do 10 i = a, b
if (insert condition) goto 10
[other processes]
10 continue

If the condition is satisfied, the process should jump to the line 10. Then, does this continue to the next iteration, or does it break the iteration?

22. Jan 30, 2017

### FactChecker

Yes, it continues to the next iteration. Line 10 is considered to be still inside the "do loop". To jump out of the loop it has to goto a line other than 10, outside of the loop

23. Jan 30, 2017

### BvU

Dangerous ! But it looks decent, is pretty modern and doesn't look devious (no EQUIVALENCE statements).
That's rather roundabout (for the physics especially)
roatm(3,20) sits is in common /sixs_disc/ which is declared in DISCOM.f and in INTERP.f, as well as in main.f, mainlutareo.f and SPECINTERP.f

Values are written into it in DISCOM
Code (Fortran):

roatm(1,l)=rorayl
roatm(2,l)=romix
roatm(3,l)=roaero
In other words: a more specific question in post #1 would have given you this answer a long time ago ! But you have learned a lot about Fortran in the mean time, so it's not a waste.

24. Jan 30, 2017

### ecastro

Yes, the values of 'roatm' come from the DISCOM subroutine. However, the whole subroutine is under four conditions, two of the first conditions are for entering the subroutine while the last two skips it. It would seem that I misunderstood the process of the do-go-to loop. I thought that if the code were to have a go-to under a do-loop, the whole do-loop breaks, so I had thought that I missed something on the processes of subroutines.

Thank you for all your help, I hope this works now.

25. Jan 30, 2017

### FactChecker

Did the original NASA code generate NaNs, or was that your code?