This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

A correction: Different invariants about the contents of static links]


This is repost of my slightly earlier post, with a critical and
confusing misstatement corrected.


Well, I agree with what you said about your example, but it's not what I am meaning. See below.

Ian Lance Taylor wrote:
"Rodney M. Bates" <rbatesjk@cox.net> writes:


I don't understand this.  A pointer to anywhere in an activation record
(or even outside it, if outside by a fixed offset) allows access to
exactly the same set of things as any other, including the value the base
register holds when the activation record is current.  That set is everything
in the AR, using different but still fixed displacements, and nothing more,
since everything else is not necessarily at a fixed displacement from the
AR.


OK, let me reset.

It seemed to me that you were suggesting that the static link pointer
should always be a constant offset from the frame pointer.

No, I am not saying this. If it were so, there would be no need for a SL at all. You could just use the FP to access both local and nonlocal things.


gcc doesn't work that way, though. Here is a simple counter-example.


int
foo (int x, int y)
{
  int
  s1 ()
  {
    if (--y == 0)
      return 0;
    return x + s1 ();
  }

  return s1 ();
}

If you compile this code, you will see that the static link pointer is
passed down unchanged through the recursive calls.  The frame pointer
is, of course, different for each call.  You can't compute the static
link pointer from the frame pointer.  It's completely independent.

If this is not a counter-example for what you want, then I think we
need a clearer explanation of what you want.

Consider this slightly different example:


int
foo (int x, int y)
{ int loc;

  int
  s1 ( )
    { if (--y == 0)
        return 0;
      return loc  + s1 ();
    }

  loc = x;
  return s1 ();
}

I added a local variable loc to s1, and both s1 and foo access loc.

When executing in foo, the frame pointer will point to a fixed spot in the
activation record of foo.  On i386, the FP is %ebx and it points to the
dynamic link field.  From there, loc is at displacement -4.  Code in the
body of foo will reference x at -4(FP).

When we get into any instance of s1, s1's AR will contain a static link that
points to the AR of foo, which is what you said.  The question is where
in foo's AR does the SL point to.  It would make sense for it to be the same
as where the frame pointer points when executing in foo, i.e., the static
link would point to the dynamic link field of foo's AR.  Then x could be
accessed from within sl by loading the SL into some register, say REG, and
referring to -4(REG).  loc is at the same displacement relative to the static
link in s1 as is used in foo relative to the FP.

This is exactly what happens in this example, all of which makes sense to
me.   However, in somewhat more complicated examples, the SL value passed
to s1 is not a copy of foo's FP, but instead the _value_ of the SL is
a copy of foo's FP, minus 8.  (I think I said this backwards in my original
post.)  Then loc would be accessed, from within s1, at a different
displacement (+4) relative to the SL pointer, whereas the same AR field is
accessed at displacement -4 relative to FP when within foo.

This all executes correctly, even if it's a bit strange.  But it has to take
extra trouble for the code generator to keep track of two different reference
addresses for the same AR and adjust SL offsets to be different from FP
offsets for the same AR.  I can't see any benefit.  It doesn't change the
set of fields a SL can access in the AR it points to a bit.  Only the
displacements needed change.

And it wouldn't matter to me, except it's making it a lot more difficult
to give gdb the ability to execute gdb-user-typed calls on nested functions
and pass nested functions as parameters (where gdb has to generate static
link values) and print nonlocal variables (where gdb has to know how to
interpret the contents of a SL).

To get the different invariant about where in an AR the SL points to, the
smallest case I could get has 1) two side-by-side nested functions inside
the outer function, 2) one of the nested functions has a parameter
of pointer-to-function type, and 3) the other nested function is passed
as the argument to said parameter.  This involves building a trampoline
that loads the static ancestor pointer.  In this case, passing a fixed
static link, constructing the static ancestor value in the trampoline,
and using a static link all use the other invariant that static link
values point 8 bytes lower than where a FP to the same AR points. Here
is the example:

int foo ( int x )

{ int loc;

    int s1 ( int j )
      { return loc + j;
      }

    int s2 ( int k, int ( * func ) ( int ) )
      { return func ( k );
      }

    loc = x;
    return s2 ( loc , s1 );
  }

When accessing parameters instead of local variables, the waters are muddied
by another mechanism that also seems unnecessary to me, but I think is
unrelated to my question.  This is why I switched to a local variable in the
examples.


-- Rodney M. Bates


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]