Index: llvm/trunk/include/llvm/CodeGen/GCStrategy.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GCStrategy.h +++ llvm/trunk/include/llvm/CodeGen/GCStrategy.h @@ -50,6 +50,7 @@ #ifndef LLVM_CODEGEN_GCSTRATEGY_H #define LLVM_CODEGEN_GCSTRATEGY_H +#include "llvm/ADT/Optional.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/Support/Registry.h" @@ -67,6 +68,10 @@ friend class GCModuleInfo; protected: + bool UseStatepoints; /// Uses gc.statepoints as opposed to gc.roots, + /// if set, none of the other options can be + /// anything but their default values. + unsigned NeededSafePoints; ///< Bitmask of required safe points. bool CustomReadBarriers; ///< Default is to insert loads. bool CustomWriteBarriers; ///< Default is to insert stores. @@ -94,6 +99,22 @@ /// them. bool customReadBarrier() const { return CustomReadBarriers; } + /// Returns true if this strategy is expecting the use of gc.statepoints, + /// and false otherwise. + bool useStatepoints() const { return UseStatepoints; } + + /** @name Statepoint Specific Properties */ + ///@{ + + /// If the value specified can be reliably distinguished, returns true for + /// pointers to GC managed locations and false for pointers to non-GC + /// managed locations. Note a GCStrategy can always return 'None' (i.e. an + /// empty optional indicating it can't reliably distinguish. + virtual Optional isGCManagedPointer(const Value *V) const { + return None; + } + ///@} + /** @name GCRoot Specific Properties * These properties and overrides only apply to collector strategies using * GCRoot. @@ -126,7 +147,9 @@ bool initializeRoots() const { return InitRoots; } /// If set, appropriate metadata tables must be emitted by the back-end - /// (assembler, JIT, or otherwise). + /// (assembler, JIT, or otherwise). For statepoint, this method is + /// currently unsupported. The stackmap information can be found in the + /// StackMap section as described in the documentation. bool usesMetadata() const { return UsesMetadata; } ///@} Index: llvm/trunk/include/llvm/CodeGen/GCs.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GCs.h +++ llvm/trunk/include/llvm/CodeGen/GCs.h @@ -36,6 +36,8 @@ /// Creates a shadow stack garbage collector. This collector requires no code /// generator support. void linkShadowStackGC(); + + void linkStatepointExampleGC(); } #endif Index: llvm/trunk/include/llvm/CodeGen/LinkAllCodegenComponents.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/LinkAllCodegenComponents.h +++ llvm/trunk/include/llvm/CodeGen/LinkAllCodegenComponents.h @@ -39,6 +39,7 @@ llvm::linkOcamlGC(); llvm::linkErlangGC(); llvm::linkShadowStackGC(); + llvm::linkStatepointExampleGC(); (void) llvm::createBURRListDAGScheduler(nullptr, llvm::CodeGenOpt::Default); Index: llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2294,6 +2294,11 @@ if (!S.usesMetadata()) return nullptr; + assert(!S.useStatepoints() && "statepoints do not currently support custom" + " stackmap formats, please see the documentation for a description of" + " the default format. If you really need a custom serialized format," + " please file a bug"); + gcp_map_type &GCMap = getGCMap(GCMetadataPrinters); gcp_map_type::iterator GCPI = GCMap.find(&S); if (GCPI != GCMap.end()) Index: llvm/trunk/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/trunk/lib/CodeGen/CMakeLists.txt +++ llvm/trunk/lib/CodeGen/CMakeLists.txt @@ -104,6 +104,7 @@ StackSlotColoring.cpp StackMapLivenessAnalysis.cpp StackMaps.cpp + StatepointExampleGC.cpp TailDuplication.cpp TargetFrameLoweringImpl.cpp TargetInstrInfo.cpp Index: llvm/trunk/lib/CodeGen/GCStrategy.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GCStrategy.cpp +++ llvm/trunk/lib/CodeGen/GCStrategy.cpp @@ -93,6 +93,7 @@ // ----------------------------------------------------------------------------- GCStrategy::GCStrategy() : + UseStatepoints(false), NeededSafePoints(0), CustomReadBarriers(false), CustomWriteBarriers(false), Index: llvm/trunk/lib/CodeGen/SelectionDAG/StatepointLowering.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/StatepointLowering.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/StatepointLowering.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" +#include "llvm/CodeGen/GCStrategy.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/StackMaps.h" #include "llvm/IR/CallingConv.h" @@ -417,6 +418,39 @@ getIncomingStatepointGCValues(Bases, Ptrs, Relocations, Statepoint.getCallSite(), Builder); +#ifndef NDEBUG + // Check that each of the gc pointer and bases we've gotten out of the + // safepoint is something the strategy thinks might be a pointer into the GC + // heap. This is basically just here to help catch errors during statepoint + // insertion. TODO: This should actually be in the Verifier, but we can't get + // to the GCStrategy from there (yet). + if (Builder.GFI) { + GCStrategy &S = Builder.GFI->getStrategy(); + for (const Value *V : Bases) { + auto Opt = S.isGCManagedPointer(V); + if (Opt.hasValue()) { + assert(Opt.getValue() && + "non gc managed base pointer found in statepoint"); + } + } + for (const Value *V : Ptrs) { + auto Opt = S.isGCManagedPointer(V); + if (Opt.hasValue()) { + assert(Opt.getValue() && + "non gc managed derived pointer found in statepoint"); + } + } + for (const Value *V : Relocations) { + auto Opt = S.isGCManagedPointer(V); + if (Opt.hasValue()) { + assert(Opt.getValue() && "non gc managed pointer relocated"); + } + } + } +#endif + + + // Before we actually start lowering (and allocating spill slots for values), // reserve any stack slots which we judge to be profitable to reuse for a // particular value. This is purely an optimization over the code below and @@ -498,6 +532,15 @@ // This should catch any IR level mistake that's made when constructing or // transforming statepoints. ISP.verify(); + + // Check that the associated GCStrategy expects to encounter statepoints. + // TODO: This if should become an assert. For now, we allow the GCStrategy + // to be optional for backwards compatibility. This will only last a short + // period (i.e. a couple of weeks). + if (GFI) { + assert(GFI->getStrategy().useStatepoints() && + "GCStrategy does not expect to encounter statepoints"); + } #endif Index: llvm/trunk/test/CodeGen/X86/statepoint-forward.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/statepoint-forward.ll +++ llvm/trunk/test/CodeGen/X86/statepoint-forward.ll @@ -20,7 +20,7 @@ ;; Forwarding the value of a pointer load is invalid since it may have ;; changed at the safepoint. Forwarding a non-gc pointer value would ;; be valid, but is not currently implemented. -define i1 @test_load_forward(i32 addrspace(1)* addrspace(1)* %p) { +define i1 @test_load_forward(i32 addrspace(1)* addrspace(1)* %p) gc "statepoint-example" { entry: %before = load i32 addrspace(1)* addrspace(1)* %p %cmp1 = call i1 @f(i32 addrspace(1)* %before) @@ -39,7 +39,7 @@ ;; Same as above, but forwarding from a store define i1 @test_store_forward(i32 addrspace(1)* addrspace(1)* %p, - i32 addrspace(1)* %v) { + i32 addrspace(1)* %v) gc "statepoint-example" { entry: %cmp1 = call i1 @f(i32 addrspace(1)* %v) call void @llvm.assume(i1 %cmp1) @@ -67,7 +67,7 @@ ; that is not itself GC managed. The GC may have an external mechanism ; to know about and update that value at a safepoint. Note that the ; statepoint does not provide the collector with this root. -define i1 @test_load_forward_nongc_heap(i32 addrspace(1)** %p) { +define i1 @test_load_forward_nongc_heap(i32 addrspace(1)** %p) gc "statepoint-example" { entry: %before = load i32 addrspace(1)** %p %cmp1 = call i1 @f(i32 addrspace(1)* %before) @@ -85,7 +85,7 @@ ;; Same as above, but forwarding from a store define i1 @test_store_forward_nongc_heap(i32 addrspace(1)** %p, - i32 addrspace(1)* %v) { + i32 addrspace(1)* %v) gc "statepoint-example" { entry: %cmp1 = call i1 @f(i32 addrspace(1)* %v) call void @llvm.assume(i1 %cmp1) @@ -101,7 +101,6 @@ ; CHECK-LLC: callq f } - declare void @llvm.assume(i1) declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()*, i32, i32, ...) declare i32 addrspace(1)* addrspace(1)* @llvm.experimental.gc.relocate.p1p1i32(i32, i32, i32) #3