Introduce an example statepoint GC strategy
authorPhilip Reames <listmail@philipreames.com>
Wed, 7 Jan 2015 19:07:50 +0000 (19:07 +0000)
committerPhilip Reames <listmail@philipreames.com>
Wed, 7 Jan 2015 19:07:50 +0000 (19:07 +0000)
This change includes the most basic possible GCStrategy for a GC which is using the statepoint lowering code. At the moment, this GCStrategy doesn't really do much - aside from actually generate correct stackmaps that is - but I went ahead and added a few extra correctness checks as proof of concept. It's mostly here to provide documentation on how to do one, and to provide a point for various optimization legality hooks I'd like to add going forward. (For context, see the TODOs in InstCombine around gc.relocate.)

Most of the validation logic added here as proof of concept will soon move in to the Verifier.  That move is dependent on http://reviews.llvm.org/D6811

There was discussion in the review thread about addrspace(1) being reserved for something.  I'm going to follow up on a seperate llvmdev thread.  If needed, I'll update all the code at once.

Note that I am deliberately not making a GCStrategy required to use gc.statepoints with this change. I want to give folks out of tree - including myself - a chance to migrate. In a week or two, I'll make having a GCStrategy be required for gc.statepoints. To this end, I added the gc tag to one of the test cases but not others.

Differential Revision: http://reviews.llvm.org/D6808

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225365 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/CodeGen/GCStrategy.h
include/llvm/CodeGen/GCs.h
include/llvm/CodeGen/LinkAllCodegenComponents.h
lib/CodeGen/AsmPrinter/AsmPrinter.cpp
lib/CodeGen/CMakeLists.txt
lib/CodeGen/GCStrategy.cpp
lib/CodeGen/SelectionDAG/StatepointLowering.cpp
test/CodeGen/X86/statepoint-forward.ll

index 98c25e4a08de69555cdebf840af91a87c9bba752..0b0c3124c537c24179733c8dee6d2503ec66b195 100644 (file)
@@ -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 @@ namespace llvm {
     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 @@ namespace llvm {
     /// 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<bool> 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 @@ namespace llvm {
     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 bb170c85cbf834bcc44392f6b095a1eb0e1862ea..51a31842a5c00bca5ba1a1d4ce25619b42245183 100644 (file)
@@ -36,6 +36,8 @@ namespace llvm {
   /// Creates a shadow stack garbage collector. This collector requires no code
   /// generator support.
   void linkShadowStackGC();
+
+  void linkStatepointExampleGC();
 }
 
 #endif
index 372c294da30616f08b467b4a7a13cc21e0cbec81..e7ccbfa617e52f93e57a1d6bab7d9a621ec0e8f7 100644 (file)
@@ -39,6 +39,7 @@ namespace {
       llvm::linkOcamlGC();
       llvm::linkErlangGC();
       llvm::linkShadowStackGC();
+      llvm::linkStatepointExampleGC();
 
       (void) llvm::createBURRListDAGScheduler(nullptr,
                                               llvm::CodeGenOpt::Default);
index ff8fc60e6030b1700db586a8c9fb54422f6c92f0..461b6d272dae3c06c84b09343c32ff28ac202b97 100644 (file)
@@ -2294,6 +2294,11 @@ GCMetadataPrinter *AsmPrinter::GetOrCreateGCPrinter(GCStrategy &S) {
   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 092346bacd718d2c5f92b28a01bcfc63249edc9d..48bf3545e7330fceeb545395e3cbd0525f258470 100644 (file)
@@ -104,6 +104,7 @@ add_llvm_library(LLVMCodeGen
   StackSlotColoring.cpp
   StackMapLivenessAnalysis.cpp
   StackMaps.cpp
+  StatepointExampleGC.cpp
   TailDuplication.cpp
   TargetFrameLoweringImpl.cpp
   TargetInstrInfo.cpp
index 4b03e9e34c684ba7cde56d4be77abee0f9a8a20d..05c36fcb00d4c99cae475a9eaf2719f79ed43324 100644 (file)
@@ -93,6 +93,7 @@ namespace {
 // -----------------------------------------------------------------------------
 
 GCStrategy::GCStrategy() :
+  UseStatepoints(false),
   NeededSafePoints(0),
   CustomReadBarriers(false),
   CustomWriteBarriers(false),
index 972aa9c4c8d04ad5c0fd59875ec36477c0baa0fb..33c20d3f21958bf773152d223026d14766890f51 100644 (file)
@@ -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 @@ static void lowerStatepointMetaArgs(SmallVectorImpl<SDValue> &Ops,
   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 @@ void SelectionDAGBuilder::visitStatepoint(const CallInst &CI) {
   // 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 372a7284afabfd0d7a79514d43d0d91fd9e98dce..12a6ac2c72a9cc89457dcac0929ede3a6934bc9d 100644 (file)
@@ -20,7 +20,7 @@ declare void @func() readonly
 ;; 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 @@ entry:
 
 ;; 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 @@ declare i1 @f(i32 addrspace(1)* %v) readnone
 ; 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 @@ entry:
 
 ;; 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 @@ entry:
 ; 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