Skip to content

Commit

Permalink
API to update MemorySSA for cloned blocks and added CFG edges.
Browse files Browse the repository at this point in the history
Summary:
End goal is to update MemorySSA in all loop passes. LoopUnswitch clones all blocks in a loop. SimpleLoopUnswitch clones some blocks. LoopRotate clones some instructions.
Some of these loop passes also make CFG changes.
This is an API based on what I found needed in LoopUnswitch, SimpleLoopUnswitch, LoopRotate, LoopInstSimplify, LoopSimplifyCFG.
Adding dependent patches using this API for context.

Reviewers: george.burgess.iv, dberlin

Subscribers: sanjoy, jlebar, Prazek, llvm-commits

Differential Revision: https://reviews.llvm.org/D45299

llvm-svn: 341855
  • Loading branch information
alinas committed Sep 10, 2018
1 parent a3dc948 commit 7980099
Show file tree
Hide file tree
Showing 5 changed files with 804 additions and 17 deletions.
6 changes: 4 additions & 2 deletions llvm/include/llvm/Analysis/MemorySSA.h
Expand Up @@ -824,7 +824,8 @@ class MemorySSA {
InsertionPlace);
void insertIntoListsBefore(MemoryAccess *, const BasicBlock *,
AccessList::iterator);
MemoryUseOrDef *createDefinedAccess(Instruction *, MemoryAccess *);
MemoryUseOrDef *createDefinedAccess(Instruction *, MemoryAccess *,
const MemoryUseOrDef *Template = nullptr);

private:
class CachingWalker;
Expand All @@ -845,7 +846,8 @@ class MemorySSA {
void markUnreachableAsLiveOnEntry(BasicBlock *BB);
bool dominatesUse(const MemoryAccess *, const MemoryAccess *) const;
MemoryPhi *createMemoryPhi(BasicBlock *BB);
MemoryUseOrDef *createNewAccess(Instruction *);
MemoryUseOrDef *createNewAccess(Instruction *,
const MemoryUseOrDef *Template = nullptr);
MemoryAccess *findDominatingDef(BasicBlock *, enum InsertionPlace);
void placePHINodes(const SmallPtrSetImpl<BasicBlock *> &);
MemoryAccess *renameBlock(BasicBlock *, MemoryAccess *, bool);
Expand Down
60 changes: 60 additions & 0 deletions llvm/include/llvm/Analysis/MemorySSAUpdater.h
Expand Up @@ -35,8 +35,10 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/LoopIterator.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFGDiff.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/OperandTraits.h"
Expand All @@ -45,6 +47,7 @@
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/IR/ValueMap.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
Expand All @@ -57,6 +60,12 @@ class MemoryAccess;
class LLVMContext;
class raw_ostream;

using ValueToValueMapTy = ValueMap<const Value *, WeakTrackingVH>;
using PhiToDefMap = SmallDenseMap<MemoryPhi *, MemoryAccess *>;
using CFGUpdate = cfg::Update<BasicBlock *>;
using GraphDiffInvBBPair =
std::pair<const GraphDiff<BasicBlock *> *, Inverse<BasicBlock *>>;

class MemorySSAUpdater {
private:
MemorySSA *MSSA;
Expand All @@ -70,6 +79,7 @@ class MemorySSAUpdater {

public:
MemorySSAUpdater(MemorySSA *MSSA) : MSSA(MSSA) {}

/// Insert a definition into the MemorySSA IR. RenameUses will rename any use
/// below the new def block (and any inserted phis). RenameUses should be set
/// to true if the definition may cause new aliases for loads below it. This
Expand All @@ -89,6 +99,39 @@ class MemorySSAUpdater {
/// Where a mayalias b, *does* require RenameUses be set to true.
void insertDef(MemoryDef *Def, bool RenameUses = false);
void insertUse(MemoryUse *Use);
/// Update the MemoryPhi in `To` following an edge deletion between `From` and
/// `To`. If `To` becomes unreachable, a call to removeBlocks should be made.
void removeEdge(BasicBlock *From, BasicBlock *To);
/// Update the MemoryPhi in `To` to have a single incoming edge from `From`,
/// following a CFG change that replaced multiple edges (switch) with a direct
/// branch.
void removeDuplicatePhiEdgesBetween(BasicBlock *From, BasicBlock *To);
/// Update MemorySSA after a loop was cloned, given the blocks in RPO order,
/// the exit blocks and a 1:1 mapping of all blocks and instructions
/// cloned. This involves duplicating all defs and uses in the cloned blocks
/// Updating phi nodes in exit block successors is done separately.
void updateForClonedLoop(const LoopBlocksRPO &LoopBlocks,
ArrayRef<BasicBlock *> ExitBlocks,
const ValueToValueMapTy &VM,
bool IgnoreIncomingWithNoClones = false);
// Block BB was fully or partially cloned into its predecessor P1. Map
// contains the 1:1 mapping of instructions cloned and VM[BB]=P1.
void updateForClonedBlockIntoPred(BasicBlock *BB, BasicBlock *P1,
const ValueToValueMapTy &VM);
/// Update phi nodes in exit block successors following cloning. Exit blocks
/// that were not cloned don't have additional predecessors added.
void updateExitBlocksForClonedLoop(ArrayRef<BasicBlock *> ExitBlocks,
const ValueToValueMapTy &VMap,
DominatorTree &DT);
void updateExitBlocksForClonedLoop(
ArrayRef<BasicBlock *> ExitBlocks,
ArrayRef<std::unique_ptr<ValueToValueMapTy>> VMaps, DominatorTree &DT);

/// Apply CFG updates, analogous with the DT edge updates.
void applyUpdates(ArrayRef<CFGUpdate> Updates, DominatorTree &DT);
/// Apply CFG insert updates, analogous with the DT edge updates.
void applyInsertUpdates(ArrayRef<CFGUpdate> Updates, DominatorTree &DT);

void moveBefore(MemoryUseOrDef *What, MemoryUseOrDef *Where);
void moveAfter(MemoryUseOrDef *What, MemoryUseOrDef *Where);
void moveToPlace(MemoryUseOrDef *What, BasicBlock *BB,
Expand Down Expand Up @@ -219,6 +262,23 @@ class MemorySSAUpdater {
template <class RangeType>
MemoryAccess *tryRemoveTrivialPhi(MemoryPhi *Phi, RangeType &Operands);
void fixupDefs(const SmallVectorImpl<WeakVH> &);
// Clone all uses and defs from BB to NewBB given a 1:1 map of all
// instructions and blocks cloned, and a map of MemoryPhi : Definition
// (MemoryAccess Phi or Def). VMap maps old instructions to cloned
// instructions and old blocks to cloned blocks. MPhiMap, is created in the
// caller of this private method, and maps existing MemoryPhis to new
// definitions that new MemoryAccesses must point to. These definitions may
// not necessarily be MemoryPhis themselves, they may be MemoryDefs. As such,
// the map is between MemoryPhis and MemoryAccesses, where the MemoryAccesses
// may be MemoryPhis or MemoryDefs and not MemoryUses.
void cloneUsesAndDefs(BasicBlock *BB, BasicBlock *NewBB,
const ValueToValueMapTy &VMap, PhiToDefMap &MPhiMap);
template <typename Iter>
void privateUpdateExitBlocksForClonedLoop(ArrayRef<BasicBlock *> ExitBlocks,
Iter ValuesBegin, Iter ValuesEnd,
DominatorTree &DT);
void applyInsertUpdates(ArrayRef<CFGUpdate>, DominatorTree &DT,
const GraphDiff<BasicBlock *> *GD);
};
} // end namespace llvm

Expand Down
45 changes: 30 additions & 15 deletions llvm/lib/Analysis/MemorySSA.cpp
Expand Up @@ -1542,9 +1542,10 @@ MemoryPhi *MemorySSA::createMemoryPhi(BasicBlock *BB) {
}

MemoryUseOrDef *MemorySSA::createDefinedAccess(Instruction *I,
MemoryAccess *Definition) {
MemoryAccess *Definition,
const MemoryUseOrDef *Template) {
assert(!isa<PHINode>(I) && "Cannot create a defined access for a PHI");
MemoryUseOrDef *NewAccess = createNewAccess(I);
MemoryUseOrDef *NewAccess = createNewAccess(I, Template);
assert(
NewAccess != nullptr &&
"Tried to create a memory access for a non-memory touching instruction");
Expand All @@ -1567,7 +1568,8 @@ static inline bool isOrdered(const Instruction *I) {
}

/// Helper function to create new memory accesses
MemoryUseOrDef *MemorySSA::createNewAccess(Instruction *I) {
MemoryUseOrDef *MemorySSA::createNewAccess(Instruction *I,
const MemoryUseOrDef *Template) {
// The assume intrinsic has a control dependency which we model by claiming
// that it writes arbitrarily. Ignore that fake memory dependency here.
// FIXME: Replace this special casing with a more accurate modelling of
Expand All @@ -1576,18 +1578,31 @@ MemoryUseOrDef *MemorySSA::createNewAccess(Instruction *I) {
if (II->getIntrinsicID() == Intrinsic::assume)
return nullptr;

// Find out what affect this instruction has on memory.
ModRefInfo ModRef = AA->getModRefInfo(I, None);
// The isOrdered check is used to ensure that volatiles end up as defs
// (atomics end up as ModRef right now anyway). Until we separate the
// ordering chain from the memory chain, this enables people to see at least
// some relative ordering to volatiles. Note that getClobberingMemoryAccess
// will still give an answer that bypasses other volatile loads. TODO:
// Separate memory aliasing and ordering into two different chains so that we
// can precisely represent both "what memory will this read/write/is clobbered
// by" and "what instructions can I move this past".
bool Def = isModSet(ModRef) || isOrdered(I);
bool Use = isRefSet(ModRef);
bool Def, Use;
if (Template) {
Def = dyn_cast_or_null<MemoryDef>(Template) != nullptr;
Use = dyn_cast_or_null<MemoryUse>(Template) != nullptr;
#if !defined(NDEBUG)
ModRefInfo ModRef = AA->getModRefInfo(I, None);
bool DefCheck, UseCheck;
DefCheck = isModSet(ModRef) || isOrdered(I);
UseCheck = isRefSet(ModRef);
assert(Def == DefCheck && (Def || Use == UseCheck) && "Invalid template");
#endif
} else {
// Find out what affect this instruction has on memory.
ModRefInfo ModRef = AA->getModRefInfo(I, None);
// The isOrdered check is used to ensure that volatiles end up as defs
// (atomics end up as ModRef right now anyway). Until we separate the
// ordering chain from the memory chain, this enables people to see at least
// some relative ordering to volatiles. Note that getClobberingMemoryAccess
// will still give an answer that bypasses other volatile loads. TODO:
// Separate memory aliasing and ordering into two different chains so that
// we can precisely represent both "what memory will this read/write/is
// clobbered by" and "what instructions can I move this past".
Def = isModSet(ModRef) || isOrdered(I);
Use = isRefSet(ModRef);
}

// It's possible for an instruction to not modify memory at all. During
// construction, we ignore them.
Expand Down

0 comments on commit 7980099

Please sign in to comment.