Re: question about negative indices in fortran 77

From: *** Hendrickson (***.hendrickson_at_att.net)
Date: 12/29/04


Date: Wed, 29 Dec 2004 16:21:53 GMT


Jose Miguel Pasini wrote:
> Dear friends,
>
> I am working with an "inherited" program written in Fortran 77. The
> program works fine as it is (both on Compaq compilers for windows and
> Intel compilers for linux), but now I'm doing some modifications and I
> need to debug it. The problem is that some (I guess very old)
> subroutines use negative indices in arrays, so whenever I compile
> with Intel Fortran 8.1 for linux with -C option, the program stops at
> these calls and doesn't arrive at the interesting parts.
>
> I'm checking for violations of array bounds because the program is doing
> something really weird: a variable is changing its value inside a
> subroutine where it isn't touched (neither read nor written!). The only
> thing that I've seen trigger this kind of behavior in other languages
> has always been array bounds violations (even just reading forbidden
> memory).
Yes, that's always the most likely reason. It's possible to
also get subroutine arguments messed up, (maybe out of
order), but that's unlikely for an old working code.

>
> Now we arrive at the interesting part. Let's say I have the following
> declaration at the beginning of a subroutine:
>
> implicit real*8 (a-h,o-z)
> real*8 e(m*n),d(m*n),c(m*n),b(m*n),a(m*n),
> & ef(m*n),df(m*n),cf(m*n),bf(m*n),af(m*n),
> & x(m*n),y(m*n),r(m*n),up(m*n),aux(m*n)
>
> The way I understand it, an expression like d(-i) is equivalent to
> e(m*n-i).
No, not really. Fortran doesn't define what happens when
array subscripts go out of bounds. Many compilers will lay
out memory in the order arrays are declared. If they
do, then, in your case above, e is sort of equivalent to
d with negative out-of-bounds subscripts. BUT, there is
no guarantee or requirement that memory be assigned in
that order. Many optimizing compilers will lay out memory
in the order arrays are used (and never assign actual memory
for unused arrays), others will overlap arrays that are only
used in discontiguous parts of the program. And the
behavior usually depends on the optimization level,
compiler release, etc. There's no guarantee.

>Using this I can translate the whole subroutine into something
> more sensible that the debugger will allow. I actually did it with one
> subroutine, and the results are identical to what I obtained before the
> translation. Now I want to do the same thing to another subroutine that
> is very similar, but I have the following problem: there is a reference
> to e(-i), but since "e" is the first array in the declaration, where is
> the memory allocated for this?

There really isn't any specific memory. If the arrays
are on the stack, then e(-i) is mucking around with
something from it's callers stack memory (or even further
up the call chain if "i" is large enough). If the arrays
are statically assigned to memory, then this subroutines
memory probably comes just after the memory of whatever
subroutine was loaded before it, likely a pseudo-random
oeganization from your point of view. So what
happens will depend on the order subroutines are loaded
or called, and you just can't count on it.

>
> Please forgive me if this is a silly question in this forum. I am mostly
> a C/C++ guy and my head tends to explode just a bit when I see code that
> depends on the details of memory layout :-)

Yeah, it's a real shame when people write code like that.
Fortran doesn't constrain memory layouts to allow optimizers
wide latitude in doing optimizations. Unfortunately,
people often look at a particular result and assume that
the standard forces that behavior when, in fact, they are
just lucky.

Realistically, your only hope is remove the negative
subscripts. That's likely to mean you'll have to figure
out why the original person wrote it that way and recode
the bad parts. This is the only way you can also run
the program with bounds checking turned on. If the
code fails with bounds checking turned on, there's
always the PRINT statement for debugging.

For a second only hope, try compiling with ever
optimization turned off. That's likely to give the
same memory layout as the original program did back in
1977 (when optimizers were less aggressive and stacks
were less common).

If they don't work, then your next only hope is to fool
around with EQUIVALENCE to force things to be in the correct
order. Something like
       real*8 memory(many*m*n)
       equivalence (memory(1), e(1))
       equivalence (memory(m*n+1), d(1))
       equivalence (memory(2*m+n+1, c(1))
...
this is what the original coder probably assumed would
happen. For your subroutine that fails with a
reference to e(-i), try putting a pad before the
big chunk of memory. Something like
       real*8 pad(m*n+1)
       equivalence (pad(m*n+1), memory(1))
        equivalence (memory(1), e(1))
that will give you something reasonably well defined
before e. Bounds checking won;t work here, because you'll
be going out of bounds ;) .

Finally, your really last only hope is to find the orginal
programmer and shoot him. That won't help you today,
but it will in the long run ;) .

*** Hendrickson
>
> Thank you very much in advance,
>