#include "llvm/Transforms/Scalar/RewriteStatepointsForGC.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GCStrategy.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Statepoint.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#define DEBUG_TYPE …
usingnamespacellvm;
static cl::opt<bool> PrintLiveSet("spp-print-liveset", cl::Hidden,
cl::init(false));
static cl::opt<bool> PrintLiveSetSize("spp-print-liveset-size", cl::Hidden,
cl::init(false));
static cl::opt<bool> PrintBasePointers("spp-print-base-pointers", cl::Hidden,
cl::init(false));
static cl::opt<unsigned>
RematerializationThreshold("spp-rematerialization-threshold", cl::Hidden,
cl::init(6));
#ifdef EXPENSIVE_CHECKS
static bool ClobberNonLive = true;
#else
static bool ClobberNonLive = …;
#endif
static cl::opt<bool, true> ClobberNonLiveOverride("rs4gc-clobber-non-live",
cl::location(ClobberNonLive),
cl::Hidden);
static cl::opt<bool>
AllowStatepointWithNoDeoptInfo("rs4gc-allow-statepoint-with-no-deopt-info",
cl::Hidden, cl::init(true));
static cl::opt<bool> RematDerivedAtUses("rs4gc-remat-derived-at-uses",
cl::Hidden, cl::init(true));
static void stripNonValidData(Module &M);
static std::unique_ptr<GCStrategy> findGCStrategy(Function &F);
static bool shouldRewriteStatepointsIn(Function &F);
PreservedAnalyses RewriteStatepointsForGC::run(Module &M,
ModuleAnalysisManager &AM) { … }
namespace {
struct GCPtrLivenessData { … };
DefiningValueMapTy;
IsKnownBaseMapTy;
PointerToBaseTy;
StatepointLiveSetTy;
RematerializedValueMapTy;
struct PartiallyConstructedSafepointRecord { … };
struct RematerizlizationCandidateRecord { … };
RematCandTy;
}
static ArrayRef<Use> GetDeoptBundleOperands(const CallBase *Call) { … }
static void computeLiveInValues(DominatorTree &DT, Function &F,
GCPtrLivenessData &Data, GCStrategy *GC);
static void findLiveSetAtInst(Instruction *inst, GCPtrLivenessData &Data,
StatepointLiveSetTy &out, GCStrategy *GC);
static bool isGCPointerType(Type *T, GCStrategy *GC) { … }
static bool isHandledGCPointerType(Type *T, GCStrategy *GC) { … }
#ifndef NDEBUG
static bool containsGCPtrType(Type *Ty, GCStrategy *GC) {
if (isGCPointerType(Ty, GC))
return true;
if (VectorType *VT = dyn_cast<VectorType>(Ty))
return isGCPointerType(VT->getScalarType(), GC);
if (ArrayType *AT = dyn_cast<ArrayType>(Ty))
return containsGCPtrType(AT->getElementType(), GC);
if (StructType *ST = dyn_cast<StructType>(Ty))
return llvm::any_of(ST->elements(),
[GC](Type *Ty) { return containsGCPtrType(Ty, GC); });
return false;
}
static bool isUnhandledGCPointerType(Type *Ty, GCStrategy *GC) {
return containsGCPtrType(Ty, GC) && !isHandledGCPointerType(Ty, GC);
}
#endif
static std::string suffixed_name_or(Value *V, StringRef Suffix,
StringRef DefaultName) { … }
static void analyzeParsePointLiveness(
DominatorTree &DT, GCPtrLivenessData &OriginalLivenessData, CallBase *Call,
PartiallyConstructedSafepointRecord &Result, GCStrategy *GC) { … }
static bool isKnownBase(Value *V, const IsKnownBaseMapTy &KnownBases);
static void setKnownBase(Value *V, bool IsKnownBase,
IsKnownBaseMapTy &KnownBases);
static Value *findBaseDefiningValue(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases);
static Value *findBaseDefiningValueOfVector(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases) { … }
static Value *findBaseDefiningValue(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases) { … }
static Value *findBaseDefiningValueCached(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases) { … }
static Value *findBaseOrBDV(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases) { … }
#ifndef NDEBUG
static bool isOriginalBaseResult(Value *V) {
return !isa<PHINode>(V) && !isa<SelectInst>(V) &&
!isa<ExtractElementInst>(V) && !isa<InsertElementInst>(V) &&
!isa<ShuffleVectorInst>(V);
}
#endif
static bool isKnownBase(Value *V, const IsKnownBaseMapTy &KnownBases) { … }
static void setKnownBase(Value *V, bool IsKnownBase,
IsKnownBaseMapTy &KnownBases) { … }
static bool areBothVectorOrScalar(Value *First, Value *Second) { … }
namespace {
class BDVState { … };
}
#ifndef NDEBUG
static raw_ostream &operator<<(raw_ostream &OS, const BDVState &State) {
State.print(OS);
return OS;
}
#endif
static Value *findBasePointer(Value *I, DefiningValueMapTy &Cache,
IsKnownBaseMapTy &KnownBases) { … }
static void findBasePointers(const StatepointLiveSetTy &live,
PointerToBaseTy &PointerToBase, DominatorTree *DT,
DefiningValueMapTy &DVCache,
IsKnownBaseMapTy &KnownBases) { … }
static void findBasePointers(DominatorTree &DT, DefiningValueMapTy &DVCache,
CallBase *Call,
PartiallyConstructedSafepointRecord &result,
PointerToBaseTy &PointerToBase,
IsKnownBaseMapTy &KnownBases) { … }
static void recomputeLiveInValues(GCPtrLivenessData &RevisedLivenessData,
CallBase *Call,
PartiallyConstructedSafepointRecord &result,
PointerToBaseTy &PointerToBase,
GCStrategy *GC);
static void recomputeLiveInValues(
Function &F, DominatorTree &DT, ArrayRef<CallBase *> toUpdate,
MutableArrayRef<struct PartiallyConstructedSafepointRecord> records,
PointerToBaseTy &PointerToBase, GCStrategy *GC) { … }
static Instruction *rematerializeChain(ArrayRef<Instruction *> ChainToBase,
Instruction *InsertBefore,
Value *RootOfChain,
Value *AlternateLiveBase) { … }
static BasicBlock *
normalizeForInvokeSafepoint(BasicBlock *BB, BasicBlock *InvokeParent,
DominatorTree &DT) { … }
static constexpr Attribute::AttrKind FnAttrsToStrip[] = …;
static AttributeList legalizeCallAttributes(CallBase *Call, bool IsMemIntrinsic,
AttributeList StatepointAL) { … }
static void CreateGCRelocates(ArrayRef<Value *> LiveVariables,
ArrayRef<Value *> BasePtrs,
Instruction *StatepointToken,
IRBuilder<> &Builder, GCStrategy *GC) { … }
namespace {
class DeferredReplacement { … };
}
static StringRef getDeoptLowering(CallBase *Call) { … }
static void
makeStatepointExplicitImpl(CallBase *Call,
const SmallVectorImpl<Value *> &BasePtrs,
const SmallVectorImpl<Value *> &LiveVariables,
PartiallyConstructedSafepointRecord &Result,
std::vector<DeferredReplacement> &Replacements,
const PointerToBaseTy &PointerToBase,
GCStrategy *GC) { … }
static void
makeStatepointExplicit(DominatorTree &DT, CallBase *Call,
PartiallyConstructedSafepointRecord &Result,
std::vector<DeferredReplacement> &Replacements,
const PointerToBaseTy &PointerToBase, GCStrategy *GC) { … }
static void
insertRelocationStores(iterator_range<Value::user_iterator> GCRelocs,
DenseMap<Value *, AllocaInst *> &AllocaMap,
DenseSet<Value *> &VisitedLiveValues) { … }
static void insertRematerializationStores(
const RematerializedValueMapTy &RematerializedValues,
DenseMap<Value *, AllocaInst *> &AllocaMap,
DenseSet<Value *> &VisitedLiveValues) { … }
static void relocationViaAlloca(
Function &F, DominatorTree &DT, ArrayRef<Value *> Live,
ArrayRef<PartiallyConstructedSafepointRecord> Records) { … }
template <typename T> static void unique_unsorted(SmallVectorImpl<T> &Vec) { … }
static void insertUseHolderAfter(CallBase *Call, const ArrayRef<Value *> Values,
SmallVectorImpl<CallInst *> &Holders) { … }
static void findLiveReferences(
Function &F, DominatorTree &DT, ArrayRef<CallBase *> toUpdate,
MutableArrayRef<struct PartiallyConstructedSafepointRecord> records,
GCStrategy *GC) { … }
static Value* findRematerializableChainToBasePointer(
SmallVectorImpl<Instruction*> &ChainToBase,
Value *CurrentValue) { … }
static InstructionCost
chainToBasePointerCost(SmallVectorImpl<Instruction *> &Chain,
TargetTransformInfo &TTI) { … }
static bool AreEquivalentPhiNodes(PHINode &OrigRootPhi, PHINode &AlternateRootPhi) { … }
static void
findRematerializationCandidates(PointerToBaseTy PointerToBase,
RematCandTy &RematerizationCandidates,
TargetTransformInfo &TTI) { … }
static void rematerializeLiveValuesAtUses(
RematCandTy &RematerizationCandidates,
MutableArrayRef<PartiallyConstructedSafepointRecord> Records,
PointerToBaseTy &PointerToBase) { … }
static void rematerializeLiveValues(CallBase *Call,
PartiallyConstructedSafepointRecord &Info,
PointerToBaseTy &PointerToBase,
RematCandTy &RematerizationCandidates,
TargetTransformInfo &TTI) { … }
static bool inlineGetBaseAndOffset(Function &F,
SmallVectorImpl<CallInst *> &Intrinsics,
DefiningValueMapTy &DVCache,
IsKnownBaseMapTy &KnownBases) { … }
static bool insertParsePoints(Function &F, DominatorTree &DT,
TargetTransformInfo &TTI,
SmallVectorImpl<CallBase *> &ToUpdate,
DefiningValueMapTy &DVCache,
IsKnownBaseMapTy &KnownBases) { … }
static AttributeMask getParamAndReturnAttributesToRemove() { … }
static void stripNonValidAttributesFromPrototype(Function &F) { … }
static void stripInvalidMetadataFromInstruction(Instruction &I) { … }
static void stripNonValidDataFromBody(Function &F) { … }
static std::unique_ptr<GCStrategy> findGCStrategy(Function &F) { … }
static bool shouldRewriteStatepointsIn(Function &F) { … }
static void stripNonValidData(Module &M) { … }
bool RewriteStatepointsForGC::runOnFunction(Function &F, DominatorTree &DT,
TargetTransformInfo &TTI,
const TargetLibraryInfo &TLI) { … }
static void computeLiveInValues(BasicBlock::reverse_iterator Begin,
BasicBlock::reverse_iterator End,
SetVector<Value *> &LiveTmp, GCStrategy *GC) { … }
static void computeLiveOutSeed(BasicBlock *BB, SetVector<Value *> &LiveTmp,
GCStrategy *GC) { … }
static SetVector<Value *> computeKillSet(BasicBlock *BB, GCStrategy *GC) { … }
#ifndef NDEBUG
static void checkBasicSSA(DominatorTree &DT, SetVector<Value *> &Live,
Instruction *TI, bool TermOkay = false) {
for (Value *V : Live) {
if (auto *I = dyn_cast<Instruction>(V)) {
if (TermOkay && TI == I)
continue;
assert(DT.dominates(I, TI) &&
"basic SSA liveness expectation violated by liveness analysis");
}
}
}
static void checkBasicSSA(DominatorTree &DT, GCPtrLivenessData &Data,
BasicBlock &BB) {
checkBasicSSA(DT, Data.LiveSet[&BB], BB.getTerminator());
checkBasicSSA(DT, Data.LiveOut[&BB], BB.getTerminator(), true);
checkBasicSSA(DT, Data.LiveIn[&BB], BB.getTerminator());
}
#endif
static void computeLiveInValues(DominatorTree &DT, Function &F,
GCPtrLivenessData &Data, GCStrategy *GC) { … }
static void findLiveSetAtInst(Instruction *Inst, GCPtrLivenessData &Data,
StatepointLiveSetTy &Out, GCStrategy *GC) { … }
static void recomputeLiveInValues(GCPtrLivenessData &RevisedLivenessData,
CallBase *Call,
PartiallyConstructedSafepointRecord &Info,
PointerToBaseTy &PointerToBase,
GCStrategy *GC) { … }