1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-21 03:53:04 +02:00

[RS4GC] Cosmetic cleanup, NFC

Summary:
A series of cosmetic cleanup changes to RewriteStatepointsForGC:

  - Rename variables to LLVM style
  - Remove some redundant asserts
  - Remove an unsued `Pass *` parameter
  - Remove unnecessary variables
  - Use C++11 idioms where applicable
  - Pass CallSite by value, not reference

Reviewers: reames, swaroop.sridhar

Subscribers: llvm-commits, sanjoy

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

llvm-svn: 249508
This commit is contained in:
Sanjoy Das 2015-10-07 02:39:18 +00:00
parent f1cabed0b6
commit 3621ece8f2

View File

@ -158,15 +158,15 @@ struct GCPtrLivenessData {
// base relation will remain. Internally, we add a mixture of the two
// types, then update all the second type to the first type
typedef DenseMap<Value *, Value *> DefiningValueMapTy;
typedef DenseSet<llvm::Value *> StatepointLiveSetTy;
typedef DenseSet<Value *> StatepointLiveSetTy;
typedef DenseMap<Instruction *, Value *> RematerializedValueMapTy;
struct PartiallyConstructedSafepointRecord {
/// The set of values known to be live across this safepoint
StatepointLiveSetTy liveset;
StatepointLiveSetTy LiveSet;
/// Mapping from live pointers to a base-defining-value
DenseMap<llvm::Value *, llvm::Value *> PointerToBase;
DenseMap<Value *, Value *> PointerToBase;
/// The *new* gc.statepoint instruction itself. This produces the token
/// that normal path gc.relocates and the gc.result are tied to.
@ -177,7 +177,7 @@ struct PartiallyConstructedSafepointRecord {
Instruction *UnwindToken;
/// Record live values we are rematerialized instead of relocating.
/// They are not included into 'liveset' field.
/// They are not included into 'LiveSet' field.
/// Maps rematerialized copy to it's original value.
RematerializedValueMapTy RematerializedValues;
};
@ -245,7 +245,7 @@ static bool isUnhandledGCPointerType(Type *Ty) {
}
#endif
static bool order_by_name(llvm::Value *a, llvm::Value *b) {
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()) {
@ -274,15 +274,15 @@ static void analyzeParsePointLiveness(
const CallSite &CS, PartiallyConstructedSafepointRecord &result) {
Instruction *inst = CS.getInstruction();
StatepointLiveSetTy liveset;
findLiveSetAtInst(inst, OriginalLivenessData, liveset);
StatepointLiveSetTy LiveSet;
findLiveSetAtInst(inst, OriginalLivenessData, LiveSet);
if (PrintLiveSet) {
// Note: This output is used by several of the test cases
// The order of elements in a set is not stable, put them in a vec and sort
// by name
SmallVector<Value *, 64> Temp;
Temp.insert(Temp.end(), liveset.begin(), liveset.end());
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)
@ -290,9 +290,9 @@ static void analyzeParsePointLiveness(
}
if (PrintLiveSetSize) {
errs() << "Safepoint For: " << CS.getCalledValue()->getName() << "\n";
errs() << "Number live values: " << liveset.size() << "\n";
errs() << "Number live values: " << LiveSet.size() << "\n";
}
result.liveset = liveset;
result.LiveSet = LiveSet;
}
static bool isKnownBaseResult(Value *V);
@ -1143,7 +1143,7 @@ static Value *findBasePointer(Value *I, DefiningValueMapTy &cache) {
// pointer was a base pointer.
static void
findBasePointers(const StatepointLiveSetTy &live,
DenseMap<llvm::Value *, llvm::Value *> &PointerToBase,
DenseMap<Value *, Value *> &PointerToBase,
DominatorTree *DT, DefiningValueMapTy &DVCache) {
// For the naming of values inserted to be deterministic - which makes for
// much cleaner and more stable tests - we need to assign an order to the
@ -1175,8 +1175,8 @@ findBasePointers(const StatepointLiveSetTy &live,
static void findBasePointers(DominatorTree &DT, DefiningValueMapTy &DVCache,
const CallSite &CS,
PartiallyConstructedSafepointRecord &result) {
DenseMap<llvm::Value *, llvm::Value *> PointerToBase;
findBasePointers(result.liveset, PointerToBase, &DT, DVCache);
DenseMap<Value *, Value *> PointerToBase;
findBasePointers(result.LiveSet, PointerToBase, &DT, DVCache);
if (PrintBasePointers) {
// Note: Need to print these in a stable order since this is checked in
@ -1292,9 +1292,9 @@ static AttributeSet legalizeCallAttributes(AttributeSet AS) {
/// statepointToken - statepoint instruction to which relocates should be
/// bound.
/// Builder - Llvm IR builder to be used to construct new calls.
static void CreateGCRelocates(ArrayRef<llvm::Value *> LiveVariables,
static void CreateGCRelocates(ArrayRef<Value *> LiveVariables,
const int LiveStart,
ArrayRef<llvm::Value *> BasePtrs,
ArrayRef<Value *> BasePtrs,
Instruction *StatepointToken,
IRBuilder<> Builder) {
if (LiveVariables.empty())
@ -1330,176 +1330,170 @@ static void CreateGCRelocates(ArrayRef<llvm::Value *> LiveVariables,
}
static void
makeStatepointExplicitImpl(const CallSite &CS, /* to replace */
const SmallVectorImpl<llvm::Value *> &basePtrs,
const SmallVectorImpl<llvm::Value *> &liveVariables,
Pass *P,
PartiallyConstructedSafepointRecord &result) {
assert(basePtrs.size() == liveVariables.size());
makeStatepointExplicitImpl(const CallSite CS, /* to replace */
const SmallVectorImpl<Value *> &BasePtrs,
const SmallVectorImpl<Value *> &LiveVariables,
PartiallyConstructedSafepointRecord &Result) {
assert(BasePtrs.size() == LiveVariables.size());
assert(isStatepoint(CS) &&
"This method expects to be rewriting a statepoint");
BasicBlock *BB = CS.getInstruction()->getParent();
assert(BB);
Function *F = BB->getParent();
assert(F && "must be set");
Module *M = F->getParent();
(void)M;
assert(M && "must be set");
// We're not changing the function signature of the statepoint since the gc
// arguments go into the var args section.
Function *gc_statepoint_decl = CS.getCalledFunction();
Function *GCStatepointDecl = CS.getCalledFunction();
// Then go ahead and use the builder do actually do the inserts. We insert
// immediately before the previous instruction under the assumption that all
// arguments will be available here. We can't insert afterwards since we may
// be replacing a terminator.
Instruction *insertBefore = CS.getInstruction();
IRBuilder<> Builder(insertBefore);
Instruction *InsertBefore = CS.getInstruction();
IRBuilder<> Builder(InsertBefore);
// Copy all of the arguments from the original statepoint - this includes the
// target, call args, and deopt args
SmallVector<llvm::Value *, 64> args;
args.insert(args.end(), CS.arg_begin(), CS.arg_end());
SmallVector<llvm::Value *, 64> Args;
Args.insert(Args.end(), CS.arg_begin(), CS.arg_end());
// TODO: Clear the 'needs rewrite' flag
// add all the pointers to be relocated (gc arguments)
// Capture the start of the live variable list for use in the gc_relocates
const int live_start = args.size();
args.insert(args.end(), liveVariables.begin(), liveVariables.end());
// Add all the pointers to be relocated (gc arguments) and capture the start
// of the live variable list for use in the gc_relocates
const int LiveStartIdx = Args.size();
Args.insert(Args.end(), LiveVariables.begin(), LiveVariables.end());
// Create the statepoint given all the arguments
Instruction *token = nullptr;
AttributeSet return_attributes;
Instruction *Token = nullptr;
AttributeSet ReturnAttrs;
if (CS.isCall()) {
CallInst *toReplace = cast<CallInst>(CS.getInstruction());
CallInst *call =
Builder.CreateCall(gc_statepoint_decl, args, "safepoint_token");
call->setTailCall(toReplace->isTailCall());
call->setCallingConv(toReplace->getCallingConv());
CallInst *ToReplace = cast<CallInst>(CS.getInstruction());
CallInst *Call =
Builder.CreateCall(GCStatepointDecl, Args, "safepoint_token");
Call->setTailCall(ToReplace->isTailCall());
Call->setCallingConv(ToReplace->getCallingConv());
// Currently we will fail on parameter attributes and on certain
// function attributes.
AttributeSet new_attrs = legalizeCallAttributes(toReplace->getAttributes());
AttributeSet NewAttrs = legalizeCallAttributes(ToReplace->getAttributes());
// In case if we can handle this set of attributes - set up function attrs
// directly on statepoint and return attrs later for gc_result intrinsic.
call->setAttributes(new_attrs.getFnAttributes());
return_attributes = new_attrs.getRetAttributes();
Call->setAttributes(NewAttrs.getFnAttributes());
ReturnAttrs = NewAttrs.getRetAttributes();
token = call;
Token = Call;
// Put the following gc_result and gc_relocate calls immediately after the
// the old call (which we're about to delete)
BasicBlock::iterator next(toReplace);
assert(BB->end() != next && "not a terminator, must have next");
next++;
Instruction *IP = &*(next);
Builder.SetInsertPoint(IP);
Builder.SetCurrentDebugLocation(IP->getDebugLoc());
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 *ToReplace = cast<InvokeInst>(CS.getInstruction());
// 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 *invoke = InvokeInst::Create(
gc_statepoint_decl, toReplace->getNormalDest(),
toReplace->getUnwindDest(), args, "statepoint_token", toReplace->getParent());
invoke->setCallingConv(toReplace->getCallingConv());
InvokeInst *Invoke =
InvokeInst::Create(GCStatepointDecl, ToReplace->getNormalDest(),
ToReplace->getUnwindDest(), Args, "statepoint_token",
ToReplace->getParent());
Invoke->setCallingConv(ToReplace->getCallingConv());
// Currently we will fail on parameter attributes and on certain
// function attributes.
AttributeSet new_attrs = legalizeCallAttributes(toReplace->getAttributes());
AttributeSet NewAttrs = legalizeCallAttributes(ToReplace->getAttributes());
// In case if we can handle this set of attributes - set up function attrs
// directly on statepoint and return attrs later for gc_result intrinsic.
invoke->setAttributes(new_attrs.getFnAttributes());
return_attributes = new_attrs.getRetAttributes();
Invoke->setAttributes(NewAttrs.getFnAttributes());
ReturnAttrs = NewAttrs.getRetAttributes();
token = invoke;
Token = Invoke;
// Generate gc relocates in exceptional path
BasicBlock *unwindBlock = toReplace->getUnwindDest();
assert(!isa<PHINode>(unwindBlock->begin()) &&
unwindBlock->getUniquePredecessor() &&
BasicBlock *UnwindBlock = ToReplace->getUnwindDest();
assert(!isa<PHINode>(UnwindBlock->begin()) &&
UnwindBlock->getUniquePredecessor() &&
"can't safely insert in this block!");
Instruction *IP = &*(unwindBlock->getFirstInsertionPt());
Builder.SetInsertPoint(IP);
Builder.SetCurrentDebugLocation(toReplace->getDebugLoc());
Builder.SetInsertPoint(UnwindBlock->getFirstInsertionPt());
Builder.SetCurrentDebugLocation(ToReplace->getDebugLoc());
// Extract second element from landingpad return value. We will attach
// exceptional gc relocates to it.
const unsigned idx = 1;
Instruction *exceptional_token =
Instruction *ExceptionalToken =
cast<Instruction>(Builder.CreateExtractValue(
unwindBlock->getLandingPadInst(), idx, "relocate_token"));
result.UnwindToken = exceptional_token;
UnwindBlock->getLandingPadInst(), 1, "relocate_token"));
Result.UnwindToken = ExceptionalToken;
CreateGCRelocates(liveVariables, live_start, basePtrs,
exceptional_token, Builder);
CreateGCRelocates(LiveVariables, LiveStartIdx, BasePtrs, ExceptionalToken,
Builder);
// Generate gc relocates and returns for normal block
BasicBlock *normalDest = toReplace->getNormalDest();
assert(!isa<PHINode>(normalDest->begin()) &&
normalDest->getUniquePredecessor() &&
BasicBlock *NormalDest = ToReplace->getNormalDest();
assert(!isa<PHINode>(NormalDest->begin()) &&
NormalDest->getUniquePredecessor() &&
"can't safely insert in this block!");
IP = &*(normalDest->getFirstInsertionPt());
Builder.SetInsertPoint(IP);
Builder.SetInsertPoint(NormalDest->getFirstInsertionPt());
// gc relocates will be generated later as if it were regular call
// statepoint
}
assert(token);
assert(Token && "Should be set in one of the above branches!");
// Take the name of the original value call if it had one.
token->takeName(CS.getInstruction());
Token->takeName(CS.getInstruction());
// The GCResult is already inserted, we just need to find it
#ifndef NDEBUG
Instruction *toReplace = CS.getInstruction();
assert((toReplace->hasNUses(0) || toReplace->hasNUses(1)) &&
Instruction *ToReplace = CS.getInstruction();
assert(!ToReplace->hasNUsesOrMore(2) &&
"only valid use before rewrite is gc.result");
assert(!toReplace->hasOneUse() ||
isGCResult(cast<Instruction>(*toReplace->user_begin())));
assert(!ToReplace->hasOneUse() ||
isGCResult(cast<Instruction>(*ToReplace->user_begin())));
#endif
// Update the gc.result of the original statepoint (if any) to use the newly
// inserted statepoint. This is safe to do here since the token can't be
// considered a live reference.
CS.getInstruction()->replaceAllUsesWith(token);
CS.getInstruction()->replaceAllUsesWith(Token);
result.StatepointToken = token;
Result.StatepointToken = Token;
// Second, create a gc.relocate for every live variable
CreateGCRelocates(liveVariables, live_start, basePtrs, token, Builder);
CreateGCRelocates(LiveVariables, LiveStartIdx, BasePtrs, Token, Builder);
}
namespace {
struct name_ordering {
Value *base;
Value *derived;
bool operator()(name_ordering const &a, name_ordering const &b) {
return -1 == a.derived->getName().compare(b.derived->getName());
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 stablize_order(SmallVectorImpl<Value *> &basevec,
SmallVectorImpl<Value *> &livevec) {
assert(basevec.size() == livevec.size());
SmallVector<name_ordering, 64> temp;
for (size_t i = 0; i < basevec.size(); i++) {
name_ordering v;
v.base = basevec[i];
v.derived = livevec[i];
temp.push_back(v);
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(), name_ordering());
for (size_t i = 0; i < basevec.size(); i++) {
basevec[i] = temp[i].base;
livevec[i] = temp[i].derived;
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;
}
}
@ -1509,39 +1503,39 @@ static void stablize_order(SmallVectorImpl<Value *> &basevec,
// WARNING: Does not do any fixup to adjust users of the original live
// values. That's the callers responsibility.
static void
makeStatepointExplicit(DominatorTree &DT, const CallSite &CS, Pass *P,
PartiallyConstructedSafepointRecord &result) {
auto liveset = result.liveset;
auto PointerToBase = result.PointerToBase;
makeStatepointExplicit(DominatorTree &DT, const CallSite &CS,
PartiallyConstructedSafepointRecord &Result) {
auto LiveSet = Result.LiveSet;
auto PointerToBase = Result.PointerToBase;
// Convert to vector for efficient cross referencing.
SmallVector<Value *, 64> basevec, livevec;
livevec.reserve(liveset.size());
basevec.reserve(liveset.size());
for (Value *L : liveset) {
livevec.push_back(L);
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[L];
basevec.push_back(base);
Value *Base = PointerToBase[L];
BaseVec.push_back(Base);
}
assert(livevec.size() == basevec.size());
assert(LiveVec.size() == BaseVec.size());
// To make the output IR slightly more stable (for use in diffs), ensure a
// fixed order of the values in the safepoint (by sorting the value name).
// The order is otherwise meaningless.
stablize_order(basevec, livevec);
StabilizeOrder(BaseVec, LiveVec);
// Do the actual rewriting and delete the old statepoint
makeStatepointExplicitImpl(CS, basevec, livevec, P, result);
makeStatepointExplicitImpl(CS, BaseVec, LiveVec, Result);
CS.getInstruction()->eraseFromParent();
}
// Helper function for the relocationViaAlloca.
// It receives iterator to the statepoint gc relocates and emits store to the
// assigned
// location (via allocaMap) for the each one of them.
// Add visited values into the visitedLiveValues set we will later use them
// for sanity check.
//
// It receives iterator to the statepoint gc relocates and emits a store to the
// assigned location (via allocaMap) for the each one of them. It adds the
// visited values into the visitedLiveValues set, which we will later use them
// for sanity checking.
static void
insertRelocationStores(iterator_range<Value::user_iterator> GCRelocs,
DenseMap<Value *, Value *> &AllocaMap,
@ -1566,9 +1560,10 @@ insertRelocationStores(iterator_range<Value::user_iterator> GCRelocs,
Value *Alloca = AllocaMap[OriginalValue];
// Emit store into the related alloca
// All gc_relocate are i8 addrspace(1)* typed, and it must be bitcasted to
// All gc_relocates are i8 addrspace(1)* typed, and it must be bitcasted to
// the correct type according to alloca.
assert(RelocatedValue->getNextNode() && "Should always have one since it's not a terminator");
assert(RelocatedValue->getNextNode() &&
"Should always have one since it's not a terminator");
IRBuilder<> Builder(RelocatedValue->getNextNode());
Value *CastedRelocatedValue =
Builder.CreateBitCast(RelocatedValue,
@ -1609,10 +1604,10 @@ insertRematerializationStores(
}
}
/// do all the relocation update via allocas and mem2reg
/// Do all the relocation update via allocas and mem2reg
static void relocationViaAlloca(
Function &F, DominatorTree &DT, ArrayRef<Value *> Live,
ArrayRef<struct PartiallyConstructedSafepointRecord> Records) {
ArrayRef<PartiallyConstructedSafepointRecord> Records) {
#ifndef NDEBUG
// record initial number of (static) allocas; we'll check we have the same
// number when we get done.
@ -1639,15 +1634,12 @@ static void relocationViaAlloca(
PromotableAllocas.push_back(Alloca);
};
// emit alloca for each live gc pointer
for (unsigned i = 0; i < Live.size(); i++) {
emitAllocaFor(Live[i]);
}
// emit allocas for rematerialized values
for (size_t i = 0; i < Records.size(); i++) {
const struct PartiallyConstructedSafepointRecord &Info = Records[i];
// Emit alloca for each live gc pointer
for (Value *V : Live)
emitAllocaFor(V);
// Emit allocas for rematerialized values
for (const auto &Info : Records)
for (auto RematerializedValuePair : Info.RematerializedValues) {
Value *OriginalValue = RematerializedValuePair.second;
if (AllocaMap.count(OriginalValue) != 0)
@ -1656,20 +1648,17 @@ static void relocationViaAlloca(
emitAllocaFor(OriginalValue);
++NumRematerializedValues;
}
}
// The next two loops are part of the same conceptual operation. We need to
// insert a store to the alloca after the original def and at each
// redefinition. We need to insert a load before each use. These are split
// into distinct loops for performance reasons.
// update gc pointer after each statepoint
// either store a relocated value or null (if no relocated value found for
// this gc pointer and it is not a gc_result)
// this must happen before we update the statepoint with load of alloca
// otherwise we lose the link between statepoint and old def
for (size_t i = 0; i < Records.size(); i++) {
const struct PartiallyConstructedSafepointRecord &Info = Records[i];
// Update gc pointer after each statepoint: either store a relocated value or
// null (if no relocated value was found for this gc pointer and it is not a
// gc_result). This must happen before we update the statepoint with load of
// alloca otherwise we lose the link between statepoint and old def.
for (const auto &Info : Records) {
Value *Statepoint = Info.StatepointToken;
// This will be used for consistency check
@ -1723,20 +1712,19 @@ static void relocationViaAlloca(
InsertClobbersAt(II->getNormalDest()->getFirstInsertionPt());
InsertClobbersAt(II->getUnwindDest()->getFirstInsertionPt());
} else {
BasicBlock::iterator Next(cast<CallInst>(Statepoint));
Next++;
InsertClobbersAt(Next);
InsertClobbersAt(cast<Instruction>(Statepoint)->getNextNode());
}
}
}
// update use with load allocas and add store for gc_relocated
// Update use with load allocas and add store for gc_relocated.
for (auto Pair : AllocaMap) {
Value *Def = Pair.first;
Value *Alloca = Pair.second;
// we pre-record the uses of allocas so that we dont have to worry about
// later update
// that change the user information.
// We pre-record the uses of allocas so that we dont have to worry about
// later update that changes the user information..
SmallVector<Instruction *, 20> Uses;
// PERF: trade a linear scan for repeated reallocation
Uses.reserve(std::distance(Def->user_begin(), Def->user_end()));
@ -1771,9 +1759,9 @@ static void relocationViaAlloca(
}
}
// emit store for the initial gc value
// store must be inserted after load, otherwise store will be in alloca's
// use list and an extra load will be inserted before it
// Emit store for the initial gc value. Store must be inserted after load,
// otherwise store will be in alloca's use list and an extra load will be
// inserted before it.
StoreInst *Store = new StoreInst(Def, Alloca);
if (Instruction *Inst = dyn_cast<Instruction>(Def)) {
if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) {
@ -1796,14 +1784,13 @@ static void relocationViaAlloca(
assert(PromotableAllocas.size() == Live.size() + NumRematerializedValues &&
"we must have the same allocas with lives");
if (!PromotableAllocas.empty()) {
// apply mem2reg to promote alloca to SSA
// Apply mem2reg to promote alloca to SSA
PromoteMemToReg(PromotableAllocas, DT);
}
#ifndef NDEBUG
for (auto I = F.getEntryBlock().begin(), E = F.getEntryBlock().end(); I != E;
I++)
if (isa<AllocaInst>(*I))
for (auto &I : F.getEntryBlock())
if (isa<AllocaInst>(I))
InitialAllocaNum--;
assert(InitialAllocaNum == 0 && "We must not introduce any extra allocas");
#endif
@ -1859,8 +1846,8 @@ static void findLiveReferences(
}
}
/// Remove any vector of pointers from the liveset by scalarizing them over the
/// statepoint instruction. Adds the scalarized pieces to the liveset. It
/// Remove any vector of pointers from the live set by scalarizing them over the
/// statepoint instruction. Adds the scalarized pieces to the live set. It
/// would be preferable to include the vector in the statepoint itself, but
/// the lowering code currently does not handle that. Extending it would be
/// slightly non-trivial since it requires a format change. Given how rare
@ -2065,8 +2052,8 @@ chainToBasePointerCost(SmallVectorImpl<Instruction*> &Chain,
return Cost;
}
// From the statepoint liveset pick values that are cheaper to recompute then to
// relocate. Remove this values from the liveset, rematerialize them after
// From the statepoint live set pick values that are cheaper to recompute then
// to relocate. Remove this values from the live set, rematerialize them after
// statepoint and record them in "Info" structure. Note that similar to
// relocated values we don't do any user adjustments here.
static void rematerializeLiveValues(CallSite CS,
@ -2078,7 +2065,7 @@ static void rematerializeLiveValues(CallSite CS,
// We can not di this in following loop due to iterator invalidation.
SmallVector<Value *, 32> LiveValuesToBeDeleted;
for (Value *LiveValue: Info.liveset) {
for (Value *LiveValue: Info.LiveSet) {
// For each live pointer find it's defining chain
SmallVector<Instruction *, 3> ChainToBase;
assert(Info.PointerToBase.count(LiveValue));
@ -2183,20 +2170,19 @@ static void rematerializeLiveValues(CallSite CS,
// Remove rematerializaed values from the live set
for (auto LiveValue: LiveValuesToBeDeleted) {
Info.liveset.erase(LiveValue);
Info.LiveSet.erase(LiveValue);
}
}
static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
SmallVectorImpl<CallSite> &toUpdate) {
SmallVectorImpl<CallSite> &ToUpdate) {
#ifndef NDEBUG
// sanity check the input
std::set<CallSite> uniqued;
uniqued.insert(toUpdate.begin(), toUpdate.end());
assert(uniqued.size() == toUpdate.size() && "no duplicates please!");
std::set<CallSite> Uniqued;
Uniqued.insert(ToUpdate.begin(), ToUpdate.end());
assert(Uniqued.size() == ToUpdate.size() && "no duplicates please!");
for (size_t i = 0; i < toUpdate.size(); i++) {
CallSite &CS = toUpdate[i];
for (CallSite CS : ToUpdate) {
assert(CS.getInstruction()->getParent()->getParent() == &F);
assert(isStatepoint(CS) && "expected to already be a deopt statepoint");
}
@ -2206,26 +2192,23 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
// the top of the successor blocks. See the comment on
// normalForInvokeSafepoint on exactly what is needed. Note that this step
// may restructure the CFG.
for (CallSite CS : toUpdate) {
for (CallSite CS : ToUpdate) {
if (!CS.isInvoke())
continue;
InvokeInst *invoke = cast<InvokeInst>(CS.getInstruction());
normalizeForInvokeSafepoint(invoke->getNormalDest(), invoke->getParent(),
DT);
normalizeForInvokeSafepoint(invoke->getUnwindDest(), invoke->getParent(),
DT);
auto *II = cast<InvokeInst>(CS.getInstruction());
normalizeForInvokeSafepoint(II->getNormalDest(), II->getParent(), DT);
normalizeForInvokeSafepoint(II->getUnwindDest(), II->getParent(), DT);
}
// A list of dummy calls added to the IR to keep various values obviously
// live in the IR. We'll remove all of these when done.
SmallVector<CallInst *, 64> holders;
SmallVector<CallInst *, 64> Holders;
// Insert a dummy call with all of the arguments to the vm_state we'll need
// for the actual safepoint insertion. This ensures reference arguments in
// the deopt argument list are considered live through the safepoint (and
// thus makes sure they get relocated.)
for (size_t i = 0; i < toUpdate.size(); i++) {
CallSite &CS = toUpdate[i];
for (CallSite CS : ToUpdate) {
Statepoint StatepointCS(CS);
SmallVector<Value *, 64> DeoptValues;
@ -2236,20 +2219,14 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
if (isHandledGCPointerType(Arg->getType()))
DeoptValues.push_back(Arg);
}
insertUseHolderAfter(CS, DeoptValues, holders);
insertUseHolderAfter(CS, DeoptValues, Holders);
}
SmallVector<struct PartiallyConstructedSafepointRecord, 64> records;
records.reserve(toUpdate.size());
for (size_t i = 0; i < toUpdate.size(); i++) {
struct PartiallyConstructedSafepointRecord info;
records.push_back(info);
}
assert(records.size() == toUpdate.size());
SmallVector<PartiallyConstructedSafepointRecord, 64> Records(ToUpdate.size());
// A) Identify all gc pointers which are statically live at the given call
// site.
findLiveReferences(F, DT, P, toUpdate, records);
findLiveReferences(F, DT, P, ToUpdate, Records);
// B) Find the base pointers for each live pointer
/* scope for caching */ {
@ -2258,10 +2235,9 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
// large numbers of duplicate base_phis.
DefiningValueMapTy DVCache;
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
CallSite &CS = toUpdate[i];
findBasePointers(DT, DVCache, CS, info);
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &info = Records[i];
findBasePointers(DT, DVCache, ToUpdate[i], info);
}
} // end of cache scope
@ -2278,49 +2254,46 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
// the base pointers which were identified for that safepoint. We'll then
// ask liveness for _every_ base inserted to see what is now live. Then we
// remove the dummy calls.
holders.reserve(holders.size() + records.size());
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
CallSite &CS = toUpdate[i];
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) {
for (auto Pair : Info.PointerToBase)
Bases.push_back(Pair.second);
}
insertUseHolderAfter(CS, Bases, holders);
insertUseHolderAfter(ToUpdate[i], Bases, Holders);
}
// By selecting base pointers, we've effectively inserted new uses. Thus, we
// need to rerun liveness. We may *also* have inserted new defs, but that's
// not the key issue.
recomputeLiveInValues(F, DT, P, toUpdate, records);
recomputeLiveInValues(F, DT, P, ToUpdate, Records);
if (PrintBasePointers) {
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
for (auto &Info : Records) {
errs() << "Base Pairs: (w/Relocation)\n";
for (auto Pair : info.PointerToBase) {
for (auto Pair : Info.PointerToBase)
errs() << " derived %" << Pair.first->getName() << " base %"
<< Pair.second->getName() << "\n";
}
}
}
for (size_t i = 0; i < holders.size(); i++) {
holders[i]->eraseFromParent();
holders[i] = nullptr;
}
holders.clear();
for (CallInst *CI : Holders)
CI->eraseFromParent();
Holders.clear();
// Do a limited scalarization of any live at safepoint vector values which
// contain pointers. This enables this pass to run after vectorization at
// the cost of some possible performance loss. TODO: it would be nice to
// natively support vectors all the way through the backend so we don't need
// to scalarize here.
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
Instruction *statepoint = toUpdate[i].getInstruction();
splitVectorValues(cast<Instruction>(statepoint), info.liveset,
info.PointerToBase, DT);
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);
}
// In order to reduce live set of statepoint we might choose to rematerialize
@ -2329,12 +2302,8 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
TargetTransformInfo &TTI =
P->getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
CallSite &CS = toUpdate[i];
rematerializeLiveValues(CS, info, TTI);
}
for (size_t i = 0; i < Records.size(); i++)
rematerializeLiveValues(ToUpdate[i], Records[i], TTI);
// Now run through and replace the existing statepoints with new ones with
// the live variables listed. We do not yet update uses of the values being
@ -2342,55 +2311,52 @@ static bool insertParsePoints(Function &F, DominatorTree &DT, Pass *P,
// survive to the last iteration of this loop. (By construction, the
// previous statepoint can not be a live variable, thus we can and remove
// the old statepoint calls as we go.)
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
CallSite &CS = toUpdate[i];
makeStatepointExplicit(DT, CS, P, info);
}
toUpdate.clear(); // prevent accident use of invalid CallSites
for (size_t i = 0; i < Records.size(); i++)
makeStatepointExplicit(DT, ToUpdate[i], Records[i]);
ToUpdate.clear(); // prevent accident use of invalid CallSites
// Do all the fixups of the original live variables to their relocated selves
SmallVector<Value *, 128> live;
for (size_t i = 0; i < records.size(); i++) {
struct PartiallyConstructedSafepointRecord &info = records[i];
SmallVector<Value *, 128> Live;
for (size_t i = 0; i < Records.size(); i++) {
PartiallyConstructedSafepointRecord &Info = Records[i];
// We can't simply save the live set from the original insertion. One of
// the live values might be the result of a call which needs a safepoint.
// That Value* no longer exists and we need to use the new gc_result.
// Thankfully, the liveset is embedded in the statepoint (and updated), so
// Thankfully, the live set is embedded in the statepoint (and updated), so
// we just grab that.
Statepoint statepoint(info.StatepointToken);
live.insert(live.end(), statepoint.gc_args_begin(),
statepoint.gc_args_end());
Statepoint Statepoint(Info.StatepointToken);
Live.insert(Live.end(), Statepoint.gc_args_begin(),
Statepoint.gc_args_end());
#ifndef NDEBUG
// Do some basic sanity checks on our liveness results before performing
// relocation. Relocation can and will turn mistakes in liveness results
// into non-sensical code which is must harder to debug.
// TODO: It would be nice to test consistency as well
assert(DT.isReachableFromEntry(info.StatepointToken->getParent()) &&
assert(DT.isReachableFromEntry(Info.StatepointToken->getParent()) &&
"statepoint must be reachable or liveness is meaningless");
for (Value *V : statepoint.gc_args()) {
for (Value *V : Statepoint.gc_args()) {
if (!isa<Instruction>(V))
// Non-instruction values trivial dominate all possible uses
continue;
auto LiveInst = cast<Instruction>(V);
auto *LiveInst = cast<Instruction>(V);
assert(DT.isReachableFromEntry(LiveInst->getParent()) &&
"unreachable values should never be live");
assert(DT.dominates(LiveInst, info.StatepointToken) &&
assert(DT.dominates(LiveInst, Info.StatepointToken) &&
"basic SSA liveness expectation violated by liveness analysis");
}
#endif
}
unique_unsorted(live);
unique_unsorted(Live);
#ifndef NDEBUG
// sanity check
for (auto ptr : live) {
assert(isGCPointerType(ptr->getType()) && "must be a gc pointer type");
}
for (auto *Ptr : Live)
assert(isGCPointerType(Ptr->getType()) && "must be a gc pointer type");
#endif
relocationViaAlloca(F, DT, live, records);
return !records.empty();
relocationViaAlloca(F, DT, Live, Records);
return !Records.empty();
}
// Handles both return values and arguments for Functions and CallSites.
@ -2808,5 +2774,5 @@ static void recomputeLiveInValues(GCPtrLivenessData &RevisedLivenessData,
assert(Updated.count(KVPair.first) && "record for non-live value");
#endif
Info.liveset = Updated;
Info.LiveSet = Updated;
}