[llvm-commits] [llvm] r41879 - in /llvm/trunk: docs/GarbageCollection.html lib/Transforms/Scalar/LowerGC.cpp

Gordon Henriksen ghenriksen at carbonite.com
Wed Sep 12 13:14:04 PDT 2007


On 2007-09-12, at 14:26, Chris Lattner wrote:

> On Sep 12, 2007, at 11:14 AM, Gordon Henriksen wrote:
>
>> Hm. Are you sure this is wise? I realize my patch didn't provide  
>> root initialization, but I have that fixed locally. Without  
>> liveness analysis, leaving stack roots uninitialized seems unwise,  
>> since gc points are not entirely predictable from the IR.
>
> I think it is fine.  trivially, any front-end that produced a call  
> to the gcroot intrinsic could emit that intrinsic and a store of  
> null, so there is no power lost here.
>
> The reason it is useful is for things like this (C syntax):
>
> void foo(object *P) {
>    gcroot(&P, 0);
>
>    ... use P ...
> }
>
> when converted to LLVM, this turns into the equivalent of:
>
> void foo(object *P) {
>    P_addr = alloca object*
>    store P -> P_addr
>    gcroot(P_addr, 0);
>
>    ... use *P_addr...
> }
>
> With the null'ing property of gcroot, it would clobber the live in
> value.

Well, this would also fix that:

    P_addr = alloca object*
-  store P -> P_addr
    gcroot(P_addr, 0);
+  store P -> P_addr

But I'd be more concerned about more complex codes:

   void f(object[] ar) {
     g();
     foreach (object o : ar)
       h(o);
   }

becomes

   void @f({ i32, [0 x %object*] }* %ar) {
   entry:
     %ar_addr = alloca { i32, [0 x %object*] }*
     @llvm.gcroot(%ar_addr, 0)
     store %ar -> %ar_addr

     %o_addr = alloca %object*
     @llvm.gcroot(%o_addr, 0)
     store null -> %o_addr   ; ****

     @g()

     tmp.0 = load %ar_addr
     tmp.1 = gep %tmp.0, 0, 0
     index.0 = load tmp.1
   loop:
     ...
   }

Here, the FE must emit the highlighted null initializer separate from  
the use, declaration, and scope of o; otherwise, a GC at a safepoint  
corresponding to call @g will likely segfault the collector. Of more  
concern, the FE must do so before any gc points, which are  
implementation-defined. It's that last property which makes this  
potentially error-prone.


But upon further musings, I'm actually convinced your new spec is  
best. llvm.gcroot should be purely an annotation, with no runtime  
component.

• In the case of a liveness-accurate collector:
The initializers are not necessary, so the collector is free to omit  
them. The alloca has the same value as if llvm.gcroot were not present.

• In the case of a collector without liveness:
The null initializers are necessary, but the initializer should  
immediately follow alloca rather than replacing call @llvm.gcroot.  
Effectively, the initial value of the alloca becomes null if and only  
if required by the collector. This change enables your alloca-store- 
gcroot sequence to behave as desired.

Regardless of the collector, the FE should not rely on the contents  
of an uninitialized alloca; it FE should emit initializers if that is  
necessary for the source language. The collector should perform a  
trivial DSE of the initial basic block to avoid obviously redundant  
initializers; the difference is that it can do this reliably, since  
it has knowledge of where safe points might occur.

I need to tweak the collector infrastructure slightly, but that's  
okay; it's definitely a stronger spec if I comply with it in this  
manner, since:

  1. The semantics of alloca are entirely preserved by the  
llvm.gcroot annotation.
  2. The user program's interaction with the collector is even more  
robust than before.

— Gordon

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20070912/52e7d864/attachment.html>


More information about the llvm-commits mailing list