RewriteStatepointsForGC.cpp [plain text]
#include "llvm/Pass.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Statepoint.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#define DEBUG_TYPE "rewrite-statepoints-for-gc"
using namespace llvm;
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 XDEBUG
static bool ClobberNonLive = true;
#else
static bool ClobberNonLive = false;
#endif
static cl::opt<bool, true> ClobberNonLiveOverride("rs4gc-clobber-non-live",
cl::location(ClobberNonLive),
cl::Hidden);
static cl::opt<bool> UseDeoptBundles("rs4gc-use-deopt-bundles", cl::Hidden,
cl::init(false));
static cl::opt<bool>
AllowStatepointWithNoDeoptInfo("rs4gc-allow-statepoint-with-no-deopt-info",
cl::Hidden, cl::init(true));
namespace {
struct RewriteStatepointsForGC : public ModulePass {
static char ID;
RewriteStatepointsForGC() : ModulePass(ID) {
initializeRewriteStatepointsForGCPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &F);
bool runOnModule(Module &M) override {
bool Changed = false;
for (Function &F : M)
Changed |= runOnFunction(F);
if (Changed) {
stripNonValidAttributes(M);
}
return Changed;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<TargetTransformInfoWrapperPass>();
}
void stripNonValidAttributes(Module &M);
void stripNonValidAttributesFromBody(Function &F);
void stripNonValidAttributesFromPrototype(Function &F);
};
}
char RewriteStatepointsForGC::ID = 0;
ModulePass *llvm::createRewriteStatepointsForGCPass() {
return new RewriteStatepointsForGC();
}
INITIALIZE_PASS_BEGIN(RewriteStatepointsForGC, "rewrite-statepoints-for-gc",
"Make relocations explicit at statepoints", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_END(RewriteStatepointsForGC, "rewrite-statepoints-for-gc",
"Make relocations explicit at statepoints", false, false)
namespace {
struct GCPtrLivenessData {
DenseMap<BasicBlock *, DenseSet<Value *>> KillSet;
DenseMap<BasicBlock *, DenseSet<Value *>> LiveSet;
DenseMap<BasicBlock *, DenseSet<Value *>> LiveIn;
DenseMap<BasicBlock *, DenseSet<Value *>> LiveOut;
};
typedef DenseMap<Value *, Value *> DefiningValueMapTy;
typedef DenseSet<Value *> StatepointLiveSetTy;
typedef DenseMap<AssertingVH<Instruction>, AssertingVH<Value>>
RematerializedValueMapTy;
struct PartiallyConstructedSafepointRecord {
StatepointLiveSetTy LiveSet;
DenseMap<Value *, Value *> PointerToBase;
Instruction *StatepointToken;
Instruction *UnwindToken;
RematerializedValueMapTy RematerializedValues;
};
}
static ArrayRef<Use> GetDeoptBundleOperands(ImmutableCallSite CS) {
assert(UseDeoptBundles && "Should not be called otherwise!");
Optional<OperandBundleUse> DeoptBundle = CS.getOperandBundle("deopt");
if (!DeoptBundle.hasValue()) {
assert(AllowStatepointWithNoDeoptInfo &&
"Found non-leaf call without deopt info!");
return None;
}
return DeoptBundle.getValue().Inputs;
}
static void computeLiveInValues(DominatorTree &DT, Function &F,
GCPtrLivenessData &Data);
static void findLiveSetAtInst(Instruction *inst, GCPtrLivenessData &Data,
StatepointLiveSetTy &out);
static bool isGCPointerType(Type *T) {
if (auto *PT = dyn_cast<PointerType>(T))
return (1 == PT->getAddressSpace());
return false;
}
static bool isHandledGCPointerType(Type *T) {
if (isGCPointerType(T))
return true;
if (auto VT = dyn_cast<VectorType>(T))
if (isGCPointerType(VT->getElementType()))
return true;
return false;
}
#ifndef NDEBUG
static bool containsGCPtrType(Type *Ty) {
if (isGCPointerType(Ty))
return true;
if (VectorType *VT = dyn_cast<VectorType>(Ty))
return isGCPointerType(VT->getScalarType());
if (ArrayType *AT = dyn_cast<ArrayType>(Ty))
return containsGCPtrType(AT->getElementType());
if (StructType *ST = dyn_cast<StructType>(Ty))
return std::any_of(
ST->subtypes().begin(), ST->subtypes().end(),
[](Type *SubType) { return containsGCPtrType(SubType); });
return false;
}
static bool isUnhandledGCPointerType(Type *Ty) {
return containsGCPtrType(Ty) && !isHandledGCPointerType(Ty);
}
#endif
static bool order_by_name(Value *a, Value *b) {
if (a->hasName() && b->hasName()) {
return -1 == a->getName().compare(b->getName());
} else if (a->hasName() && !b->hasName()) {
return true;
} else if (!a->hasName() && b->hasName()) {
return false;
} else {
return a < b;
}
}
static std::string suffixed_name_or(Value *V, StringRef Suffix,
StringRef DefaultName) {
return V->hasName() ? (V->getName() + Suffix).str() : DefaultName.str();
}
static void analyzeParsePointLiveness(
DominatorTree &DT, GCPtrLivenessData &OriginalLivenessData,
const CallSite &CS, PartiallyConstructedSafepointRecord &result) {
Instruction *inst = CS.getInstruction();
StatepointLiveSetTy LiveSet;
findLiveSetAtInst(inst, OriginalLivenessData, LiveSet);
if (PrintLiveSet) {
SmallVector<Value *, 64> Temp;
Temp.insert(Temp.end(), LiveSet.begin(), LiveSet.end());
std::sort(Temp.begin(), Temp.end(), order_by_name);
errs() << "Live Variables:\n";
for (Value *V : Temp)
dbgs() << " " << V->getName() << " " << *V << "\n";
}
if (PrintLiveSetSize) {
errs() << "Safepoint For: " << CS.getCalledValue()->getName() << "\n";
errs() << "Number live values: " << LiveSet.size() << "\n";
}
result.LiveSet = LiveSet;
}
static bool isKnownBaseResult(Value *V);
namespace {
struct BaseDefiningValueResult {
Value * const BDV;
const bool IsKnownBase;
BaseDefiningValueResult(Value *BDV, bool IsKnownBase)
: BDV(BDV), IsKnownBase(IsKnownBase) {
#ifndef NDEBUG
bool MustBeBase = isKnownBaseResult(BDV);
assert(!MustBeBase || MustBeBase == IsKnownBase);
#endif
}
};
}
static BaseDefiningValueResult findBaseDefiningValue(Value *I);
static BaseDefiningValueResult
findBaseDefiningValueOfVector(Value *I) {
assert(I->getType()->isVectorTy() &&
cast<VectorType>(I->getType())->getElementType()->isPointerTy() &&
"Illegal to ask for the base pointer of a non-pointer type");
if (isa<Argument>(I))
return BaseDefiningValueResult(I, true);
assert(!isa<GlobalVariable>(I) &&
"unexpected global variable found in base of vector");
if (isa<UndefValue>(I))
return BaseDefiningValueResult(I, true);
if (Constant *Con = dyn_cast<Constant>(I)) {
assert(!isa<GlobalVariable>(I) && !isa<UndefValue>(I) &&
"order of checks wrong!");
assert(Con->isNullValue() && "null is the only case which makes sense");
return BaseDefiningValueResult(Con, true);
}
if (isa<LoadInst>(I))
return BaseDefiningValueResult(I, true);
if (isa<InsertElementInst>(I))
return BaseDefiningValueResult(I, false);
if (isa<ShuffleVectorInst>(I))
return BaseDefiningValueResult(I, false);
assert((isa<SelectInst>(I) || isa<PHINode>(I)) &&
"unknown vector instruction - no base found for vector element");
return BaseDefiningValueResult(I, false);
}
static BaseDefiningValueResult findBaseDefiningValue(Value *I) {
if (I->getType()->isVectorTy())
return findBaseDefiningValueOfVector(I);
assert(I->getType()->isPointerTy() &&
"Illegal to ask for the base pointer of a non-pointer type");
if (isa<Argument>(I))
return BaseDefiningValueResult(I, true);
if (isa<GlobalVariable>(I))
return BaseDefiningValueResult(I, true);
if (isa<UndefValue>(I))
return BaseDefiningValueResult(I, true);
if (isa<Constant>(I)) {
assert(!isa<GlobalVariable>(I) && !isa<UndefValue>(I) &&
"order of checks wrong!");
assert(isa<ConstantPointerNull>(I) &&
"null is the only case which makes sense");
return BaseDefiningValueResult(I, true);
}
if (CastInst *CI = dyn_cast<CastInst>(I)) {
Value *Def = CI->stripPointerCasts();
assert(!isa<CastInst>(Def) && "shouldn't find another cast here");
return findBaseDefiningValue(Def);
}
if (isa<LoadInst>(I))
return BaseDefiningValueResult(I, true);
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(I))
return findBaseDefiningValue(GEP->getPointerOperand());
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
switch (II->getIntrinsicID()) {
case Intrinsic::experimental_gc_result_ptr:
default:
break;
case Intrinsic::experimental_gc_statepoint:
case Intrinsic::experimental_gc_result_float:
case Intrinsic::experimental_gc_result_int:
llvm_unreachable("these don't produce pointers");
case Intrinsic::experimental_gc_relocate: {
llvm_unreachable("repeat safepoint insertion is not supported");
}
case Intrinsic::gcroot:
llvm_unreachable(
"interaction with the gcroot mechanism is not supported");
}
}
if (isa<CallInst>(I) || isa<InvokeInst>(I))
return BaseDefiningValueResult(I, true);
assert(!isa<LandingPadInst>(I) && "Landing Pad is unimplemented");
if (isa<AtomicCmpXchgInst>(I))
return BaseDefiningValueResult(I, true);
assert(!isa<AtomicRMWInst>(I) && "Xchg handled above, all others are "
"binary ops which don't apply to pointers");
if (isa<ExtractValueInst>(I))
return BaseDefiningValueResult(I, true);
assert(!isa<InsertValueInst>(I) &&
"Base pointer for a struct is meaningless");
if (isa<ExtractElementInst>(I))
return BaseDefiningValueResult(I, false);
assert((isa<SelectInst>(I) || isa<PHINode>(I)) &&
"missing instruction case in findBaseDefiningValing");
return BaseDefiningValueResult(I, false);
}
static Value *findBaseDefiningValueCached(Value *I, DefiningValueMapTy &Cache) {
Value *&Cached = Cache[I];
if (!Cached) {
Cached = findBaseDefiningValue(I).BDV;
DEBUG(dbgs() << "fBDV-cached: " << I->getName() << " -> "
<< Cached->getName() << "\n");
}
assert(Cache[I] != nullptr);
return Cached;
}
static Value *findBaseOrBDV(Value *I, DefiningValueMapTy &Cache) {
Value *Def = findBaseDefiningValueCached(I, Cache);
auto Found = Cache.find(Def);
if (Found != Cache.end()) {
return Found->second;
}
return Def;
}
static bool isKnownBaseResult(Value *V) {
if (!isa<PHINode>(V) && !isa<SelectInst>(V) &&
!isa<ExtractElementInst>(V) && !isa<InsertElementInst>(V) &&
!isa<ShuffleVectorInst>(V)) {
return true;
}
if (isa<Instruction>(V) &&
cast<Instruction>(V)->getMetadata("is_base_value")) {
return true;
}
return false;
}
namespace {
class BDVState {
public:
enum Status { Unknown, Base, Conflict };
BDVState(Status s, Value *b = nullptr) : status(s), base(b) {
assert(status != Base || b);
}
explicit BDVState(Value *b) : status(Base), base(b) {}
BDVState() : status(Unknown), base(nullptr) {}
Status getStatus() const { return status; }
Value *getBase() const { return base; }
bool isBase() const { return getStatus() == Base; }
bool isUnknown() const { return getStatus() == Unknown; }
bool isConflict() const { return getStatus() == Conflict; }
bool operator==(const BDVState &other) const {
return base == other.base && status == other.status;
}
bool operator!=(const BDVState &other) const { return !(*this == other); }
LLVM_DUMP_METHOD
void dump() const { print(dbgs()); dbgs() << '\n'; }
void print(raw_ostream &OS) const {
switch (status) {
case Unknown:
OS << "U";
break;
case Base:
OS << "B";
break;
case Conflict:
OS << "C";
break;
};
OS << " (" << base << " - "
<< (base ? base->getName() : "nullptr") << "): ";
}
private:
Status status;
Value *base; };
}
#ifndef NDEBUG
static raw_ostream &operator<<(raw_ostream &OS, const BDVState &State) {
State.print(OS);
return OS;
}
#endif
namespace {
class MeetBDVStates {
public:
MeetBDVStates() {}
void meetWith(BDVState otherState) {
currentResult = meet(otherState, currentResult);
}
BDVState getResult() const { return currentResult; }
private:
BDVState currentResult;
static BDVState meet(BDVState LHS, BDVState RHS) {
assert((pureMeet(LHS, RHS) == pureMeet(RHS, LHS)) &&
"math is wrong: meet does not commute!");
BDVState Result = pureMeet(LHS, RHS);
DEBUG(dbgs() << "meet of " << LHS << " with " << RHS
<< " produced " << Result << "\n");
return Result;
}
static BDVState pureMeet(const BDVState &stateA, const BDVState &stateB) {
switch (stateA.getStatus()) {
case BDVState::Unknown:
return stateB;
case BDVState::Base:
assert(stateA.getBase() && "can't be null");
if (stateB.isUnknown())
return stateA;
if (stateB.isBase()) {
if (stateA.getBase() == stateB.getBase()) {
assert(stateA == stateB && "equality broken!");
return stateA;
}
return BDVState(BDVState::Conflict);
}
assert(stateB.isConflict() && "only three states!");
return BDVState(BDVState::Conflict);
case BDVState::Conflict:
return stateA;
}
llvm_unreachable("only three states!");
}
};
}
static Value *findBasePointer(Value *I, DefiningValueMapTy &cache) {
Value *def = findBaseOrBDV(I, cache);
if (isKnownBaseResult(def)) {
return def;
}
#ifndef NDEBUG
auto isExpectedBDVType = [](Value *BDV) {
return isa<PHINode>(BDV) || isa<SelectInst>(BDV) ||
isa<ExtractElementInst>(BDV) || isa<InsertElementInst>(BDV);
};
#endif
MapVector<Value *, BDVState> States;
{
SmallVector<Value*, 16> Worklist;
Worklist.push_back(def);
States.insert(std::make_pair(def, BDVState()));
while (!Worklist.empty()) {
Value *Current = Worklist.pop_back_val();
assert(!isKnownBaseResult(Current) && "why did it get added?");
auto visitIncomingValue = [&](Value *InVal) {
Value *Base = findBaseOrBDV(InVal, cache);
if (isKnownBaseResult(Base))
return;
assert(isExpectedBDVType(Base) && "the only non-base values "
"we see should be base defining values");
if (States.insert(std::make_pair(Base, BDVState())).second)
Worklist.push_back(Base);
};
if (PHINode *Phi = dyn_cast<PHINode>(Current)) {
for (Value *InVal : Phi->incoming_values())
visitIncomingValue(InVal);
} else if (SelectInst *Sel = dyn_cast<SelectInst>(Current)) {
visitIncomingValue(Sel->getTrueValue());
visitIncomingValue(Sel->getFalseValue());
} else if (auto *EE = dyn_cast<ExtractElementInst>(Current)) {
visitIncomingValue(EE->getVectorOperand());
} else if (auto *IE = dyn_cast<InsertElementInst>(Current)) {
visitIncomingValue(IE->getOperand(0)); visitIncomingValue(IE->getOperand(1)); } else {
assert(isa<ShuffleVectorInst>(Current));
llvm_unreachable("unimplemented instruction case");
}
}
}
#ifndef NDEBUG
DEBUG(dbgs() << "States after initialization:\n");
for (auto Pair : States) {
DEBUG(dbgs() << " " << Pair.second << " for " << *Pair.first << "\n");
}
#endif
auto getStateForBDV = [&](Value *baseValue) {
if (isKnownBaseResult(baseValue))
return BDVState(baseValue);
auto I = States.find(baseValue);
assert(I != States.end() && "lookup failed!");
return I->second;
};
bool progress = true;
while (progress) {
#ifndef NDEBUG
const size_t oldSize = States.size();
#endif
progress = false;
for (auto Pair : States) {
Value *BDV = Pair.first;
assert(!isKnownBaseResult(BDV) && "why did it get added?");
auto getStateForInput = [&](Value *V) mutable {
Value *BDV = findBaseOrBDV(V, cache);
return getStateForBDV(BDV);
};
MeetBDVStates calculateMeet;
if (SelectInst *select = dyn_cast<SelectInst>(BDV)) {
calculateMeet.meetWith(getStateForInput(select->getTrueValue()));
calculateMeet.meetWith(getStateForInput(select->getFalseValue()));
} else if (PHINode *Phi = dyn_cast<PHINode>(BDV)) {
for (Value *Val : Phi->incoming_values())
calculateMeet.meetWith(getStateForInput(Val));
} else if (auto *EE = dyn_cast<ExtractElementInst>(BDV)) {
calculateMeet.meetWith(getStateForInput(EE->getVectorOperand()));
} else {
auto *IE = cast<InsertElementInst>(BDV);
calculateMeet.meetWith(getStateForInput(IE->getOperand(0)));
calculateMeet.meetWith(getStateForInput(IE->getOperand(1)));
}
BDVState oldState = States[BDV];
BDVState newState = calculateMeet.getResult();
if (oldState != newState) {
progress = true;
States[BDV] = newState;
}
}
assert(oldSize == States.size() &&
"fixed point shouldn't be adding any new nodes to state");
}
#ifndef NDEBUG
DEBUG(dbgs() << "States after meet iteration:\n");
for (auto Pair : States) {
DEBUG(dbgs() << " " << Pair.second << " for " << *Pair.first << "\n");
}
#endif
for (auto Pair : States) {
Instruction *I = cast<Instruction>(Pair.first);
BDVState State = Pair.second;
assert(!isKnownBaseResult(I) && "why did it get added?");
assert(!State.isUnknown() && "Optimistic algorithm didn't complete!");
if (State.isBase() && isa<ExtractElementInst>(I) &&
isa<VectorType>(State.getBase()->getType())) {
auto *EE = cast<ExtractElementInst>(I);
auto *BaseInst = ExtractElementInst::Create(State.getBase(),
EE->getIndexOperand(),
"base_ee", EE);
BaseInst->setMetadata("is_base_value", MDNode::get(I->getContext(), {}));
States[I] = BDVState(BDVState::Base, BaseInst);
}
if (isa<InsertElementInst>(I)) {
assert(State.isConflict());
}
if (!State.isConflict())
continue;
auto MakeBaseInstPlaceholder = [](Instruction *I) -> Instruction* {
if (isa<PHINode>(I)) {
BasicBlock *BB = I->getParent();
int NumPreds = std::distance(pred_begin(BB), pred_end(BB));
assert(NumPreds > 0 && "how did we reach here");
std::string Name = suffixed_name_or(I, ".base", "base_phi");
return PHINode::Create(I->getType(), NumPreds, Name, I);
} else if (SelectInst *Sel = dyn_cast<SelectInst>(I)) {
UndefValue *Undef = UndefValue::get(Sel->getType());
std::string Name = suffixed_name_or(I, ".base", "base_select");
return SelectInst::Create(Sel->getCondition(), Undef,
Undef, Name, Sel);
} else if (auto *EE = dyn_cast<ExtractElementInst>(I)) {
UndefValue *Undef = UndefValue::get(EE->getVectorOperand()->getType());
std::string Name = suffixed_name_or(I, ".base", "base_ee");
return ExtractElementInst::Create(Undef, EE->getIndexOperand(), Name,
EE);
} else {
auto *IE = cast<InsertElementInst>(I);
UndefValue *VecUndef = UndefValue::get(IE->getOperand(0)->getType());
UndefValue *ScalarUndef = UndefValue::get(IE->getOperand(1)->getType());
std::string Name = suffixed_name_or(I, ".base", "base_ie");
return InsertElementInst::Create(VecUndef, ScalarUndef,
IE->getOperand(2), Name, IE);
}
};
Instruction *BaseInst = MakeBaseInstPlaceholder(I);
BaseInst->setMetadata("is_base_value", MDNode::get(I->getContext(), {}));
States[I] = BDVState(BDVState::Conflict, BaseInst);
}
auto getBaseForInput = [&](Value *Input, Instruction *InsertPt) {
Value *BDV = findBaseOrBDV(Input, cache);
Value *Base = nullptr;
if (isKnownBaseResult(BDV)) {
Base = BDV;
} else {
assert(States.count(BDV));
Base = States[BDV].getBase();
}
assert(Base && "can't be null");
if (Base->getType() != Input->getType() &&
InsertPt) {
Base = new BitCastInst(Base, Input->getType(), "cast",
InsertPt);
}
return Base;
};
for (auto Pair : States) {
Instruction *BDV = cast<Instruction>(Pair.first);
BDVState State = Pair.second;
assert(!isKnownBaseResult(BDV) && "why did it get added?");
assert(!State.isUnknown() && "Optimistic algorithm didn't complete!");
if (!State.isConflict())
continue;
if (PHINode *basephi = dyn_cast<PHINode>(State.getBase())) {
PHINode *phi = cast<PHINode>(BDV);
unsigned NumPHIValues = phi->getNumIncomingValues();
for (unsigned i = 0; i < NumPHIValues; i++) {
Value *InVal = phi->getIncomingValue(i);
BasicBlock *InBB = phi->getIncomingBlock(i);
int blockIndex = basephi->getBasicBlockIndex(InBB);
if (blockIndex != -1) {
Value *oldBase = basephi->getIncomingValue(blockIndex);
basephi->addIncoming(oldBase, InBB);
#ifndef NDEBUG
Value *Base = getBaseForInput(InVal, nullptr);
assert(Base->stripPointerCasts() == oldBase->stripPointerCasts() &&
"sanity -- findBaseOrBDV should be pure!");
#endif
continue;
}
Value *Base = getBaseForInput(InVal, InBB->getTerminator());
basephi->addIncoming(Base, InBB);
}
assert(basephi->getNumIncomingValues() == NumPHIValues);
} else if (SelectInst *BaseSel = dyn_cast<SelectInst>(State.getBase())) {
SelectInst *Sel = cast<SelectInst>(BDV);
for (int i = 1; i <= 2; i++) {
Value *InVal = Sel->getOperand(i);
Value *Base = getBaseForInput(InVal, BaseSel);
BaseSel->setOperand(i, Base);
}
} else if (auto *BaseEE = dyn_cast<ExtractElementInst>(State.getBase())) {
Value *InVal = cast<ExtractElementInst>(BDV)->getVectorOperand();
Value *Base = getBaseForInput(InVal, BaseEE);
BaseEE->setOperand(0, Base);
} else {
auto *BaseIE = cast<InsertElementInst>(State.getBase());
auto *BdvIE = cast<InsertElementInst>(BDV);
auto UpdateOperand = [&](int OperandIdx) {
Value *InVal = BdvIE->getOperand(OperandIdx);
Value *Base = getBaseForInput(InVal, BaseIE);
BaseIE->setOperand(OperandIdx, Base);
};
UpdateOperand(0); UpdateOperand(1); }
}
DenseMap<Value *, Value *> ReverseMap;
SmallPtrSet<Instruction *, 16> NewInsts;
SmallSetVector<AssertingVH<Instruction>, 16> Worklist;
for (auto Pair : States) {
auto *BDV = Pair.first;
auto State = Pair.second;
Value *Base = State.getBase();
assert(BDV && Base);
assert(!isKnownBaseResult(BDV) && "why did it get added?");
assert(isKnownBaseResult(Base) &&
"must be something we 'know' is a base pointer");
if (!State.isConflict())
continue;
ReverseMap[Base] = BDV;
if (auto *BaseI = dyn_cast<Instruction>(Base)) {
NewInsts.insert(BaseI);
Worklist.insert(BaseI);
}
}
auto ReplaceBaseInstWith = [&](Value *BDV, Instruction *BaseI,
Value *Replacement) {
for (User *U : BaseI->users())
if (auto *UI = dyn_cast<Instruction>(U))
if (NewInsts.count(UI) && UI != BaseI)
Worklist.insert(UI);
NewInsts.erase(BaseI);
ReverseMap.erase(BaseI);
BaseI->replaceAllUsesWith(Replacement);
BaseI->eraseFromParent();
assert(States.count(BDV));
assert(States[BDV].isConflict() && States[BDV].getBase() == BaseI);
States[BDV] = BDVState(BDVState::Conflict, Replacement);
};
const DataLayout &DL = cast<Instruction>(def)->getModule()->getDataLayout();
while (!Worklist.empty()) {
Instruction *BaseI = Worklist.pop_back_val();
assert(NewInsts.count(BaseI));
Value *Bdv = ReverseMap[BaseI];
if (auto *BdvI = dyn_cast<Instruction>(Bdv))
if (BaseI->isIdenticalTo(BdvI)) {
DEBUG(dbgs() << "Identical Base: " << *BaseI << "\n");
ReplaceBaseInstWith(Bdv, BaseI, Bdv);
continue;
}
if (Value *V = SimplifyInstruction(BaseI, DL)) {
DEBUG(dbgs() << "Base " << *BaseI << " simplified to " << *V << "\n");
ReplaceBaseInstWith(Bdv, BaseI, V);
continue;
}
}
for (auto Pair : States) {
auto *BDV = Pair.first;
Value *base = Pair.second.getBase();
assert(BDV && base);
std::string fromstr = cache.count(BDV) ? cache[BDV]->getName() : "none";
DEBUG(dbgs() << "Updating base value cache"
<< " for: " << BDV->getName()
<< " from: " << fromstr
<< " to: " << base->getName() << "\n");
if (cache.count(BDV)) {
assert((!isKnownBaseResult(cache[BDV]) || cache[BDV] == base) &&
"base relation should be stable");
}
cache[BDV] = base;
}
assert(cache.find(def) != cache.end());
return cache[def];
}
static void
findBasePointers(const StatepointLiveSetTy &live,
DenseMap<Value *, Value *> &PointerToBase,
DominatorTree *DT, DefiningValueMapTy &DVCache) {
SmallVector<Value *, 64> Temp;
Temp.insert(Temp.end(), live.begin(), live.end());
std::sort(Temp.begin(), Temp.end(), order_by_name);
for (Value *ptr : Temp) {
Value *base = findBasePointer(ptr, DVCache);
assert(base && "failed to find base pointer");
PointerToBase[ptr] = base;
assert((!isa<Instruction>(base) || !isa<Instruction>(ptr) ||
DT->dominates(cast<Instruction>(base)->getParent(),
cast<Instruction>(ptr)->getParent())) &&
"The base we found better dominate the derived pointer");
assert(!isa<ConstantPointerNull>(base) &&
"the relocation code needs adjustment to handle the relocation of "
"a null pointer constant without causing false positives in the "
"safepoint ir verifier.");
}
}
static void findBasePointers(DominatorTree &DT, DefiningValueMapTy &DVCache,
const CallSite &CS,
PartiallyConstructedSafepointRecord &result) {
DenseMap<Value *, Value *> PointerToBase;
findBasePointers(result.LiveSet, PointerToBase, &DT, DVCache);
if (PrintBasePointers) {
errs() << "Base Pairs (w/o Relocation):\n";
SmallVector<Value *, 64> Temp;
Temp.reserve(PointerToBase.size());
for (auto Pair : PointerToBase) {
Temp.push_back(Pair.first);
}
std::sort(Temp.begin(), Temp.end(), order_by_name);
for (Value *Ptr : Temp) {
Value *Base = PointerToBase[Ptr];
errs() << " derived %" << Ptr->getName() << " base %" << Base->getName()
<< "\n";
}
}
result.PointerToBase = PointerToBase;
}
static void recomputeLiveInValues(GCPtrLivenessData &RevisedLivenessData,
const CallSite &CS,
PartiallyConstructedSafepointRecord &result);
static void recomputeLiveInValues(
Function &F, DominatorTree &DT, Pass *P, ArrayRef<CallSite> toUpdate,
MutableArrayRef<struct PartiallyConstructedSafepointRecord> records) {
GCPtrLivenessData RevisedLivenessData;
computeLiveInValues(DT, F, RevisedLivenessData);
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
const CallSite &CS = toUpdate[i];
recomputeLiveInValues(RevisedLivenessData, CS, info);
}
}
static BasicBlock *
normalizeForInvokeSafepoint(BasicBlock *BB, BasicBlock *InvokeParent,
DominatorTree &DT) {
BasicBlock *Ret = BB;
if (!BB->getUniquePredecessor())
Ret = SplitBlockPredecessors(BB, InvokeParent, "", &DT);
FoldSingleEntryPHINodes(Ret);
assert(!isa<PHINode>(Ret->begin()) &&
"All PHI nodes should have been removed!");
return Ret;
}
static AttributeSet legalizeCallAttributes(AttributeSet AS) {
AttributeSet Ret;
for (unsigned Slot = 0; Slot < AS.getNumSlots(); Slot++) {
unsigned Index = AS.getSlotIndex(Slot);
if (Index == AttributeSet::ReturnIndex ||
Index == AttributeSet::FunctionIndex) {
for (Attribute Attr : make_range(AS.begin(Slot), AS.end(Slot))) {
if (Attr.hasAttribute(Attribute::ReadNone) ||
Attr.hasAttribute(Attribute::ReadOnly))
continue;
if (Attr.hasAttribute("statepoint-num-patch-bytes") ||
Attr.hasAttribute("statepoint-id"))
continue;
Ret = Ret.addAttributes(
AS.getContext(), Index,
AttributeSet::get(AS.getContext(), Index, AttrBuilder(Attr)));
}
}
}
return Ret;
}
static void CreateGCRelocates(ArrayRef<Value *> LiveVariables,
const int LiveStart,
ArrayRef<Value *> BasePtrs,
Instruction *StatepointToken,
IRBuilder<> Builder) {
if (LiveVariables.empty())
return;
auto FindIndex = [](ArrayRef<Value *> LiveVec, Value *Val) {
auto ValIt = std::find(LiveVec.begin(), LiveVec.end(), Val);
assert(ValIt != LiveVec.end() && "Val not found in LiveVec!");
size_t Index = std::distance(LiveVec.begin(), ValIt);
assert(Index < LiveVec.size() && "Bug in std::find?");
return Index;
};
Module *M = StatepointToken->getModule();
auto AS = cast<PointerType>(LiveVariables[0]->getType())->getAddressSpace();
Type *Types[] = {Type::getInt8PtrTy(M->getContext(), AS)};
Value *GCRelocateDecl =
Intrinsic::getDeclaration(M, Intrinsic::experimental_gc_relocate, Types);
for (unsigned i = 0; i < LiveVariables.size(); i++) {
Value *BaseIdx =
Builder.getInt32(LiveStart + FindIndex(LiveVariables, BasePtrs[i]));
Value *LiveIdx = Builder.getInt32(LiveStart + i);
CallInst *Reloc = Builder.CreateCall(
GCRelocateDecl, {StatepointToken, BaseIdx, LiveIdx},
suffixed_name_or(LiveVariables[i], ".relocated", ""));
Reloc->setCallingConv(CallingConv::Cold);
}
}
namespace {
class DeferredReplacement {
AssertingVH<Instruction> Old;
AssertingVH<Instruction> New;
public:
explicit DeferredReplacement(Instruction *Old, Instruction *New) :
Old(Old), New(New) {
assert(Old != New && "Not allowed!");
}
void doReplacement() {
Instruction *OldI = Old;
Instruction *NewI = New;
assert(OldI != NewI && "Disallowed at construction?!");
Old = nullptr;
New = nullptr;
if (NewI)
OldI->replaceAllUsesWith(NewI);
OldI->eraseFromParent();
}
};
}
static void
makeStatepointExplicitImpl(const CallSite CS,
const SmallVectorImpl<Value *> &BasePtrs,
const SmallVectorImpl<Value *> &LiveVariables,
PartiallyConstructedSafepointRecord &Result,
std::vector<DeferredReplacement> &Replacements) {
assert(BasePtrs.size() == LiveVariables.size());
assert((UseDeoptBundles || isStatepoint(CS)) &&
"This method expects to be rewriting a statepoint");
Instruction *InsertBefore = CS.getInstruction();
IRBuilder<> Builder(InsertBefore);
ArrayRef<Value *> GCArgs(LiveVariables);
uint64_t StatepointID = 0xABCDEF00;
uint32_t NumPatchBytes = 0;
uint32_t Flags = uint32_t(StatepointFlags::None);
ArrayRef<Use> CallArgs;
ArrayRef<Use> DeoptArgs;
ArrayRef<Use> TransitionArgs;
Value *CallTarget = nullptr;
if (UseDeoptBundles) {
CallArgs = {CS.arg_begin(), CS.arg_end()};
DeoptArgs = GetDeoptBundleOperands(CS);
AttributeSet OriginalAttrs = CS.getAttributes();
Attribute AttrID = OriginalAttrs.getAttribute(AttributeSet::FunctionIndex,
"statepoint-id");
if (AttrID.isStringAttribute())
AttrID.getValueAsString().getAsInteger(10, StatepointID);
Attribute AttrNumPatchBytes = OriginalAttrs.getAttribute(
AttributeSet::FunctionIndex, "statepoint-num-patch-bytes");
if (AttrNumPatchBytes.isStringAttribute())
AttrNumPatchBytes.getValueAsString().getAsInteger(10, NumPatchBytes);
CallTarget = CS.getCalledValue();
} else {
Statepoint OldSP(CS);
StatepointID = OldSP.getID();
NumPatchBytes = OldSP.getNumPatchBytes();
Flags = OldSP.getFlags();
CallArgs = {OldSP.arg_begin(), OldSP.arg_end()};
DeoptArgs = {OldSP.vm_state_begin(), OldSP.vm_state_end()};
TransitionArgs = {OldSP.gc_transition_args_begin(),
OldSP.gc_transition_args_end()};
CallTarget = OldSP.getCalledValue();
}
Instruction *Token = nullptr;
AttributeSet ReturnAttrs;
if (CS.isCall()) {
CallInst *ToReplace = cast<CallInst>(CS.getInstruction());
CallInst *Call = Builder.CreateGCStatepointCall(
StatepointID, NumPatchBytes, CallTarget, Flags, CallArgs,
TransitionArgs, DeoptArgs, GCArgs, "safepoint_token");
Call->setTailCall(ToReplace->isTailCall());
Call->setCallingConv(ToReplace->getCallingConv());
AttributeSet NewAttrs = legalizeCallAttributes(ToReplace->getAttributes());
Call->setAttributes(NewAttrs.getFnAttributes());
ReturnAttrs = NewAttrs.getRetAttributes();
Token = Call;
assert(ToReplace->getNextNode() && "Not a terminator, must have next!");
Builder.SetInsertPoint(ToReplace->getNextNode());
Builder.SetCurrentDebugLocation(ToReplace->getNextNode()->getDebugLoc());
} else {
InvokeInst *ToReplace = cast<InvokeInst>(CS.getInstruction());
InvokeInst *Invoke = Builder.CreateGCStatepointInvoke(
StatepointID, NumPatchBytes, CallTarget, ToReplace->getNormalDest(),
ToReplace->getUnwindDest(), Flags, CallArgs, TransitionArgs, DeoptArgs,
GCArgs, "statepoint_token");
Invoke->setCallingConv(ToReplace->getCallingConv());
AttributeSet NewAttrs = legalizeCallAttributes(ToReplace->getAttributes());
Invoke->setAttributes(NewAttrs.getFnAttributes());
ReturnAttrs = NewAttrs.getRetAttributes();
Token = Invoke;
BasicBlock *UnwindBlock = ToReplace->getUnwindDest();
assert(!isa<PHINode>(UnwindBlock->begin()) &&
UnwindBlock->getUniquePredecessor() &&
"can't safely insert in this block!");
Builder.SetInsertPoint(&*UnwindBlock->getFirstInsertionPt());
Builder.SetCurrentDebugLocation(ToReplace->getDebugLoc());
Instruction *ExceptionalToken =
cast<Instruction>(Builder.CreateExtractValue(
UnwindBlock->getLandingPadInst(), 1, "relocate_token"));
Result.UnwindToken = ExceptionalToken;
const unsigned LiveStartIdx = Statepoint(Token).gcArgsStartIdx();
CreateGCRelocates(LiveVariables, LiveStartIdx, BasePtrs, ExceptionalToken,
Builder);
BasicBlock *NormalDest = ToReplace->getNormalDest();
assert(!isa<PHINode>(NormalDest->begin()) &&
NormalDest->getUniquePredecessor() &&
"can't safely insert in this block!");
Builder.SetInsertPoint(&*NormalDest->getFirstInsertionPt());
}
assert(Token && "Should be set in one of the above branches!");
if (UseDeoptBundles) {
Token->setName("statepoint_token");
if (!CS.getType()->isVoidTy() && !CS.getInstruction()->use_empty()) {
StringRef Name =
CS.getInstruction()->hasName() ? CS.getInstruction()->getName() : "";
CallInst *GCResult = Builder.CreateGCResult(Token, CS.getType(), Name);
GCResult->setAttributes(CS.getAttributes().getRetAttributes());
Replacements.emplace_back(CS.getInstruction(), GCResult);
} else {
Replacements.emplace_back(CS.getInstruction(), nullptr);
}
} else {
assert(!CS.getInstruction()->hasNUsesOrMore(2) &&
"only valid use before rewrite is gc.result");
assert(!CS.getInstruction()->hasOneUse() ||
isGCResult(cast<Instruction>(*CS.getInstruction()->user_begin())));
Token->takeName(CS.getInstruction());
CS.getInstruction()->replaceAllUsesWith(Token);
CS.getInstruction()->eraseFromParent();
}
Result.StatepointToken = Token;
const unsigned LiveStartIdx = Statepoint(Token).gcArgsStartIdx();
CreateGCRelocates(LiveVariables, LiveStartIdx, BasePtrs, Token, Builder);
}
namespace {
struct NameOrdering {
Value *Base;
Value *Derived;
bool operator()(NameOrdering const &a, NameOrdering const &b) {
return -1 == a.Derived->getName().compare(b.Derived->getName());
}
};
}
static void StabilizeOrder(SmallVectorImpl<Value *> &BaseVec,
SmallVectorImpl<Value *> &LiveVec) {
assert(BaseVec.size() == LiveVec.size());
SmallVector<NameOrdering, 64> Temp;
for (size_t i = 0; i < BaseVec.size(); i++) {
NameOrdering v;
v.Base = BaseVec[i];
v.Derived = LiveVec[i];
Temp.push_back(v);
}
std::sort(Temp.begin(), Temp.end(), NameOrdering());
for (size_t i = 0; i < BaseVec.size(); i++) {
BaseVec[i] = Temp[i].Base;
LiveVec[i] = Temp[i].Derived;
}
}
static void
makeStatepointExplicit(DominatorTree &DT, const CallSite &CS,
PartiallyConstructedSafepointRecord &Result,
std::vector<DeferredReplacement> &Replacements) {
const auto &LiveSet = Result.LiveSet;
const auto &PointerToBase = Result.PointerToBase;
SmallVector<Value *, 64> BaseVec, LiveVec;
LiveVec.reserve(LiveSet.size());
BaseVec.reserve(LiveSet.size());
for (Value *L : LiveSet) {
LiveVec.push_back(L);
assert(PointerToBase.count(L));
Value *Base = PointerToBase.find(L)->second;
BaseVec.push_back(Base);
}
assert(LiveVec.size() == BaseVec.size());
StabilizeOrder(BaseVec, LiveVec);
makeStatepointExplicitImpl(CS, BaseVec, LiveVec, Result, Replacements);
}
static void
insertRelocationStores(iterator_range<Value::user_iterator> GCRelocs,
DenseMap<Value *, Value *> &AllocaMap,
DenseSet<Value *> &VisitedLiveValues) {
for (User *U : GCRelocs) {
if (!isa<IntrinsicInst>(U))
continue;
IntrinsicInst *RelocatedValue = cast<IntrinsicInst>(U);
if (RelocatedValue->getIntrinsicID() !=
Intrinsic::experimental_gc_relocate) {
continue;
}
GCRelocateOperands RelocateOperands(RelocatedValue);
Value *OriginalValue =
const_cast<Value *>(RelocateOperands.getDerivedPtr());
assert(AllocaMap.count(OriginalValue));
Value *Alloca = AllocaMap[OriginalValue];
assert(RelocatedValue->getNextNode() &&
"Should always have one since it's not a terminator");
IRBuilder<> Builder(RelocatedValue->getNextNode());
Value *CastedRelocatedValue =
Builder.CreateBitCast(RelocatedValue,
cast<AllocaInst>(Alloca)->getAllocatedType(),
suffixed_name_or(RelocatedValue, ".casted", ""));
StoreInst *Store = new StoreInst(CastedRelocatedValue, Alloca);
Store->insertAfter(cast<Instruction>(CastedRelocatedValue));
#ifndef NDEBUG
VisitedLiveValues.insert(OriginalValue);
#endif
}
}
static void
insertRematerializationStores(
RematerializedValueMapTy RematerializedValues,
DenseMap<Value *, Value *> &AllocaMap,
DenseSet<Value *> &VisitedLiveValues) {
for (auto RematerializedValuePair: RematerializedValues) {
Instruction *RematerializedValue = RematerializedValuePair.first;
Value *OriginalValue = RematerializedValuePair.second;
assert(AllocaMap.count(OriginalValue) &&
"Can not find alloca for rematerialized value");
Value *Alloca = AllocaMap[OriginalValue];
StoreInst *Store = new StoreInst(RematerializedValue, Alloca);
Store->insertAfter(RematerializedValue);
#ifndef NDEBUG
VisitedLiveValues.insert(OriginalValue);
#endif
}
}
static void relocationViaAlloca(
Function &F, DominatorTree &DT, ArrayRef<Value *> Live,
ArrayRef<PartiallyConstructedSafepointRecord> Records) {
#ifndef NDEBUG
int InitialAllocaNum = 0;
for (auto I = F.getEntryBlock().begin(), E = F.getEntryBlock().end(); I != E;
I++)
if (isa<AllocaInst>(*I))
InitialAllocaNum++;
#endif
DenseMap<Value *, Value *> AllocaMap;
SmallVector<AllocaInst *, 200> PromotableAllocas;
std::size_t NumRematerializedValues = 0;
PromotableAllocas.reserve(Live.size());
auto emitAllocaFor = [&](Value *LiveValue) {
AllocaInst *Alloca = new AllocaInst(LiveValue->getType(), "",
F.getEntryBlock().getFirstNonPHI());
AllocaMap[LiveValue] = Alloca;
PromotableAllocas.push_back(Alloca);
};
for (Value *V : Live)
emitAllocaFor(V);
for (const auto &Info : Records)
for (auto RematerializedValuePair : Info.RematerializedValues) {
Value *OriginalValue = RematerializedValuePair.second;
if (AllocaMap.count(OriginalValue) != 0)
continue;
emitAllocaFor(OriginalValue);
++NumRematerializedValues;
}
for (const auto &Info : Records) {
Value *Statepoint = Info.StatepointToken;
DenseSet<Value *> VisitedLiveValues;
insertRelocationStores(Statepoint->users(), AllocaMap, VisitedLiveValues);
if (isa<InvokeInst>(Statepoint)) {
insertRelocationStores(Info.UnwindToken->users(), AllocaMap,
VisitedLiveValues);
}
insertRematerializationStores(Info.RematerializedValues, AllocaMap,
VisitedLiveValues);
if (ClobberNonLive) {
SmallVector<AllocaInst *, 64> ToClobber;
for (auto Pair : AllocaMap) {
Value *Def = Pair.first;
AllocaInst *Alloca = cast<AllocaInst>(Pair.second);
if (VisitedLiveValues.count(Def)) {
continue;
}
ToClobber.push_back(Alloca);
}
auto InsertClobbersAt = [&](Instruction *IP) {
for (auto *AI : ToClobber) {
auto AIType = cast<PointerType>(AI->getType());
auto PT = cast<PointerType>(AIType->getElementType());
Constant *CPN = ConstantPointerNull::get(PT);
StoreInst *Store = new StoreInst(CPN, AI);
Store->insertBefore(IP);
}
};
if (auto II = dyn_cast<InvokeInst>(Statepoint)) {
InsertClobbersAt(&*II->getNormalDest()->getFirstInsertionPt());
InsertClobbersAt(&*II->getUnwindDest()->getFirstInsertionPt());
} else {
InsertClobbersAt(cast<Instruction>(Statepoint)->getNextNode());
}
}
}
for (auto Pair : AllocaMap) {
Value *Def = Pair.first;
Value *Alloca = Pair.second;
SmallVector<Instruction *, 20> Uses;
Uses.reserve(std::distance(Def->user_begin(), Def->user_end()));
for (User *U : Def->users()) {
if (!isa<ConstantExpr>(U)) {
Uses.push_back(cast<Instruction>(U));
}
}
std::sort(Uses.begin(), Uses.end());
auto Last = std::unique(Uses.begin(), Uses.end());
Uses.erase(Last, Uses.end());
for (Instruction *Use : Uses) {
if (isa<PHINode>(Use)) {
PHINode *Phi = cast<PHINode>(Use);
for (unsigned i = 0; i < Phi->getNumIncomingValues(); i++) {
if (Def == Phi->getIncomingValue(i)) {
LoadInst *Load = new LoadInst(
Alloca, "", Phi->getIncomingBlock(i)->getTerminator());
Phi->setIncomingValue(i, Load);
}
}
} else {
LoadInst *Load = new LoadInst(Alloca, "", Use);
Use->replaceUsesOfWith(Def, Load);
}
}
StoreInst *Store = new StoreInst(Def, Alloca);
if (Instruction *Inst = dyn_cast<Instruction>(Def)) {
if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) {
BasicBlock *NormalDest = Invoke->getNormalDest();
Store->insertBefore(NormalDest->getFirstNonPHI());
} else {
assert(!Inst->isTerminator() &&
"The only TerminatorInst that can produce a value is "
"InvokeInst which is handled above.");
Store->insertAfter(Inst);
}
} else {
assert(isa<Argument>(Def));
Store->insertAfter(cast<Instruction>(Alloca));
}
}
assert(PromotableAllocas.size() == Live.size() + NumRematerializedValues &&
"we must have the same allocas with lives");
if (!PromotableAllocas.empty()) {
PromoteMemToReg(PromotableAllocas, DT);
}
#ifndef NDEBUG
for (auto &I : F.getEntryBlock())
if (isa<AllocaInst>(I))
InitialAllocaNum--;
assert(InitialAllocaNum == 0 && "We must not introduce any extra allocas");
#endif
}
template <typename T> static void unique_unsorted(SmallVectorImpl<T> &Vec) {
SmallSet<T, 8> Seen;
Vec.erase(std::remove_if(Vec.begin(), Vec.end(), [&](const T &V) {
return !Seen.insert(V).second;
}), Vec.end());
}
static void insertUseHolderAfter(CallSite &CS, const ArrayRef<Value *> Values,
SmallVectorImpl<CallInst *> &Holders) {
if (Values.empty())
return;
Module *M = CS.getInstruction()->getParent()->getParent()->getParent();
Function *Func = cast<Function>(M->getOrInsertFunction(
"__tmp_use", FunctionType::get(Type::getVoidTy(M->getContext()), true)));
if (CS.isCall()) {
Holders.push_back(CallInst::Create(Func, Values, "",
&*++CS.getInstruction()->getIterator()));
return;
}
auto *II = cast<InvokeInst>(CS.getInstruction());
Holders.push_back(CallInst::Create(
Func, Values, "", &*II->getNormalDest()->getFirstInsertionPt()));
Holders.push_back(CallInst::Create(
Func, Values, "", &*II->getUnwindDest()->getFirstInsertionPt()));
}
static void findLiveReferences(
Function &F, DominatorTree &DT, Pass *P, ArrayRef<CallSite> toUpdate,
MutableArrayRef<struct PartiallyConstructedSafepointRecord> records) {
GCPtrLivenessData OriginalLivenessData;
computeLiveInValues(DT, F, OriginalLivenessData);
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
const CallSite &CS = toUpdate[i];
analyzeParsePointLiveness(DT, OriginalLivenessData, CS, info);
}
}
static void splitVectorValues(Instruction *StatepointInst,
StatepointLiveSetTy &LiveSet,
DenseMap<Value *, Value *>& PointerToBase,
DominatorTree &DT) {
SmallVector<Value *, 16> ToSplit;
for (Value *V : LiveSet)
if (isa<VectorType>(V->getType()))
ToSplit.push_back(V);
if (ToSplit.empty())
return;
DenseMap<Value *, SmallVector<Value *, 16>> ElementMapping;
Function &F = *(StatepointInst->getParent()->getParent());
DenseMap<Value *, AllocaInst *> AllocaMap;
DenseMap<Value *, std::pair<Value *, Value *>> Replacements;
for (Value *V : ToSplit) {
AllocaInst *Alloca =
new AllocaInst(V->getType(), "", F.getEntryBlock().getFirstNonPHI());
AllocaMap[V] = Alloca;
VectorType *VT = cast<VectorType>(V->getType());
IRBuilder<> Builder(StatepointInst);
SmallVector<Value *, 16> Elements;
for (unsigned i = 0; i < VT->getNumElements(); i++)
Elements.push_back(Builder.CreateExtractElement(V, Builder.getInt32(i)));
ElementMapping[V] = Elements;
auto InsertVectorReform = [&](Instruction *IP) {
Builder.SetInsertPoint(IP);
Builder.SetCurrentDebugLocation(IP->getDebugLoc());
Value *ResultVec = UndefValue::get(VT);
for (unsigned i = 0; i < VT->getNumElements(); i++)
ResultVec = Builder.CreateInsertElement(ResultVec, Elements[i],
Builder.getInt32(i));
return ResultVec;
};
if (isa<CallInst>(StatepointInst)) {
BasicBlock::iterator Next(StatepointInst);
Next++;
Instruction *IP = &*(Next);
Replacements[V].first = InsertVectorReform(IP);
Replacements[V].second = nullptr;
} else {
InvokeInst *Invoke = cast<InvokeInst>(StatepointInst);
BasicBlock *NormalDest = Invoke->getNormalDest();
assert(!isa<PHINode>(NormalDest->begin()));
BasicBlock *UnwindDest = Invoke->getUnwindDest();
assert(!isa<PHINode>(UnwindDest->begin()));
Instruction *IP = &*(NormalDest->getFirstInsertionPt());
Replacements[V].first = InsertVectorReform(IP);
IP = &*(UnwindDest->getFirstInsertionPt());
Replacements[V].second = InsertVectorReform(IP);
}
}
for (Value *V : ToSplit) {
AllocaInst *Alloca = AllocaMap[V];
SmallVector<Instruction *, 16> Users;
for (User *U : V->users())
Users.push_back(cast<Instruction>(U));
for (Instruction *I : Users) {
if (auto Phi = dyn_cast<PHINode>(I)) {
for (unsigned i = 0; i < Phi->getNumIncomingValues(); i++)
if (V == Phi->getIncomingValue(i)) {
LoadInst *Load = new LoadInst(
Alloca, "", Phi->getIncomingBlock(i)->getTerminator());
Phi->setIncomingValue(i, Load);
}
} else {
LoadInst *Load = new LoadInst(Alloca, "", I);
I->replaceUsesOfWith(V, Load);
}
}
StoreInst *Store = new StoreInst(V, Alloca);
if (auto I = dyn_cast<Instruction>(V))
Store->insertAfter(I);
else
Store->insertAfter(Alloca);
Instruction *Replacement = cast<Instruction>(Replacements[V].first);
(new StoreInst(Replacement, Alloca))->insertAfter(Replacement);
Replacement = cast_or_null<Instruction>(Replacements[V].second);
if (Replacement)
(new StoreInst(Replacement, Alloca))->insertAfter(Replacement);
}
SmallVector<AllocaInst *, 16> Allocas;
for (Value *V : ToSplit)
Allocas.push_back(AllocaMap[V]);
PromoteMemToReg(Allocas, DT);
for (Value *V : ToSplit) {
auto &Elements = ElementMapping[V];
LiveSet.erase(V);
LiveSet.insert(Elements.begin(), Elements.end());
assert(PointerToBase.count(V));
Value *OldBase = PointerToBase[V];
auto &BaseElements = ElementMapping[OldBase];
PointerToBase.erase(V);
assert(Elements.size() == BaseElements.size());
for (unsigned i = 0; i < Elements.size(); i++) {
Value *Elem = Elements[i];
PointerToBase[Elem] = BaseElements[i];
}
}
}
static bool findRematerializableChainToBasePointer(
SmallVectorImpl<Instruction*> &ChainToBase,
Value *CurrentValue, Value *BaseValue) {
if (CurrentValue == BaseValue) {
return true;
}
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(CurrentValue)) {
ChainToBase.push_back(GEP);
return findRematerializableChainToBasePointer(ChainToBase,
GEP->getPointerOperand(),
BaseValue);
}
if (CastInst *CI = dyn_cast<CastInst>(CurrentValue)) {
Value *Def = CI->stripPointerCasts();
assert(!isa<CastInst>(Def) && "not a pointer cast found");
if (!CI->isNoopCast(CI->getModule()->getDataLayout()))
return false;
ChainToBase.push_back(CI);
return findRematerializableChainToBasePointer(ChainToBase, Def, BaseValue);
}
return false;
}
static unsigned
chainToBasePointerCost(SmallVectorImpl<Instruction*> &Chain,
TargetTransformInfo &TTI) {
unsigned Cost = 0;
for (Instruction *Instr : Chain) {
if (CastInst *CI = dyn_cast<CastInst>(Instr)) {
assert(CI->isNoopCast(CI->getModule()->getDataLayout()) &&
"non noop cast is found during rematerialization");
Type *SrcTy = CI->getOperand(0)->getType();
Cost += TTI.getCastInstrCost(CI->getOpcode(), CI->getType(), SrcTy);
} else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Instr)) {
Type *ValTy = GEP->getPointerOperandType()->getPointerElementType();
Cost += TTI.getAddressComputationCost(ValTy);
if (!GEP->hasAllConstantIndices())
Cost += 2;
} else {
llvm_unreachable("unsupported instruciton type during rematerialization");
}
}
return Cost;
}
static void rematerializeLiveValues(CallSite CS,
PartiallyConstructedSafepointRecord &Info,
TargetTransformInfo &TTI) {
const unsigned int ChainLengthThreshold = 10;
SmallVector<Value *, 32> LiveValuesToBeDeleted;
for (Value *LiveValue: Info.LiveSet) {
SmallVector<Instruction *, 3> ChainToBase;
assert(Info.PointerToBase.count(LiveValue));
bool FoundChain =
findRematerializableChainToBasePointer(ChainToBase,
LiveValue,
Info.PointerToBase[LiveValue]);
if (!FoundChain ||
ChainToBase.size() == 0 ||
ChainToBase.size() > ChainLengthThreshold)
continue;
unsigned Cost = chainToBasePointerCost(ChainToBase, TTI);
if (CS.isInvoke()) {
Cost *= 2;
}
if (Cost >= RematerializationThreshold)
continue;
LiveValuesToBeDeleted.push_back(LiveValue);
std::reverse(ChainToBase.begin(), ChainToBase.end());
auto rematerializeChain = [&ChainToBase](Instruction *InsertBefore) {
Instruction *LastClonedValue = nullptr;
Instruction *LastValue = nullptr;
for (Instruction *Instr: ChainToBase) {
assert(isa<GetElementPtrInst>(Instr) || isa<CastInst>(Instr));
Instruction *ClonedValue = Instr->clone();
ClonedValue->insertBefore(InsertBefore);
ClonedValue->setName(Instr->getName() + ".remat");
if (LastClonedValue) {
assert(LastValue);
ClonedValue->replaceUsesOfWith(LastValue, LastClonedValue);
#ifndef NDEBUG
for (auto OpValue : ClonedValue->operand_values()) {
assert(std::find(ChainToBase.begin(), ChainToBase.end(), OpValue) ==
ChainToBase.end() &&
"incorrect use in rematerialization chain");
}
#endif
}
LastClonedValue = ClonedValue;
LastValue = Instr;
}
assert(LastClonedValue);
return LastClonedValue;
};
if (CS.isCall()) {
Instruction *InsertBefore = CS.getInstruction()->getNextNode();
assert(InsertBefore);
Instruction *RematerializedValue = rematerializeChain(InsertBefore);
Info.RematerializedValues[RematerializedValue] = LiveValue;
} else {
InvokeInst *Invoke = cast<InvokeInst>(CS.getInstruction());
Instruction *NormalInsertBefore =
&*Invoke->getNormalDest()->getFirstInsertionPt();
Instruction *UnwindInsertBefore =
&*Invoke->getUnwindDest()->getFirstInsertionPt();
Instruction *NormalRematerializedValue =
rematerializeChain(NormalInsertBefore);
Instruction *UnwindRematerializedValue =
rematerializeChain(UnwindInsertBefore);
Info.RematerializedValues[NormalRematerializedValue] = LiveValue;
Info.RematerializedValues[UnwindRematerializedValue] = LiveValue;
}
}
for (auto LiveValue: LiveValuesToBeDeleted) {
Info.LiveSet.erase(LiveValue);
}
}
static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
SmallVectorImpl<CallSite> &ToUpdate) {
#ifndef NDEBUG
std::set<CallSite> Uniqued;
Uniqued.insert(ToUpdate.begin(), ToUpdate.end());
assert(Uniqued.size() == ToUpdate.size() && "no duplicates please!");
for (CallSite CS : ToUpdate) {
assert(CS.getInstruction()->getParent()->getParent() == &F);
assert((UseDeoptBundles || isStatepoint(CS)) &&
"expected to already be a deopt statepoint");
}
#endif
for (CallSite CS : ToUpdate) {
if (!CS.isInvoke())
continue;
auto *II = cast<InvokeInst>(CS.getInstruction());
normalizeForInvokeSafepoint(II->getNormalDest(), II->getParent(), DT);
normalizeForInvokeSafepoint(II->getUnwindDest(), II->getParent(), DT);
}
SmallVector<CallInst *, 64> Holders;
for (CallSite CS : ToUpdate) {
SmallVector<Value *, 64> DeoptValues;
iterator_range<const Use *> DeoptStateRange =
UseDeoptBundles
? iterator_range<const Use *>(GetDeoptBundleOperands(CS))
: iterator_range<const Use *>(Statepoint(CS).vm_state_args());
for (Value *Arg : DeoptStateRange) {
assert(!isUnhandledGCPointerType(Arg->getType()) &&
"support for FCA unimplemented");
if (isHandledGCPointerType(Arg->getType()))
DeoptValues.push_back(Arg);
}
insertUseHolderAfter(CS, DeoptValues, Holders);
}
SmallVector<PartiallyConstructedSafepointRecord, 64> Records(ToUpdate.size());
findLiveReferences(F, DT, P, ToUpdate, Records);
{
DefiningValueMapTy DVCache;
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &info = Records[i];
findBasePointers(DT, DVCache, ToUpdate[i], info);
}
}
Holders.reserve(Holders.size() + Records.size());
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &Info = Records[i];
SmallVector<Value *, 128> Bases;
for (auto Pair : Info.PointerToBase)
Bases.push_back(Pair.second);
insertUseHolderAfter(ToUpdate[i], Bases, Holders);
}
recomputeLiveInValues(F, DT, P, ToUpdate, Records);
if (PrintBasePointers) {
for (auto &Info : Records) {
errs() << "Base Pairs: (w/Relocation)\n";
for (auto Pair : Info.PointerToBase)
errs() << " derived %" << Pair.first->getName() << " base %"
<< Pair.second->getName() << "\n";
}
}
for (CallInst *CI : Holders)
CI->eraseFromParent();
Holders.clear();
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &Info = Records[i];
Instruction *Statepoint = ToUpdate[i].getInstruction();
splitVectorValues(cast<Instruction>(Statepoint), Info.LiveSet,
Info.PointerToBase, DT);
}
TargetTransformInfo &TTI =
P->getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
for (size_t i = 0; i < Records.size(); i++)
rematerializeLiveValues(ToUpdate[i], Records[i], TTI);
std::vector<DeferredReplacement> Replacements;
for (size_t i = 0; i < Records.size(); i++)
makeStatepointExplicit(DT, ToUpdate[i], Records[i], Replacements);
ToUpdate.clear();
for (auto &PR : Replacements)
PR.doReplacement();
Replacements.clear();
for (auto &Info : Records) {
Info.LiveSet.clear();
Info.PointerToBase.clear();
}
SmallVector<Value *, 128> Live;
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &Info = Records[i];
Statepoint Statepoint(Info.StatepointToken);
Live.insert(Live.end(), Statepoint.gc_args_begin(),
Statepoint.gc_args_end());
#ifndef NDEBUG
assert(DT.isReachableFromEntry(Info.StatepointToken->getParent()) &&
"statepoint must be reachable or liveness is meaningless");
for (Value *V : Statepoint.gc_args()) {
if (!isa<Instruction>(V))
continue;
auto *LiveInst = cast<Instruction>(V);
assert(DT.isReachableFromEntry(LiveInst->getParent()) &&
"unreachable values should never be live");
assert(DT.dominates(LiveInst, Info.StatepointToken) &&
"basic SSA liveness expectation violated by liveness analysis");
}
#endif
}
unique_unsorted(Live);
#ifndef NDEBUG
for (auto *Ptr : Live)
assert(isGCPointerType(Ptr->getType()) && "must be a gc pointer type");
#endif
relocationViaAlloca(F, DT, Live, Records);
return !Records.empty();
}
template <typename AttrHolder>
static void RemoveNonValidAttrAtIndex(LLVMContext &Ctx, AttrHolder &AH,
unsigned Index) {
AttrBuilder R;
if (AH.getDereferenceableBytes(Index))
R.addAttribute(Attribute::get(Ctx, Attribute::Dereferenceable,
AH.getDereferenceableBytes(Index)));
if (AH.getDereferenceableOrNullBytes(Index))
R.addAttribute(Attribute::get(Ctx, Attribute::DereferenceableOrNull,
AH.getDereferenceableOrNullBytes(Index)));
if (AH.doesNotAlias(Index))
R.addAttribute(Attribute::NoAlias);
if (!R.empty())
AH.setAttributes(AH.getAttributes().removeAttributes(
Ctx, Index, AttributeSet::get(Ctx, Index, R)));
}
void
RewriteStatepointsForGC::stripNonValidAttributesFromPrototype(Function &F) {
LLVMContext &Ctx = F.getContext();
for (Argument &A : F.args())
if (isa<PointerType>(A.getType()))
RemoveNonValidAttrAtIndex(Ctx, F, A.getArgNo() + 1);
if (isa<PointerType>(F.getReturnType()))
RemoveNonValidAttrAtIndex(Ctx, F, AttributeSet::ReturnIndex);
}
void RewriteStatepointsForGC::stripNonValidAttributesFromBody(Function &F) {
if (F.empty())
return;
LLVMContext &Ctx = F.getContext();
MDBuilder Builder(Ctx);
for (Instruction &I : instructions(F)) {
if (const MDNode *MD = I.getMetadata(LLVMContext::MD_tbaa)) {
assert(MD->getNumOperands() < 5 && "unrecognized metadata shape!");
bool IsImmutableTBAA =
MD->getNumOperands() == 4 &&
mdconst::extract<ConstantInt>(MD->getOperand(3))->getValue() == 1;
if (!IsImmutableTBAA)
continue;
MDNode *Base = cast<MDNode>(MD->getOperand(0));
MDNode *Access = cast<MDNode>(MD->getOperand(1));
uint64_t Offset =
mdconst::extract<ConstantInt>(MD->getOperand(2))->getZExtValue();
MDNode *MutableTBAA =
Builder.createTBAAStructTagNode(Base, Access, Offset);
I.setMetadata(LLVMContext::MD_tbaa, MutableTBAA);
}
if (CallSite CS = CallSite(&I)) {
for (int i = 0, e = CS.arg_size(); i != e; i++)
if (isa<PointerType>(CS.getArgument(i)->getType()))
RemoveNonValidAttrAtIndex(Ctx, CS, i + 1);
if (isa<PointerType>(CS.getType()))
RemoveNonValidAttrAtIndex(Ctx, CS, AttributeSet::ReturnIndex);
}
}
}
static bool shouldRewriteStatepointsIn(Function &F) {
if (F.hasGC()) {
const char *FunctionGCName = F.getGC();
const StringRef StatepointExampleName("statepoint-example");
const StringRef CoreCLRName("coreclr");
return (StatepointExampleName == FunctionGCName) ||
(CoreCLRName == FunctionGCName);
} else
return false;
}
void RewriteStatepointsForGC::stripNonValidAttributes(Module &M) {
#ifndef NDEBUG
assert(std::any_of(M.begin(), M.end(), shouldRewriteStatepointsIn) &&
"precondition!");
#endif
for (Function &F : M)
stripNonValidAttributesFromPrototype(F);
for (Function &F : M)
stripNonValidAttributesFromBody(F);
}
bool RewriteStatepointsForGC::runOnFunction(Function &F) {
if (F.isDeclaration() || F.empty())
return false;
if (!shouldRewriteStatepointsIn(F))
return false;
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
auto NeedsRewrite = [](Instruction &I) {
if (UseDeoptBundles) {
if (ImmutableCallSite CS = ImmutableCallSite(&I))
return !callsGCLeafFunction(CS);
return false;
}
return isStatepoint(I);
};
SmallVector<CallSite, 64> ParsePointNeeded;
bool HasUnreachableStatepoint = false;
for (Instruction &I : instructions(F)) {
if (NeedsRewrite(I)) {
if (DT.isReachableFromEntry(I.getParent()))
ParsePointNeeded.push_back(CallSite(&I));
else
HasUnreachableStatepoint = true;
}
}
bool MadeChange = false;
if (HasUnreachableStatepoint)
MadeChange |= removeUnreachableBlocks(F);
if (ParsePointNeeded.empty())
return MadeChange;
for (BasicBlock &BB : F)
if (BB.getUniquePredecessor()) {
MadeChange = true;
FoldSingleEntryPHINodes(&BB);
}
auto getConditionInst = [](TerminatorInst *TI) -> Instruction* {
if (auto *BI = dyn_cast<BranchInst>(TI))
if (BI->isConditional())
return dyn_cast<Instruction>(BI->getCondition());
return nullptr;
};
for (BasicBlock &BB : F) {
TerminatorInst *TI = BB.getTerminator();
if (auto *Cond = getConditionInst(TI))
if (isa<ICmpInst>(Cond) && Cond->hasOneUse()) {
MadeChange = true;
Cond->moveBefore(TI);
}
}
MadeChange |= insertParsePoints(F, DT, this, ParsePointNeeded);
return MadeChange;
}
static void computeLiveInValues(BasicBlock::reverse_iterator rbegin,
BasicBlock::reverse_iterator rend,
DenseSet<Value *> &LiveTmp) {
for (BasicBlock::reverse_iterator ritr = rbegin; ritr != rend; ritr++) {
Instruction *I = &*ritr;
LiveTmp.erase(I);
if (isa<PHINode>(I))
continue;
for (Value *V : I->operands()) {
assert(!isUnhandledGCPointerType(V->getType()) &&
"support for FCA unimplemented");
if (isHandledGCPointerType(V->getType()) && !isa<Constant>(V)) {
LiveTmp.insert(V);
}
}
}
}
static void computeLiveOutSeed(BasicBlock *BB, DenseSet<Value *> &LiveTmp) {
for (BasicBlock *Succ : successors(BB)) {
const BasicBlock::iterator E(Succ->getFirstNonPHI());
for (BasicBlock::iterator I = Succ->begin(); I != E; I++) {
PHINode *Phi = cast<PHINode>(&*I);
Value *V = Phi->getIncomingValueForBlock(BB);
assert(!isUnhandledGCPointerType(V->getType()) &&
"support for FCA unimplemented");
if (isHandledGCPointerType(V->getType()) && !isa<Constant>(V)) {
LiveTmp.insert(V);
}
}
}
}
static DenseSet<Value *> computeKillSet(BasicBlock *BB) {
DenseSet<Value *> KillSet;
for (Instruction &I : *BB)
if (isHandledGCPointerType(I.getType()))
KillSet.insert(&I);
return KillSet;
}
#ifndef NDEBUG
static void checkBasicSSA(DominatorTree &DT, DenseSet<Value *> &Live,
TerminatorInst *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) {
SmallSetVector<BasicBlock *, 200> Worklist;
auto AddPredsToWorklist = [&](BasicBlock *BB) {
Worklist.insert(pred_begin(BB), pred_end(BB));
};
auto NextItem = [&]() {
BasicBlock *BB = Worklist.back();
Worklist.pop_back();
return BB;
};
for (BasicBlock &BB : F) {
Data.KillSet[&BB] = computeKillSet(&BB);
Data.LiveSet[&BB].clear();
computeLiveInValues(BB.rbegin(), BB.rend(), Data.LiveSet[&BB]);
#ifndef NDEBUG
for (Value *Kill : Data.KillSet[&BB])
assert(!Data.LiveSet[&BB].count(Kill) && "live set contains kill");
#endif
Data.LiveOut[&BB] = DenseSet<Value *>();
computeLiveOutSeed(&BB, Data.LiveOut[&BB]);
Data.LiveIn[&BB] = Data.LiveSet[&BB];
set_union(Data.LiveIn[&BB], Data.LiveOut[&BB]);
set_subtract(Data.LiveIn[&BB], Data.KillSet[&BB]);
if (!Data.LiveIn[&BB].empty())
AddPredsToWorklist(&BB);
}
while (!Worklist.empty()) {
BasicBlock *BB = NextItem();
DenseSet<Value *> LiveOut = Data.LiveOut[BB];
const auto OldLiveOutSize = LiveOut.size();
for (BasicBlock *Succ : successors(BB)) {
assert(Data.LiveIn.count(Succ));
set_union(LiveOut, Data.LiveIn[Succ]);
}
if (OldLiveOutSize == LiveOut.size()) {
continue;
}
Data.LiveOut[BB] = LiveOut;
DenseSet<Value *> LiveTmp = LiveOut;
set_union(LiveTmp, Data.LiveSet[BB]);
set_subtract(LiveTmp, Data.KillSet[BB]);
assert(Data.LiveIn.count(BB));
const DenseSet<Value *> &OldLiveIn = Data.LiveIn[BB];
if (OldLiveIn.size() != LiveTmp.size()) {
Data.LiveIn[BB] = LiveTmp;
AddPredsToWorklist(BB);
}
}
#ifndef NDEBUG
for (BasicBlock &BB : F) {
checkBasicSSA(DT, Data, BB);
}
#endif
}
static void findLiveSetAtInst(Instruction *Inst, GCPtrLivenessData &Data,
StatepointLiveSetTy &Out) {
BasicBlock *BB = Inst->getParent();
assert(Data.LiveOut.count(BB));
DenseSet<Value *> LiveOut = Data.LiveOut[BB];
BasicBlock::reverse_iterator rend(Inst->getIterator());
computeLiveInValues(BB->rbegin(), rend, LiveOut);
LiveOut.erase(Inst);
Out.insert(LiveOut.begin(), LiveOut.end());
}
static void recomputeLiveInValues(GCPtrLivenessData &RevisedLivenessData,
const CallSite &CS,
PartiallyConstructedSafepointRecord &Info) {
Instruction *Inst = CS.getInstruction();
StatepointLiveSetTy Updated;
findLiveSetAtInst(Inst, RevisedLivenessData, Updated);
#ifndef NDEBUG
DenseSet<Value *> Bases;
for (auto KVPair : Info.PointerToBase) {
Bases.insert(KVPair.second);
}
#endif
for (auto V : Updated)
if (!Info.PointerToBase.count(V)) {
assert(Bases.count(V) && "can't find base for unexpected live value");
Info.PointerToBase[V] = V;
continue;
}
#ifndef NDEBUG
for (auto V : Updated) {
assert(Info.PointerToBase.count(V) &&
"must be able to find base for live value");
}
#endif
DenseSet<Value *> ToErase;
for (auto KVPair : Info.PointerToBase)
if (!Updated.count(KVPair.first))
ToErase.insert(KVPair.first);
for (auto V : ToErase)
Info.PointerToBase.erase(V);
#ifndef NDEBUG
for (auto KVPair : Info.PointerToBase)
assert(Updated.count(KVPair.first) && "record for non-live value");
#endif
Info.LiveSet = Updated;
}