[LLVMdev] GC questions.

Tobias Nurmiranta spyck at lysator.liu.se
Wed Jul 21 21:04:50 PDT 2004


Ok, here's the new patch. (Please tell me if I shouldn't mail patches
directly on the mailing list.)

While I was editing LowerGC.cpp I made a little test (not part of this
patch, but the diff with LowerGC.cpp in cvs is attached). I've added a new
intrinsic called llvm.gcroot_value(sbyte*, sbyte*), which takes a pointer
directly instead and transforms it into an alloca. The idea is the
following,

this:

  %X = call sbyte* %llvm_gc_allocate(uint 16)
  call void %llvm.gcroot_value(sbyte* %X, sbyte* null)

is transformed into:

  %stackGcRoot = alloca sbyte*
  %X = call sbyte* %llvm_gc_allocate(uint 16)
  store sbyte* %X, sbyte** %stackGcRoot
  call void %llvm.gcroot(sbyte** %stackGcRoot, sbyte* null)

and all other uses of %X, for example:

  store sbyte 10, sbyte* %X

is transformed into:

  %loadGcRoot = load sbyte** %stackGcRoot
  store sbyte 10, sbyte* %loadGcRoot

In this way, the frontends don't have to explictly alloca the gcroots,
but instead just use the intrinsic, which hides how the gcroots are
implemented.

,	Tobias

On Wed, 21 Jul 2004, Tobias Nurmiranta wrote:
>
> Ok, that makes sense :).
> ,	Tobias
>
> On Wed, 21 Jul 2004, Chris Lattner wrote:
>
> > On Wed, 21 Jul 2004, Tobias Nurmiranta wrote:
> > > > void *llvm_gc_read(void *ObjPtr, void **FieldPtr) {
> > > >   return *FieldPtr;
> > > > }
> > >
> > > Hm, but doesn't FieldPtr need to be calculated target-specific in those
> > > cases?
> >
> > For the field pointer, one could use the getelementptr instruction:
> >
> > %pairty = { sbyte, sbyte, int* }
> >
> >   %pairPtr = ...
> >   %fieldptr = getelementptr %pairty* %pairPtr, int 0, uint 2
> >   FieldVal = llvm.gc_read(pairptr, fieldptr)
> >
> > Because of the getelementptr instruction is used, we don't have the
> > explicit offset encoded into the bytecode file.  The other advantage of
> > this is that when the gc_read/write intrinsics are lowered to load/stores,
> > the resultant LLVM code will be much simpler.
> >
> > > My thoughts was that it's better to just have one pointer to the heap, and
> > > reference fields by offset, rather than alloca'ing memory for each
> > > FieldPtr needed (to make sure the recording of gcroots is sound). If we
> > > use FieldPtr we get the problem again with the GC traversing pointers into
> > > objects, rather than to their headers.
> >
> > You're right, but you shouldn't need to put the inner pointer into an
> > alloca.  The idea is that the GC will (eventually) only stop a thread at a
> > GC safe point, so heap-directed pointers that don't live across a safe
> > point can live in an LLVM register.  Currently function calls are the only
> > safe points, though we will add an explicit gc.safepoint intrinsic when we
> > get threads (to put safe points in loops and other places that don't have
> > calls).  Since field accesses like this are all very short lived, they can
> > all just be registers.
> >
> > -Chris
-------------- next part --------------
? llvm/l
? llvm/lib/BytecodeLibs
? llvm/lib/Debug
? llvm/lib/Analysis/Debug
? llvm/lib/Analysis/Depend
? llvm/lib/Analysis/DataStructure/Debug
? llvm/lib/Analysis/DataStructure/Depend
? llvm/lib/Analysis/IPA/Debug
? llvm/lib/Analysis/IPA/Depend
? llvm/lib/AsmParser/Debug
? llvm/lib/AsmParser/Depend
? llvm/lib/Bytecode/Reader/Debug
? llvm/lib/Bytecode/Reader/Depend
? llvm/lib/Bytecode/Writer/Debug
? llvm/lib/Bytecode/Writer/Depend
? llvm/lib/CodeGen/Debug
? llvm/lib/CodeGen/Depend
? llvm/lib/CodeGen/InstrSched/Debug
? llvm/lib/CodeGen/InstrSched/Depend
? llvm/lib/CodeGen/SelectionDAG/Debug
? llvm/lib/CodeGen/SelectionDAG/Depend
? llvm/lib/Debugger/Debug
? llvm/lib/Debugger/Depend
? llvm/lib/ExecutionEngine/Debug
? llvm/lib/ExecutionEngine/Depend
? llvm/lib/ExecutionEngine/Interpreter/Debug
? llvm/lib/ExecutionEngine/Interpreter/Depend
? llvm/lib/ExecutionEngine/JIT/Debug
? llvm/lib/ExecutionEngine/JIT/Depend
? llvm/lib/Support/Debug
? llvm/lib/Support/Depend
? llvm/lib/Target/Debug
? llvm/lib/Target/Depend
? llvm/lib/Target/CBackend/Debug
? llvm/lib/Target/CBackend/Depend
? llvm/lib/Target/Skeleton/Debug
? llvm/lib/Target/Skeleton/Depend
? llvm/lib/Target/Skeleton/SkeletonGenInstrInfo.inc
? llvm/lib/Target/Skeleton/SkeletonGenInstrNames.inc
? llvm/lib/Target/Skeleton/SkeletonGenRegisterInfo.h.inc
? llvm/lib/Target/Skeleton/SkeletonGenRegisterInfo.inc
? llvm/lib/Target/Skeleton/SkeletonGenRegisterNames.inc
? llvm/lib/Target/SparcV9/Debug
? llvm/lib/Target/SparcV9/Depend
? llvm/lib/Target/SparcV9/SparcV9.burg.in1
? llvm/lib/Target/SparcV9/SparcV9.burm
? llvm/lib/Target/SparcV9/SparcV9.burm.cpp
? llvm/lib/Target/SparcV9/InstrSelection/Debug
? llvm/lib/Target/SparcV9/InstrSelection/Depend
? llvm/lib/Target/SparcV9/LiveVar/Debug
? llvm/lib/Target/SparcV9/LiveVar/Depend
? llvm/lib/Target/SparcV9/RegAlloc/Debug
? llvm/lib/Target/SparcV9/RegAlloc/Depend
? llvm/lib/Target/X86/Debug
? llvm/lib/Target/X86/Depend
? llvm/lib/Transforms/Debug
? llvm/lib/Transforms/Depend
? llvm/lib/Transforms/IPO/Debug
? llvm/lib/Transforms/IPO/Depend
? llvm/lib/Transforms/Instrumentation/Debug
? llvm/lib/Transforms/Instrumentation/Depend
? llvm/lib/Transforms/Instrumentation/ProfilePaths/Debug
? llvm/lib/Transforms/Instrumentation/ProfilePaths/Depend
? llvm/lib/Transforms/Scalar/Debug
? llvm/lib/Transforms/Scalar/Depend
? llvm/lib/Transforms/Utils/Debug
? llvm/lib/Transforms/Utils/Depend
? llvm/lib/VMCore/Debug
? llvm/lib/VMCore/Depend
? llvm/projects/ModuleMaker/Makefile.common
? llvm/projects/ModuleMaker/config.log
? llvm/projects/ModuleMaker/config.status
? llvm/projects/ModuleMaker/tools/Debug
? llvm/projects/ModuleMaker/tools/ModuleMaker/Debug
? llvm/projects/ModuleMaker/tools/ModuleMaker/Depend
? llvm/projects/Stacker/lib/compiler/Debug
? llvm/projects/Stacker/lib/compiler/Depend
? llvm/projects/Stacker/lib/compiler/Lexer.cpp
? llvm/projects/Stacker/lib/compiler/StackerParser.cpp
? llvm/projects/Stacker/lib/compiler/StackerParser.h
? llvm/projects/Stacker/lib/compiler/StackerParser.output
? llvm/projects/Stacker/lib/runtime/Debug
? llvm/projects/Stacker/lib/runtime/Depend
? llvm/projects/Stacker/tools/stkrc/Debug
? llvm/projects/Stacker/tools/stkrc/Depend
? llvm/projects/sample/Makefile.common
? llvm/projects/sample/config.log
? llvm/projects/sample/config.status
? llvm/projects/sample/lib/Debug
? llvm/projects/sample/lib/sample/Debug
? llvm/projects/sample/lib/sample/Depend
? llvm/projects/sample/tools/Debug
? llvm/projects/sample/tools/sample/Debug
? llvm/projects/sample/tools/sample/Depend
? llvm/runtime/GC/SemiSpace/BytecodeObj
? llvm/runtime/GC/SemiSpace/Debug
? llvm/runtime/GC/SemiSpace/Depend
? llvm/runtime/GCCLibraries/crtend/BytecodeObj
? llvm/runtime/GCCLibraries/crtend/Depend
? llvm/runtime/GCCLibraries/libc/BytecodeObj
? llvm/runtime/GCCLibraries/libc/Depend
? llvm/runtime/GCCLibraries/libcurses/BytecodeObj
? llvm/runtime/GCCLibraries/libcurses/Depend
? llvm/runtime/GCCLibraries/libg/BytecodeObj
? llvm/runtime/GCCLibraries/libg/Depend
? llvm/runtime/GCCLibraries/libgcc/BytecodeObj
? llvm/runtime/GCCLibraries/libgcc/Depend
? llvm/runtime/GCCLibraries/libgdbm/BytecodeObj
? llvm/runtime/GCCLibraries/libgdbm/Depend
? llvm/runtime/GCCLibraries/libm/BytecodeObj
? llvm/runtime/GCCLibraries/libm/Depend
? llvm/runtime/GCCLibraries/libmalloc/BytecodeObj
? llvm/runtime/GCCLibraries/libmalloc/Depend
? llvm/runtime/GCCLibraries/libtermcap/BytecodeObj
? llvm/runtime/GCCLibraries/libtermcap/Depend
? llvm/runtime/GCCLibraries/libucb/BytecodeObj
? llvm/runtime/GCCLibraries/libucb/Depend
? llvm/runtime/GCCLibraries/libutempter/BytecodeObj
? llvm/runtime/GCCLibraries/libutempter/Depend
? llvm/runtime/GCCLibraries/libutil/BytecodeObj
? llvm/runtime/GCCLibraries/libutil/Depend
? llvm/runtime/libdummy/BytecodeObj
? llvm/runtime/libdummy/Depend
? llvm/runtime/libprofile/BytecodeObj
? llvm/runtime/libprofile/Debug
? llvm/runtime/libprofile/Depend
? llvm/runtime/libtrace/BytecodeObj
? llvm/runtime/libtrace/Debug
? llvm/runtime/libtrace/Depend
? llvm/tools/Debug
? llvm/tools/analyze/Debug
? llvm/tools/analyze/Depend
? llvm/tools/bugpoint/Debug
? llvm/tools/bugpoint/Depend
? llvm/tools/extract/Debug
? llvm/tools/extract/Depend
? llvm/tools/gccas/Debug
? llvm/tools/gccas/Depend
? llvm/tools/gccld/Debug
? llvm/tools/gccld/Depend
? llvm/tools/llc/Debug
? llvm/tools/llc/Depend
? llvm/tools/lli/Debug
? llvm/tools/lli/Depend
? llvm/tools/llvm-ar/Debug
? llvm/tools/llvm-ar/Depend
? llvm/tools/llvm-as/Debug
? llvm/tools/llvm-as/Depend
? llvm/tools/llvm-bcanalyzer/Debug
? llvm/tools/llvm-bcanalyzer/Depend
? llvm/tools/llvm-db/Debug
? llvm/tools/llvm-db/Depend
? llvm/tools/llvm-dis/Debug
? llvm/tools/llvm-dis/Depend
? llvm/tools/llvm-link/Debug
? llvm/tools/llvm-link/Depend
? llvm/tools/llvm-nm/Debug
? llvm/tools/llvm-nm/Depend
? llvm/tools/llvm-prof/Debug
? llvm/tools/llvm-prof/Depend
? llvm/tools/llvm-stub/Debug
? llvm/tools/llvm-stub/Depend
? llvm/tools/opt/Debug
? llvm/tools/opt/Depend
? llvm/utils/Burg/Debug
? llvm/utils/Burg/Depend
? llvm/utils/Burg/gram.tab.c
? llvm/utils/Burg/gram.tab.h
? llvm/utils/TableGen/Debug
? llvm/utils/TableGen/Depend
? llvm/utils/TableGen/FileLexer.cpp
? llvm/utils/TableGen/FileParser.cpp
? llvm/utils/TableGen/FileParser.h
? llvm/utils/TableGen/FileParser.output
? llvm/utils/fpcmp/Debug
? llvm/utils/fpcmp/Depend
Index: llvm/docs/GarbageCollection.html
===================================================================
RCS file: /var/cvs/llvm/llvm/docs/GarbageCollection.html,v
retrieving revision 1.6
diff -r1.6 GarbageCollection.html
237,238c237,238
<   sbyte *%llvm.gcread(sbyte **)<br>
<   void %llvm.gcwrite(sbyte*, sbyte**)
---
>   sbyte *%llvm.gcread(sbyte *, sbyte **)<br>
>   void %llvm.gcwrite(sbyte*, sbyte*, sbyte**)
253c253,255
< second has the same semantics as a non-volatile LLVM store.  At code generation
---
> second has the same semantics as a non-volatile LLVM store, with the
> additions that they also take a pointer to the start of the memory
> object as an argument.  At code generation
344,345c346,347
<     void *llvm_gc_read(void **)<br>
<     void llvm_gc_write(void*, void**)
---
>     void *llvm_gc_read(void*, void **)<br>
>     void llvm_gc_write(void*, void *, void**)
356,357c358
< implement it.  Note that we may add a pointer to the start of the memory object
< as a parameter in the future, if needed.
---
> implement it.
Index: llvm/lib/Transforms/Scalar/LowerGC.cpp
===================================================================
RCS file: /var/cvs/llvm/llvm/lib/Transforms/Scalar/LowerGC.cpp,v
retrieving revision 1.4
diff -r1.4 LowerGC.cpp
112c112
<     GCRead = M.getOrInsertFunction("llvm_gc_read", VoidPtr, VoidPtrPtr, 0);
---
>     GCRead = M.getOrInsertFunction("llvm_gc_read", VoidPtr, VoidPtr, VoidPtrPtr, 0);
115c115
<                                     VoidPtr, VoidPtrPtr, 0);
---
>                                     VoidPtr, VoidPtr, VoidPtrPtr, 0);
185c185,186
<               Coerce(CI, 2, VoidPtrPtr);
---
>               Coerce(Ci, 2, VoidPtr);
>               Coerce(CI, 3, VoidPtrPtr);
187c188,189
<               Coerce(CI, 1, VoidPtrPtr);
---
>               Coerce(CI, 1, VoidPtr);
>               Coerce(CI, 2, VoidPtrPtr);
192c194
<                 CallInst *NC = new CallInst(GCRead, CI->getOperand(1),
---
>                 CallInst *NC = new CallInst(GCRead, CI->getOperand(1), CI->getOperand(2),
Index: llvm/lib/VMCore/Verifier.cpp
===================================================================
RCS file: /var/cvs/llvm/llvm/lib/VMCore/Verifier.cpp,v
retrieving revision 1.115
diff -r1.115 Verifier.cpp
714,715c714,715
<   case Intrinsic::gcread:          NumArgs = 1; break;
<   case Intrinsic::gcwrite:         NumArgs = 2; break;
---
>   case Intrinsic::gcread:          NumArgs = 2; break;
>   case Intrinsic::gcwrite:         NumArgs = 3; break;
Index: llvm/runtime/GC/GCInterface.h
===================================================================
RCS file: /var/cvs/llvm/llvm/runtime/GC/GCInterface.h,v
retrieving revision 1.2
diff -r1.2 GCInterface.h
41c41
< void *llvm_gc_read(void **P);
---
> void *llvm_gc_read(void *ObjPtr, void **FieldPtr);
46c46
< void llvm_gc_write(void *V, void **P);
---
> void llvm_gc_write(void *V, void *ObjPtr, void **FieldPtr);
Index: llvm/runtime/GC/SemiSpace/semispace.c
===================================================================
RCS file: /var/cvs/llvm/llvm/runtime/GC/SemiSpace/semispace.c,v
retrieving revision 1.4
diff -r1.4 semispace.c
92,93c92,93
< void *llvm_gc_read(void **P) { return *P; }
< void llvm_gc_write(void *V, void **P) { *P = V; }
---
> void *llvm_gc_read(void *ObjPtr, void **FieldPtr) { return *FieldPtr; }
> void llvm_gc_write(void *V, void *ObjPtr, void **FieldPtr) { *FieldPtr = V; }
Index: llvm/test/Regression/CodeGen/Generic/GC/alloc_loop.ll
===================================================================
RCS file: /var/cvs/llvm/llvm/test/Regression/CodeGen/Generic/GC/alloc_loop.ll,v
retrieving revision 1.2
diff -r1.2 alloc_loop.ll
7c7
< declare void %llvm.gcwrite(sbyte*, sbyte**)
---
> declare void %llvm.gcwrite(sbyte*, sbyte*, sbyte**)
35c35
< 	call void %llvm.gcwrite(sbyte* %A.1, sbyte** %B.1)
---
> 	call void %llvm.gcwrite(sbyte* %A.1, sbyte* %B, sbyte** %B.1)
-------------- next part --------------
30d29
< #include <iostream>
37c36
<     Function *GCRootInt, *GCRootValueInt, *GCReadInt, *GCWriteInt;
---
>     Function *GCRootInt, *GCReadInt, *GCWriteInt;
61c60
<   X("lowergc2", "Lower GC intrinsics, for GCless code generators");
---
>   X("lowergc", "Lower GC intrinsics, for GCless code generators");
103d101
<   GCRootValueInt = M.getNamedFunction("llvm.gcroot_value");
106c104,105
<   if (!GCRootInt && !GCRootValueInt && !GCReadInt && !GCWriteInt) return false;
---
>   if (!GCRootInt && !GCReadInt && !GCWriteInt) return false;
> 
108a108
> 
112c112
<     GCRead = M.getOrInsertFunction("llvm_gc_read", VoidPtr, VoidPtr, VoidPtrPtr, 0);
---
>     GCRead = M.getOrInsertFunction("llvm_gc_read", VoidPtr, VoidPtrPtr, 0);
115c115
<                                     VoidPtr, VoidPtr, VoidPtrPtr, 0);
---
>                                     VoidPtr, VoidPtrPtr, 0);
118c118
<   if (GCRootInt || GCRootValueInt) {
---
>   if (GCRootInt) {
121a122
> 
156c157
<   if (!GCRootInt && !GCRootValueInt && !GCReadInt && !GCWriteInt) return false;
---
>   if (!GCRootInt && !GCReadInt && !GCWriteInt) return false;
165d165
<   std::vector<CallInst*> GCRootValues;
177c177
<           if (F == GCRootInt) {
---
>           if (F == GCRootInt)
179,182c179
<           } else if (F == GCRootValueInt) {
< 	    GCRoots.push_back(CI);
<             GCRootValues.push_back(CI);
<           } else if (F == GCReadInt || F == GCWriteInt) {
---
>           else if (F == GCReadInt || F == GCWriteInt) {
188,191d184
<               Coerce(CI, 2, VoidPtr);
<               Coerce(CI, 3, VoidPtrPtr);
<             } else {
<               Coerce(CI, 1, VoidPtr);
192a186,187
>             } else {
>               Coerce(CI, 1, VoidPtrPtr);
197c192
<                 CallInst *NC = new CallInst(GCRead, CI->getOperand(1), CI->getOperand(2),
---
>                 CallInst *NC = new CallInst(GCRead, CI->getOperand(1),
212,234d206
< 
<   if(!GCRootValues.empty())
<     for (unsigned i = 0, e = GCRootValues.size(); i != e; ++i) {
<       Value* v = GCRootValues[i]->getOperand(1);
<       AllocaInst* a = new AllocaInst(v->getType(), 0, "stackGcRoot", cast<Instruction>(v));
<       GCRootValues[i]->setOperand(1, a);
< 
<       std::vector<Instruction*> vUses;
<       std::vector<Instruction*> vLoads;
< 
<       for (Value::use_iterator j = v->use_begin(), e = v->use_end(); j != e; ++j) {
<         if (Instruction *Inst = dyn_cast<Instruction>(*j)) {
<           LoadInst* l = new LoadInst(a, "loadGcRoot", Inst);
<           vUses.push_back(Inst); vLoads.push_back(l);
<         }
<       }
< 
<       for (unsigned j = 0, e = vUses.size(); j != e; ++j)
<       	vUses[j]->replaceUsesOfWith(v, vLoads[j]);
<       vUses.clear(); vLoads.clear();
< 
<       new StoreInst(v, a, GCRootValues[i]);
<     }


More information about the llvm-dev mailing list