Index: llvm/include/llvm/IR/GCStrategy.h =================================================================== --- llvm/include/llvm/IR/GCStrategy.h +++ llvm/include/llvm/IR/GCStrategy.h @@ -76,6 +76,11 @@ /// this function's calls. /// This should only be set if UseStatepoints is set. + bool HasCompressedPointers = + false; /// Handles live pointers in addrspace(2) + /// as deopt values. + /// This should only be set if UseRS4GC is set. + bool NeededSafePoints = false; ///< if set, calls are inferred to be safepoints bool UsesMetadata = false; ///< If set, backend must emit metadata tables. @@ -110,6 +115,14 @@ return UseRS4GC; } + /// Returns true if the RewriteStatepointsForGC pass should track pointers + /// in addrspace(2) as deopt parameters. + bool hasCompressedPointers() const { + assert((!HasCompressedPointers || useRS4GC()) && + "GC strategy has hasCompressedPointers but not use RS4GC set"); + return HasCompressedPointers; + } + ///@} /// If set, appropriate metadata tables must be emitted by the back-end Index: llvm/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/include/llvm/IR/IRBuilder.h +++ llvm/include/llvm/IR/IRBuilder.h @@ -835,6 +835,16 @@ ArrayRef GCArgs, const Twine &Name = ""); + /// Create a call to the experimental.gc.statepoint intrinsic to + /// start a new statepoint sequence. + CallInst *CreateGCStatepointCall(uint64_t ID, uint32_t NumPatchBytes, + FunctionCallee ActualCallee, uint32_t Flags, + ArrayRef CallArgs, + Optional> TransitionArgs, + Optional> DeoptArgs, + ArrayRef GCArgs, + const Twine &Name = ""); + /// Conveninence function for the common case when CallArgs are filled /// in using ArrayRef(CS.arg_begin(), CS.arg_end()); Use needs to be /// .get()'ed to get the Value pointer. @@ -863,6 +873,15 @@ std::optional> DeoptArgs, ArrayRef GCArgs, const Twine &Name = ""); + /// Create an invoke to the experimental.gc.statepoint intrinsic to + /// start a new statepoint sequence. + InvokeInst *CreateGCStatepointInvoke( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualInvokee, + BasicBlock *NormalDest, BasicBlock *UnwindDest, uint32_t Flags, + ArrayRef InvokeArgs, Optional> TransitionArgs, + Optional> DeoptArgs, ArrayRef GCArgs, + const Twine &Name = ""); + // Convenience function for the common case when CallArgs are filled in using // ArrayRef(CS.arg_begin(), CS.arg_end()); Use needs to be .get()'ed to // get the Value *. Index: llvm/lib/IR/BuiltinGCs.cpp =================================================================== --- llvm/lib/IR/BuiltinGCs.cpp +++ llvm/lib/IR/BuiltinGCs.cpp @@ -114,6 +114,33 @@ } }; +/// A GC strategy for the LLVM backend of the GraalVM compiler. The strategy +/// is similar to the statepoint-example GC, but adds support for a second +/// type of pointer in addrspace(2). Live pointers in this address space +/// are inserted as deopt parameters as well as GC parameters in the +/// statepoint intrinsic. As a consequence, this GC doesn't support regular +/// deopt parameters +class CompressedPointerGC : public GCStrategy { +public: + CompressedPointerGC() { + UseStatepoints = true; + UseRS4GC = true; + HasCompressedPointers = true; + // These options are all gc.root specific, we specify them so that the + // gc.root lowering code doesn't run. + NeededSafePoints = false; + UsesMetadata = false; + } + + Optional isGCManagedPointer(const Type *Ty) const override { + // Method is only valid on pointer typed values. + const PointerType *PT = cast(Ty); + // addrspace(1) represents absolute tracked pointers, and addrspace(2) + // represents heap base-relative pointers. + return (1 == PT->getAddressSpace() || 2 == PT->getAddressSpace()); + } +}; + } // end anonymous namespace // Register all the above so that they can be found at runtime. Note that @@ -127,6 +154,8 @@ static GCRegistry::Add D("statepoint-example", "an example strategy for statepoint"); static GCRegistry::Add E("coreclr", "CoreCLR-compatible GC"); +static GCRegistry::Add + F("compressed-pointer", "GC supporting compressed pointers"); // Provide hook to ensure the containing library is fully loaded. void llvm::linkAllBuiltinGCs() {} Index: llvm/lib/IR/IRBuilder.cpp =================================================================== --- llvm/lib/IR/IRBuilder.cpp +++ llvm/lib/IR/IRBuilder.cpp @@ -843,6 +843,17 @@ DeoptArgs, GCArgs, Name); } +CallInst *IRBuilderBase::CreateGCStatepointCall( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualCallee, + uint32_t Flags, ArrayRef CallArgs, + Optional> TransitionArgs, + Optional> DeoptArgs, ArrayRef GCArgs, + const Twine &Name) { + return CreateGCStatepointCallCommon( + this, ID, NumPatchBytes, ActualCallee, Flags, CallArgs, TransitionArgs, + DeoptArgs, GCArgs, Name); +} + CallInst *IRBuilderBase::CreateGCStatepointCall( uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualCallee, ArrayRef CallArgs, std::optional> DeoptArgs, @@ -901,6 +912,17 @@ InvokeArgs, TransitionArgs, DeoptArgs, GCArgs, Name); } +InvokeInst *IRBuilderBase::CreateGCStatepointInvoke( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualInvokee, + BasicBlock *NormalDest, BasicBlock *UnwindDest, uint32_t Flags, + ArrayRef InvokeArgs, Optional> TransitionArgs, + Optional> DeoptArgs, ArrayRef GCArgs, + const Twine &Name) { + return CreateGCStatepointInvokeCommon( + this, ID, NumPatchBytes, ActualInvokee, NormalDest, UnwindDest, Flags, + InvokeArgs, TransitionArgs, DeoptArgs, GCArgs, Name); +} + InvokeInst *IRBuilderBase::CreateGCStatepointInvoke( uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualInvokee, BasicBlock *NormalDest, BasicBlock *UnwindDest, ArrayRef InvokeArgs, Index: llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp =================================================================== --- llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -1666,6 +1666,15 @@ return "live-through"; } +static void getCompressedGCArgs(std::vector &CompressedArgsVector, + ArrayRef GCArgs) { + for (Value *LiveVal : GCArgs) { + if (auto *PT = dyn_cast(LiveVal->getType())) + if (PT->getAddressSpace() == 2) + CompressedArgsVector.push_back(LiveVal); + } +} + static void makeStatepointExplicitImpl(CallBase *Call, /* to replace */ const SmallVectorImpl &BasePtrs, @@ -1850,9 +1859,22 @@ // Create the statepoint given all the arguments GCStatepointInst *Token = nullptr; if (auto *CI = dyn_cast(Call)) { - CallInst *SPCall = Builder.CreateGCStatepointCall( - StatepointID, NumPatchBytes, CallTarget, Flags, CallArgs, - TransitionArgs, DeoptArgs, GCArgs, "safepoint_token"); + CallInst *SPCall; + if (GC->hasCompressedPointers()) { + assert(DeoptArgs->empty() && + "Deopt args are not supported when using compressed pointers"); + std::vector CompressedArgsVector; + getCompressedGCArgs(CompressedArgsVector, GCArgs); + ArrayRef CompressedArgs(CompressedArgsVector); + + SPCall = Builder.CreateGCStatepointCall( + StatepointID, NumPatchBytes, CallTarget, Flags, CallArgs, + TransitionArgs, CompressedArgs, GCArgs, "safepoint_token"); + } else { + SPCall = Builder.CreateGCStatepointCall( + StatepointID, NumPatchBytes, CallTarget, Flags, CallArgs, + TransitionArgs, DeoptArgs, GCArgs, "safepoint_token"); + } SPCall->setTailCallKind(CI->getTailCallKind()); SPCall->setCallingConv(CI->getCallingConv()); @@ -1877,10 +1899,24 @@ // Insert the new invoke into the old block. We'll remove the old one in a // moment at which point this will become the new terminator for the // original block. - InvokeInst *SPInvoke = Builder.CreateGCStatepointInvoke( - StatepointID, NumPatchBytes, CallTarget, II->getNormalDest(), - II->getUnwindDest(), Flags, CallArgs, TransitionArgs, DeoptArgs, GCArgs, - "statepoint_token"); + InvokeInst *SPInvoke; + if (GC->hasCompressedPointers()) { + assert(DeoptArgs->empty() && + "Deopt args are not supported when using compressed pointers"); + std::vector CompressedArgsVector; + getCompressedGCArgs(CompressedArgsVector, GCArgs); + ArrayRef CompressedArgs(CompressedArgsVector); + + SPInvoke = Builder.CreateGCStatepointInvoke( + StatepointID, NumPatchBytes, CallTarget, II->getNormalDest(), + II->getUnwindDest(), Flags, CallArgs, TransitionArgs, CompressedArgs, + GCArgs, "statepoint_token"); + } else { + SPInvoke = Builder.CreateGCStatepointInvoke( + StatepointID, NumPatchBytes, CallTarget, II->getNormalDest(), + II->getUnwindDest(), Flags, CallArgs, TransitionArgs, DeoptArgs, + GCArgs, "statepoint_token"); + } SPInvoke->setCallingConv(II->getCallingConv());