[M3devel] trampolines and uplevel locals declared after function starts..

Jay K jay.krell at cornell.edu
Fri Sep 21 07:03:10 CEST 2012


 >  Did you take a look at the LLVM IR I sent?  T
Sorry, I couldn't quickly make sense of it. :(I have to read up on LLVM or study your code more.(This is why I favor a C backend -- I'm already an expert and there is far more C expertise in the world than LLVM expertise..I think.. and other reasons like portability..)  Mention of "trampolines" was worrisome though.That usually means "runtime generated code, often on the stack". True here?  If not, nevermindIf so:   There is no good solution here, but I think runtime generated code is mostly a losing battle..at least on the stack. If you dedicate a little bit of special purpose platform-specific code to it, and you don't mind it being slow, and you deal with the lifetime management, then it becomes viable.e.g. on Windows, you use VirtualAlloc(MEM_EXECUTE).On other platforms you use mprotect I think. And/or mmap? mmap(PROT_EXEC)?But you end up allocating memory in large chunks, like at least 4K or 64K.(You can also write out little temporary .so/.dll files, but that is unnecessarily inefficient.)  I've seen code that calls mprotect against the stack. Yuck.The stack is nonexecutable on purpose -- for security (assuming there is any C or C++ code in the process).  If you read the libffi mailing list, you see they are constantly in this arms race.There is a slow steady trickle of problems making the libffi code executable.For Darwin/ARM they came up with a trick to avoid generating code, since the platform doesn't allow unsigned code to run.   Pluses/minuses:  runtime generated code:Producing the code is fast, on historical systems that don't make it difficult to execute memory, e.g. on the stack.Running the code is fast.Pointers to closures become compatible with C -- you can pass a pointer to a closure as a callback to or C++ and it works.Highly target-dependent.  The unusual Modula-3 method:Also fast.Calling function pointers is different and larger and slower.C code cannot call function pointers to nested functions. That is the main downside.Only slightly target-dependent -- in its choice of closure marker.I think we should change the closure marker slightly -- its size should be target-dependent, it should only be 4 bytes on SPARC64, MIPS64, PPC64, Alpha, AMD64 -- so the alignment check can be skipped. And it should possibly be larger than 4 or 8 bytes on IA64.But someone needs to read up on all the various instruction encodings and verify that 4 or 8 0xFF bytes are really invalid and won't occur.But most likely if 4 bytes of 0xFF are a good marker on MIPS32, PPC32, SPARC32, I386, they are also probably a good marker on MIPS64, PPC64, SPARC64, AMD64. And Alpha.Another option is to use something like "jmp marker". i.e. a jump to an address that is used just be the marker. It might not be a constant though. Maybe it is relocated. But you want it to be the same across all dynamic linked code, actually.  We might want to have calls through a function pointer go through a helper function, to keep it small.  <tangent>Hm.. I wonder..maybe a closure can be generated fairly portably actually.Maybe you just need one little wrapper per function signature. And most would really be the same.Let me think on this..hm..well..you could do it if you have a special static link register probably. I need to think about this more. Do "all" the calling conventions provide a special extra register for "static link"? And is that often used for C++ "this"? In that case, there is a way..  <tangent>Ultimately, I should point out, the idea of passing a closure to C is actually very useful.One should be able to pass an "object method pointer".So some language construct or pragma might be nice there -- to force down the runtime generated code path.But usually the C function that wants a function pointer also takes a "void* context", so maybe not so needed.  Or maybe folks would just use "libffi" for that.    - Jay
 CC: m3devel at elegosoft.com
From: antony.hosking at gmail.com
Subject: Re: [M3devel] uplevel locals declared after function starts..
Date: Thu, 20 Sep 2012 19:38:27 -0400
To: jay.krell at cornell.edu

Agreed. You need to backpatch the uplevel locals. I would accumulate all of them. Did you take a look at the LLVM IR I sent?  That's how I plan to so it. 

Sent from my iPhone
On Sep 20, 2012, at 19:18, Jay K <jay.krell at cornell.edu> wrote:




We get uplevel locals declared while within a function.So I think I'll bite the bullet and make multiple passes now.Otherwise I build up the frame struct "too early".I need to see all the locals -- all the uplevels, early in a function.

There is also a chance they don't all have unique names.

I do NOT expect I should have separate frames/static_links per-scope,just per-function.

I could put structs inside the frame, named after the line they starton or such.. that might be prettier and more readable, or not.

The structs could then be unioned at some point -- parallel scopescan/should share storage.

M3C.m3:PROCEDURE declare_local(...)BEGIN...IF u.in_proc THEN        ExtraScope_Open(u);        print(u, var.Declare() & " M3_INIT;");        <* ASSERT up_level = FALSE *>  this fails    ELSE        IF up_level THEN            u.current_proc.uplevels := TRUE;        END;        u.current_proc.locals.addhi(var);    END;    RETURN var;END declare_local;

== package /Users/jay/dev2/cm3/m3-libs/m3tk-misc ==
...new source -> compiling Args.m3

****** runtime error:***    <*ASSERT*> failed.***    file "../src/M3C.m3", line 1778***
  m3_backend => 1536m3cc (aka cm3cg) failed compiling: Args.mc

I'm guessing the code is here:

PROCEDURE Errors(h: Handle; indent: CARDINAL := 0): Text.T RAISES {}=  BEGIN    IF h.errors = 0 THEN      <*FATAL Fatal*> BEGIN RAISE Fatal; END;    ELSE
      VAR        texts := NEW(REF ARRAY OF TEXT, h.errors * 2);            (* allocates space for all the error messages + padding *)        pos: CARDINAL := 0;        padding := Fmt.Pad("", indent);        fmt: Text.T;
      <*INLINE*> PROCEDURE Add(t: Text.T) RAISES {}=          BEGIN texts[pos] := t; INC(pos) END Add;      <*INLINE*> PROCEDURE PaddedAdd(t: Text.T) RAISES {}=          BEGIN Add(padding); Add(t) END PaddedAdd;


see how "texts" is both uplevel and "not at the start" of the function.

This is going to take a few days to deal with.

Compiler can compile itself, and a large swath of the system now.I didn't use the self-built compiler, but I will do that next. It probably works.Very good.

 - Jay
 		 	   		  
 		 	   		  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20120921/48287a24/attachment-0001.html>


More information about the M3devel mailing list