[LLVMdev] Stack roots and function parameters

Talin viridia at gmail.com
Tue Sep 21 18:20:56 PDT 2010


On Mon, Sep 20, 2010 at 3:16 PM, Talin <viridia at gmail.com> wrote:

> So I've managed to get my stack crawler working and passing its unit tests
> - this is the one I've been working on as an alternative to shadow-stack: it
> uses only static constant data structures (no global variables or
> thread-local data), which means that it's fully compatible with a
> multi-threaded environment.
>
> One question that has arisen, however, is what to do about function
> parameters that may be stack roots. You can't call llvm.gcroot() on a
> function parameter. The only thing I can think of is to copy any function
> parameter that is a pointer, or which contains a pointer, into a local
> variable. This seems complicated and inefficient to me (for one thing, it
> means that every stack frame just got 8 bytes bigger due to the need to
> store the 'this' pointer locally)  - is there a better way?
>

Anyone?

Here's some examples of what I am talking about (I'll use Java for the
examples):

Example 1:

  class Foo {
    static String st = "Hello";

    void f1() {
       f2(st);
    }

    void f2(String s) {
      st = null; // Destroy static root
      // Now allocate something, which might trigger
      // a garbage collection.
      StringBuffer sb = new StringBuffer();

      // Is 's' still live? It's not stored in any
      // alloca, or in any global variable. It only
      // exists as a function parameter, which cannot
      // be declared as a root via llvm.gcroot.
      sb.append(s);
    }
  }

Example 2:

  class Foo {
    static void f1() {
      // Create a new 'Foo' but don't store it
      // anywhere.
      new Foo().f2();
    }

    void f2() {
      // Possibly trigger a collection
      StringBuffer sb = new StringBuffer();

      // Is 'this' still live at this point?
      sb.append(toString());
    }
  }

Now, I know that the LLVM "Accurate GC" document says that any "intermediate
values" must be declared as a GC root. My question is, does merely _loading_
a global variable - or any mutable, non-strictly-local variable for that
matter - count as an "intermediate" value in this case?

My problem is that I can't see how to address this without generating
horribly bloated and inefficient code. Pretty much every function argument
that isn't a simple integer or float will have to be copied to a hidden
local variable before every function call - in addition to copying it to the
stack to create the call frame. Even local variables are not immune if you
allow closures in your language. Under this scenario, calling conventions
that pass arguments in registers are utterly futile and save nothing,
because everything has to get copied to the stack anyway.

I really wish I could simply declare function arguments as llvm.gcroots. For
arguments that are not in registers, it would treat it just like an alloca,
since they both represent stack slots. For arguments that are passed in
registers, LLVM should automatically lower it to a non-register argument,
making a copy if needed. (I can't do this myself, since I'm not supposed to
*know* which arguments are passed in registers or not - that depends on the
target and the code generators and such).

That still leaves me with the problem of declaring as roots function
arguments that are the result of complex calculations, but those are far
fewer - declaring temporary vars for those won't bloat the code so badly.
But having to copy every single load of a non-local variable into a
temporary is a nightmare.

-- 
-- Talin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20100921/427044c0/attachment.html>


More information about the llvm-dev mailing list