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]);
< }