[M3devel] m3cc static chain stuff

Jay K jay.krell at cornell.edu
Wed May 26 17:02:10 CEST 2010


> From: hosking at cs.purdue.edu
> Date: Wed, 26 May 2010 10:28:51 -0400
> To: jay.krell at cornell.edu
> CC: m3devel at elegosoft.com
> Subject: Re: [M3devel] m3cc static chain stuff
>
>
>
> We should endeavour to use the same functionality that the C compiler uses for its own nested functions, whereever possible. The only thing is that we also build closures, so the trampoline mechanisms should be avoided.


Right..for those that don't know..


There's no "good" efficient way to implement nested functions.
Our implementation is pretty "bad".
gcc's implementation is pretty "bad".
They are different.


gcc (Tony mentions "trampoline"):
In particular, taking the address of a nested function
involves runtime code generation on the stack.
These days, running code on the stack is "difficult".
On some systems, you do mprotect() to make that part of the stack executable.
On other systems, you just can't do it (iPhone).
On older systems, no problem. It is simple and efficient.


Modula-3:
Whenever we go to call a function pointer, we first see if it start with a 4 or 8 byte -1.
If so, it is our "closure" thingy, and the -1 is followed
I guess by an actual function pointer and a static link.
The static link is loaded wherever and the function called.


Other systems (see modern ATL on Windows) allocate executable
memory not on the stack. Not even regular heap is executable
generally any longer though, one need to VirtualAlloc or mmap or such.


This is a powerful mechanism in general, you can do stuff like
runtime "currying".


Pointers to nested Modula-3 function cannot be passed off
as function pointers to other languages.
  But hey, at least we can write nested functions and pass
    them off to ourselves. Interop with C would just be bonus?


Calling function pointers in Modula-3 is slowed down
everywhere, in case the function pointer is nested.
I think.


-1 must be ensured to be invalid code.
Or we could make it a more visible part of porting.
 You know -- it could be per-target and folks would have
 to experiment with each target to find a good sequence,
 that is cheap to detect and guaranteed invalid.
 (ie: 0 might be better than -1!)


Could possibly replace the -1 with an actual function pointer
that is always called.
But then pointers to non-nested functions also wouldn't
interoperate with C.


I think there's something more to the mechanisms than I realize.
The "static link" must survive calls out to non-nested functions
or somesuch.


Anyway, while I think our mechanism is pretty clearly flawed,
it seems to be slightly less bad than gcc's.


Perhaps we should invest in what ATL does though and
dynamically allocate executable memory not on the stack?
Still, an "open" iPhone is probably better with the current scheme.
Most other systems should be ok either way.


I think there is a bit of 4.3=>4.5 that needs closer attention
and isn't obvious, this part:

+    case STATIC_CHAIN_EXPR:
+      decl = TREE_OPERAND (t, 0);
+      target_context = decl_function_context (decl);
+      if (target_context)
+    {
+      if (info->context == target_context)
+        {
+          /* Make sure frame_decl gets created.  */
+          (void) get_frame_type (info);
+        }
+      *tp = get_static_chain (info, target_context, &wi->tsi);
+    }
+      else
+    *tp = null_pointer_node;
+      break;
+

 - Jay
 		 	   		  


More information about the M3devel mailing list