[M3devel] gcc 4.5.0?
Tony Hosking
hosking at cs.purdue.edu
Fri May 21 04:22:46 CEST 2010
That is the standard formulation, but different architectures have different optimised forms (passing static link in a register, etc.). In any case, gcc has its own weird way of doing things, which we mostly use, but from which we need a little extra support (hence the small amount of special tailoring).
On 20 May 2010, at 21:43, Jay K wrote:
>
> Wierd. To me the model to follow is almost obvious. It should be obvious to everyone and implemented the way I have in mind. :)
>
>
> The static link is an extra parameter.
> And, as such, also an extra local variable.
> You only need one.
> Each nesting level would follow another chain.
> Or you can pass each nesting level as an extra parameter.
> Or you can optimize and pass locals/parameters separately.
> But there is an obvious straightforward non-optimized form. ?
>
>
> Here, I worked it through:
>
>
>
> Why doesn't it just look something like this:
>
>
> nested:
>
> void F1(int a, int b)
> {
> int c = a + b;
>
> int f2()
> {
> int d = a + b;
>
> int f3()
> {
> return d + a + c;
> }
>
> return a + c + f3();
> }
>
> return f2();
> }
>
> not nested:
>
>
> typedef struct {
> /* locals */
> int d;
> void* x86_frame_pointer;
> void* x86_return_address;
> /* parameters */
> f1_frame_t* f1;
> } f2_frame_t;
>
>
> typedef struct {
> /* locals */
> int c;
> void* x86_frame_pointer;
> void* x86_return_address;
> /* parameters */
> int a;
> int b;
> } f1_frame_t;
>
>
> int f3(f2_frame_t* f2)
> {
> return f2->d + f2->f1->a + f2->f1->c;
> }
>
>
> int f2(f1_frame_t* f1)
> {
> int d = f1->a + f1->b;
> return f1->a + f1->c + f3((f2_frame_t*)&d);
> }
>
>
> int F1(int a, int b)
> {
> int c = a + b;
> return f2((f1_frame_t*)&c);
> }
>
>
> or, alernatively, almost the same, pass each chain as another parameter:
>
>
> typedef struct {
> /* locals */
> int d;
> void* x86_frame_pointer;
> void* x86_return_address;
> /* parameters */
> } f2_frame_t;
>
>
> typedef struct {
> /* locals */
> int c;
> void* x86_frame_pointer;
> void* x86_return_address;
> /* parameters */
> int a;
> int b;
> } f1_frame_t;
>
>
> int f3(f2_frame_t* f2, f1_frame_t* f1)
> {
> return f2->d + f1->a + f1->c;
> }
>
>
>
> int f2(f1_frame_t* f1)
> {
> int d = f1->a + f1->b;
> return f1->a + f1->c + f3((f2_frame_t*)&d, f1);
> }
>
>
> int F1(int a, int b)
> {
> int c = a + b;
> return f2((f1_frame_t*)&c);
> }
>
>
> This should be easily adapted to any function call convention/sequence.
> e.g. even if parameters are passed in registers.
>
>
> And for that matter, why don't we just transform it to be like this?
> With the goal of reducing/eliminating gcc patches?
>
>
> Am I missing something?
>
>
> This form doesn't work?
>
>
> Isn't reasonably simple?
>
>
> Granted it might not be optimal. But it depends.
> You know, you can copy in the local/parameter values, but then you have
> to copy them out too. You can do analysis to show they aren't changed,
> in which case no need to copy them out.
> Similarly you can pass the parameters as separate parameters, by address
> if they are ever changed.
>
>
> A portable transform couldn't depend on adjacency of locals and parameters.
> It would have to either copy the parameters into the frame_t, or their addresses.
>
>
> A portable form couldn't get the frame by taking the address of a "individual" local.
>
>
> Here is portable form:
>
>
> typedef struct {
> int d;
> f1_frame_t* f1;
> } f2_frame_t;
>
> typedef struct {
> int a;
> int b;
> int c;
> } f1_frame_t;
>
>
> int f3(f2_frame_t* f2)
> {
> return f2->d + f2->f1->a + f2->f1->c;
> }
>
> int f2(f1_frame_t* f1)
> {
> f2_frame_t f;
> f.f1 = f1;
> f.d = f1->a + f1->b;
> return f1->a + f1->c + f3(&f);
> }
>
>
> int F1(int a, int b)
> {
> f1_frame_t f;
> f.a = a;
> f.b = b;
> f.c = a + b;
> return f2(&f);
> }
>
>
> or:
>
>
> typedef struct {
> int d;
> } f2_frame_t;
>
>
> typedef struct {
> int a;
> int b;
> int c;
> } f1_frame_t;
>
>
> int f3(f2_frame_t* f2, f1_frame_t* f1)
> {
> return f2->d + f1->a + f1->c;
> }
>
>
> int f2(f1_frame_t* f1)
> {
> f2_frame_t f;
> f.d = f1->a + f1->b;
> return f1->a + f1->c + f3(&f, f1);
> }
>
>
> int F1(int a, int b)
> {
> f1_frame_t f;
> f.a = a;
> f.b = b;
> f.c = a + b;
> return f2(&f);
> }
>
>
>
>
> - Jay
>
>
>
> ----------------------------------------
>> Date: Thu, 20 May 2010 12:12:00 -0500
>> From: rodney_bates at lcwb.coop
>> To: m3devel at elegosoft.com
>> Subject: Re: [M3devel] gcc 4.5.0?
>>
>> gcc even extends C with nested functions, and the support is there for that. We use it too.
>>
>> Beware: The runtime model is, IMO, bizarre. Definitely counterintuitive and unlike anything
>> you will ever see elsewhere. There are two static links. One is used for the first step
>> in following a static chain and points to the expected place in the target frame. The other
>> is used of subsequent steps in chain-following, and points to somewhere inside the target
>> frame that is dependent on the target procedure. It's the beginning of a subrecord where
>> all the nonlocally-referenced variables are collected. The second static link is also
>> located in there. All the nonlocally-referenced variables and parameters are duplicated
>> in there too.
>>
>> I'm not sure I remembered these details exactly right. Maybe both links point to the
>> subrecord, but one is located at a traditional procedure-independent, fixed location
>> in the frame, while the second is located in the subrecord. Also, sometimes the first
>> static link value is kept in a register and never stored.
>>
>> What this all apparently accomplishes is simplifying an "insanely complicated" _compile-time_
>> scheme that, I believe came from interaction between nested procedures and inlining them.
>>
>> But it's at the cost of what I would call an insanely complicated _runtime_ scheme.
>>
>> It undermined m3gdb's handling of static links, and I don't think it's possible to fix
>> with the current design of emitting debug info very early. The locations and target
>> offsets of these things aren't know until later, and m3gdb really needs to know them.
>>
>>
>> Jay K wrote:
>>> What I meant regarding "static chain" was more like "does gcc already have support that we can use".
>>> Don't any of the other languages need it already?
>>> Ada? Pascal? Maybe the nested C function support?
>>>
>>> - Jay
>>>
More information about the M3devel
mailing list