• Re: modifying constants in Fortran and elsewhere

    From Thomas Koenig@tkoenig@netcologne.de to comp.compilers on Sat Jul 15 10:57:48 2023
    From Newsgroup: comp.compilers

    gah4 <gah4@u.washington.edu> schrieb:
    A potential bug since the earliest days of Fortran is passing a
    constant to a subroutine, and then changing the value of the dummy
    argument.

    In at least some Fortran system, this modifies the value of a constant
    used other places in a program.

    Could come in handy if the value of PI should change during the
    excecution of the program :-)

    This is a consequence of the standard /360 calling convention.
    Both arguments and local variables were put in close proximity to
    the code, if posssible within the range of a base register. It was
    all read-write, and the compiler optimized duplicate constants.
    (The explanation above is only for non-reentrant code, which was
    the usual case for FORTRAN, but they could be made to use reentrant
    code using a compiler option).

    As this was known when PL/I was designed, it is defined such that
    modifiable constants are passed to called procedures. C avoids it by
    not allowing the & operator on constants. (Though K&R allows
    modification of string constants.)

    You can still try by passing a pointer to a const variable, but
    chances are you will get a segfault when you try to modify it.

    Somehow, in all the years, that feature was never added to Fortran.

    Fortran has the VALUE attribute for dummy variables now, which
    generates a local copy of the variable. Compilers differ on how
    they implement it; passing VALUE arguments as actual value, like
    C usually does, or passing a pointer and making a local copy are
    both valid choices.

    It is easy to write programs and test for it, but I wonder if there
    are any stories for real program that had this bug, and even better,
    stories about the difficulty of finding it, or problems caused by it.

    I actually got bitten by that while using a mainframe for scientific
    work as a student. It's been a few decades, so I don't recall too
    many details. It was difficult to find, but I was paid by the hour,
    so I didn't mind too much :-)
    [The constant stomping issue far predates S/360. As soon as Fortran II
    added subroutines on the 704, there were constant arguments you could
    change by mistake. The problem is that it took quite a while for people
    to sort out the differences among call by reference, call by value,
    and call by copy in/out. Fortran on the 70x and S/360 user reference
    for array arguments, copy in/out for scalars. Algol 60 tried to define
    its argument passing in an elegant way, and accidentally invented call
    by name when they meant call by reference. -John]
    --- Synchronet 3.21b-Linux NewsLink 1.2
  • From Hans-Peter Diettrich@DrDiettrich1@netscape.net to comp.compilers on Sun Jul 16 11:56:27 2023
    From Newsgroup: comp.compilers

    On 7/15/23 12:57 PM, Thomas Koenig wrote:
    gah4 <gah4@u.washington.edu> schrieb:
    A potential bug since the earliest days of Fortran is passing a
    constant to a subroutine, and then changing the value of the dummy
    argument.

    I actually got bitten by that while using a mainframe for scientific
    work as a student. It's been a few decades, so I don't recall too
    many details. It was difficult to find, but I was paid by the hour,
    so I didn't mind too much :-)

    I remember such a bug in the 80s on the pdp-11/34 of our institute.


    Algol 60 tried to define
    its argument passing in an elegant way, and accidentally invented call
    by name when they meant call by reference. -John]

    By Name example from the German wikipedia <https://de.wikipedia.org/wiki/Namensparameter>


    A[1] := 10
    A[2] := 20
    i := 1
    x := Funkt (A[i], i)

    FUNCTION Funkt (a, i) : Real
    i := i+1
    RETURN a
    END

    x wird der Wert 20 zugewiesen. Im Werteparameter-Fall w|nre x dagegen 10, ebenso bei Referenzparametern.
    <<
    Returns 20 as ByName while 10 were returned for ByRef.

    DoDi
    [Then there's the infamous Knuth Man or Boy program, where even he
    couldn't figure out what the answer was supposed to be. https://en.wikipedia.org/wiki/Man_or_boy_test
    -John]
    --- Synchronet 3.21b-Linux NewsLink 1.2
  • From Thomas Koenig@tkoenig@netcologne.de to comp.compilers on Sun Jul 16 13:08:07 2023
    From Newsgroup: comp.compilers

    Our esteemed moderator wrote:

    [The constant stomping issue far predates S/360. As soon as Fortran II
    added subroutines on the 704, there were constant arguments you could
    change by mistake. The problem is that it took quite a while for people
    to sort out the differences among call by reference, call by value,
    and call by copy in/out. Fortran on the 70x and S/360 user reference
    for array arguments, copy in/out for scalars.

    I just have the IBM System/360 Operating System FORTRAN IV (H)
    Programmer's Guide, Fourth Edition, open (isn't Bitsavers great?).

    It states, on page 108

    Argument List

    The argument list contains addresses of
    variables, arrays, and subprogram names
    used as arguments. Each entry in the argu-
    ment list is four bytes and is aligned on a
    full-word boundary. The last three bytes
    of each entry contain the 24-bit address of
    an argument. The first byte of each entry
    contains zeros, unless it is the last entry
    in the argument list. If this is the last
    entry, the sign bit in the entry is set on.

    So, apparently no copy in/out on that particular compiler, at least.
    It also shows the (ab)use that was possible of the uppermost byte,
    because clearly 24 bits are enough for everybody and for all
    time, right? :-)
    [See the 360 Fortran IV Language manual, where on pages 90-95 it explains
    copy in/out and how to avoid it by putting slashes around the dummy
    argument names.

    https://bitsavers.org/pdf/ibm/360/fortran/C28-6515-7_System_360_FORTRAN_IV_Language_1966.pdf

    I was there, I actually used this stuff. Re abuse of the upper byte, as the size of
    OS/360 exploded way past what they expected, programmers were under pressure to make
    every bit and byte count, hence overloading the high byte. -John]
    --- Synchronet 3.21b-Linux NewsLink 1.2
  • From gah4@gah4@u.washington.edu to comp.compilers on Sun Jul 16 19:09:36 2023
    From Newsgroup: comp.compilers

    On Sunday, July 16, 2023 at 9:43:20rC>AM UTC-7, Thomas Koenig wrote:

    (snip)

    I just have the IBM System/360 Operating System FORTRAN IV (H)
    Programmer's Guide, Fourth Edition, open (isn't Bitsavers great?).

    It states, on page 108

    Argument List

    The argument list contains addresses of
    variables, arrays, and subprogram names
    used as arguments. Each entry in the argu-
    ment list is four bytes and is aligned on a
    full-word boundary. The last three bytes
    of each entry contain the 24-bit address of
    an argument. The first byte of each entry
    contains zeros, unless it is the last entry
    in the argument list. If this is the last
    entry, the sign bit in the entry is set on.

    So, apparently no copy in/out on that particular compiler, at least.
    It also shows the (ab)use that was possible of the uppermost byte,
    because clearly 24 bits are enough for everybody and for all
    time, right? :-)

    Soon after I started learning OS/360 Fortran, I found the LIST compiler option.

    That gives a listing, sort-of, of the generated assembly code.
    (Not good enough to actually assemble, but useful to figure out
    what it actually does.)

    First, OS/360 Fortran, like just about all the others at the time, does
    all static allocation. The MAP option gives a listing of all the variables used, and their addresses.

    Each subroutine has a prologue, which copies all scalar variables to
    the address indicated in the map, and an epilogue which copies them
    back.

    Variables are stored just after the executable code, and so addressable
    with the same base register, as long as it fits in 4K bytes.
    (Or two or three base registers for 8K or 12K bytes.) Once copied,
    they are easily directly addressed.

    Otherwise, to use one, the address is loaded into a register, and then
    used, requiring usually two instructions. And two instructions to
    copy each in, and two more to copy back out. So any variable referenced
    more than four times, is faster with the copy.

    And yes, you can put the dummy argument between slashed, in which case
    it won't do that.

    There is one reason given by IBM. A DIRECT ACCESS statement, used with
    a direct access file, keeps a variable with the record number of the next record, in case you want to read sequentially. If you need to reference that from a called subroutine, you need the slashes.

    There are a few other cases involving variables in COMMON, such that a
    variable could change at an unusual time.

    In any case, the Fortran standard, since Fortran 66 allows for this.

    I didn't know until the above note from or moderator that went back
    to the IBM 704, where Fortran originated. I don't know the addressing
    modes of the 704, and how or why that helped.

    The only other compiler that I know in that much detail is Fortran-10
    for the PDP-10. The PDP-10 has an indirect bit on addresses, such that
    the processor will indirectly address with one instruction, though likely
    the time of two instructions.

    It is only more recently, with non-contiguous assumed shape arrays,
    that this came back again. Fixed size and assumed size arrays are
    assumed by the called routine to be contiguous. If a routine with
    an assumed shape argument, calls another with assumed size,
    it is usual to make a copy and pass that. Then copy back again
    on return. In that case, it is the caller that does the copy, not the
    callee, as for IBM.
    [The 70x series had 15 bit word addressing with index registers. Even
    though they mostly did sign/magnitude arithmetic, indexing worked by
    doing a two's complement subtract of the index register from the
    address in the instruction. (I've seen lots of guesses why they did
    that, but never found an actual source.) So Fortran arrays were stored
    in reverse order starting in high memory. 70x Fortran did not have
    the /X/ argument specifier and I cannot find anything in the manuals
    about argument aliasing, although the calling sequence example in the
    FAP assember program shows how to copy in arguments.

    The Fortran manual does say:

    A constant may not appear as an argument in the
    call to a SUBROUTINE or FUNCTION subprogram if
    the corresponding dummy variable in the definition
    of the subprogram appeared either on the let side of an
    arithmetic statement or in an input list.

    -John]
    --- Synchronet 3.21b-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.compilers on Mon Jul 17 13:09:43 2023
    From Newsgroup: comp.compilers

    On 11/07/2023 04:42, gah4 wrote:
    A potential bug since the earliest days of Fortran is passing a
    constant to a subroutine, and then changing the value of the dummy
    argument.

    In at least some Fortran system, this modifies the value of a constant
    used other places in a program.

    As this was known when PL/I was designed, it is defined such that
    modifiable constants are passed to called procedures. C avoids it by
    not allowing the & operator on constants. (Though K&R allows
    modification of string constants.)

    Somehow, in all the years, that feature was never added to Fortran.

    It is easy to write programs and test for it, but I wonder if there
    are any stories for real program that had this bug, and even better,
    stories about the difficulty of finding it, or problems caused by it.

    I don't think any language can beat Forth here:

    1 .
    1 ok
    1 1 + .
    2 ok

    : 1 2 ;
    ok
    1 .
    2 ok
    1 1 + .
    4 ok

    If you want to redefine the meaning of a number, you just define it like
    any other identifier.
    --- Synchronet 3.21b-Linux NewsLink 1.2
  • From gah4@gah4@u.washington.edu to comp.compilers on Mon Jul 17 10:51:56 2023
    From Newsgroup: comp.compilers

    (snip, our moderator wrote)

    [The 70x series had 15 bit word addressing with index registers. Even
    though they mostly did sign/magnitude arithmetic, indexing worked by
    doing a two's complement subtract of the index register from the
    address in the instruction. (I've seen lots of guesses why they did
    that, but never found an actual source.) So Fortran arrays were stored
    in reverse order starting in high memory. 70x Fortran did not have
    the /X/ argument specifier and I cannot find anything in the manuals
    about argument aliasing, although the calling sequence example in the
    FAP assember program shows how to copy in arguments.

    I have known for a long time that the 704 (and I believe later 7.. machines) indexes arrays backwards, and allocates from the end of memory.

    Allocating COMMON from the end of memory is convenient, in that is the
    one place (other than the beginning) where you know will always by the same.

    Indexing back means that it will still work, even with different length COMMON.

    One Fortran feature that I only recently found out about, from 1130 ECAP,
    is CALL LINK.

    A program can CALL LINK(other program name), and replace itself with
    another executable program. Not a subroutine call, but the whole program
    is replaced in memory. But COMMON blocks are kept!

    (That might be before subroutine overlay that I know well.)

    By the time of OS/360, that seems to have disappeared, but a fancy
    subroutine overlay system then appeared.

    I any case, allocating from the end of memory, and indexing backward,
    is convenient for aligning data in different whole executable programs.

    Maybe they were just lucky.
    [The 70x IBSYS had what they called chain jobs, same idea that the
    code was overwritten but the common data stayed. The 360 linker had an elaborate system of tree structured overlays that let you divide the
    code into segments loaded automatically when the program called a
    routine in the segment, again leaving whatever was in the root
    segment, typically including common blocks. You can read all about it
    in Chapter 8 of "Linkers and Loaders", available from better
    booksellers everywhere. -John]
    --- Synchronet 3.21b-Linux NewsLink 1.2