Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Fortran How does Fortran know the Inputs and Outputs of Subroutines

  1. Jan 23, 2017 #1
    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?

    Thank you in advance.
     
  2. jcsd
  3. Jan 23, 2017 #2

    BvU

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    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.
    [edit] 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
  4. Jan 23, 2017 #3

    Vanadium 50

    User Avatar
    Staff Emeritus
    Science Advisor
    Education Advisor

    Yes. This is a feature of the language.
     
  5. Jan 23, 2017 #4

    anorlunda

    User Avatar
    Science Advisor
    Gold Member

    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
  6. Jan 23, 2017 #5

    Mark44

    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
     
  7. Jan 23, 2017 #6

    BvU

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    Ha, I missed that in first reading. What's the goal: reverse engineering for technology, porting to a newer environment, ?
    [edit] and your source code dates from when, approximately ? Any knowlegde about the environment it was used in (IBM, DEC, CDC... ) ?
     
  8. Jan 24, 2017 #7
    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.
     
  9. Jan 24, 2017 #8

    BvU

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    2015 is very recent for a Fortran IV programmer :smile:


    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
  10. Jan 24, 2017 #9

    Mark44

    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.
     
  11. Jan 24, 2017 #10

    BvU

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

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

    Interesting case, isn't it ?
     
  12. Jan 24, 2017 #11

    jbriggs444

    User Avatar
    Science Advisor

    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.
     
  13. Jan 24, 2017 #12
    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?
     
  14. Jan 24, 2017 #13

    Mark44

    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.

    In call-by-reference what is actually passed in a function or subroutine call is the address of the parameter. "call sub(j)" is passing the address of j. In the sub subroutine, the parameter is declared as a three element array. Fortran arrays have indexes that normally start at 1, so the value for the parameter j would normally be at m(1). By accessing m(0), the subroutine has access to i (in the main program), and m(1) provides access to j (in the main program), and m(2) provides access to k (in the main program). The subroutine doesn't know about the identifiers i, j, and k, but it has access to them via the array m.
     
  15. Jan 24, 2017 #14
    This is problematic... So, it doesn't just change the input 'j' (presumably a single-valued integer value) to an array?
     
  16. Jan 25, 2017 #15

    Mark44

    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.
     
  17. Jan 25, 2017 #16

    BvU

    User Avatar
    Science Advisor
    Homework Helper
    Gold Member

    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 :rolleyes: (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 :smile:)
    JB example is malignant. Be optimistic (you must be) and give the original programmers the benefit of the doubt.
     
  18. Jan 25, 2017 #17

    FactChecker

    User Avatar
    Science Advisor
    Gold Member

    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.)
     
  19. Jan 25, 2017 #18

    jbriggs444

    User Avatar
    Science Advisor

    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.
     
  20. Jan 25, 2017 #19

    anorlunda

    User Avatar
    Science Advisor
    Gold Member

    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.
     
  21. Jan 25, 2017 #20

    FactChecker

    User Avatar
    Science Advisor
    Gold Member

    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
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted



Similar Discussions: How does Fortran know the Inputs and Outputs of Subroutines
Loading...