[M3devel] trampolines and uplevel locals declared after function starts..
Antony Hosking
hosking at cs.purdue.edu
Fri Sep 21 15:50:21 CEST 2012
I think we should leave the CG interfaces as they are already (for static links). I can see how to generate LLVM IR easily with things the way they are. And you are already working to it. I wasn’t suggesting that you use trampolines, though I think it would be easy enough to do. But I am going to use the LLVM nest approach to pass frame pointers. That way I’ll get the efficient native static link passing code.
On Sep 21, 2012, at 1:03 AM, Jay K <jay.krell at cornell.edu> wrote:
> > 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, nevermind
> If 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 start
> on or such.. that might be prettier and more readable, or not.
>
>
> The structs could then be unioned at some point -- parallel scopes
> can/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 => 1536
> m3cc (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/d6d59123/attachment-0002.html>
More information about the M3devel
mailing list