Skip to content

Commit

Permalink
[DA] DivergenceAnalysis for unstructured, reducible CFGs
Browse files Browse the repository at this point in the history
Summary:
This is patch 2 of the new DivergenceAnalysis (https://reviews.llvm.org/D50433).

This patch contains a generic divergence analysis implementation for
unstructured, reducible Control-Flow Graphs. It contains two new classes.
The `SyncDependenceAnalysis` class lazily computes sync dependences, which
relate divergent branches to points of joining divergent control. The
`DivergenceAnalysis` class contains the generic divergence analysis
implementation.

Reviewers: nhaehnle

Reviewed By: nhaehnle

Subscribers: sameerds, kristina, nhaehnle, xbolva00, tschuett, mgorny, llvm-commits

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

llvm-svn: 344734
  • Loading branch information
nhaehnle committed Oct 18, 2018
1 parent 547f89d commit 5904168
Show file tree
Hide file tree
Showing 8 changed files with 1,508 additions and 0 deletions.
3 changes: 3 additions & 0 deletions llvm/include/llvm/ADT/PostOrderIterator.h
Expand Up @@ -296,12 +296,15 @@ class ReversePostOrderTraversal {

public:
using rpo_iterator = typename std::vector<NodeRef>::reverse_iterator;
using const_rpo_iterator = typename std::vector<NodeRef>::const_reverse_iterator;

ReversePostOrderTraversal(GraphT G) { Initialize(GT::getEntryNode(G)); }

// Because we want a reverse post order, use reverse iterators from the vector
rpo_iterator begin() { return Blocks.rbegin(); }
const_rpo_iterator begin() const { return Blocks.crbegin(); }
rpo_iterator end() { return Blocks.rend(); }
const_rpo_iterator end() const { return Blocks.crend(); }
};

} // end namespace llvm
Expand Down
178 changes: 178 additions & 0 deletions llvm/include/llvm/Analysis/DivergenceAnalysis.h
@@ -0,0 +1,178 @@
//===- llvm/Analysis/DivergenceAnalysis.h - Divergence Analysis -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// \file
// The divergence analysis determines which instructions and branches are
// divergent given a set of divergent source instructions.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ANALYSIS_DIVERGENCE_ANALYSIS_H
#define LLVM_ANALYSIS_DIVERGENCE_ANALYSIS_H

#include "llvm/ADT/DenseSet.h"
#include "llvm/Analysis/SyncDependenceAnalysis.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include <vector>

namespace llvm {
class Module;
class Value;
class Instruction;
class Loop;
class raw_ostream;
class TargetTransformInfo;

/// \brief Generic divergence analysis for reducible CFGs.
///
/// This analysis propagates divergence in a data-parallel context from sources
/// of divergence to all users. It requires reducible CFGs. All assignments
/// should be in SSA form.
class DivergenceAnalysis {
public:
/// \brief This instance will analyze the whole function \p F or the loop \p
/// RegionLoop.
///
/// \param RegionLoop if non-null the analysis is restricted to \p RegionLoop.
/// Otherwise the whole function is analyzed.
/// \param IsLCSSAForm whether the analysis may assume that the IR in the
/// region in in LCSSA form.
DivergenceAnalysis(const Function &F, const Loop *RegionLoop,
const DominatorTree &DT, const LoopInfo &LI,
SyncDependenceAnalysis &SDA, bool IsLCSSAForm);

/// \brief The loop that defines the analyzed region (if any).
const Loop *getRegionLoop() const { return RegionLoop; }
const Function &getFunction() const { return F; }

/// \brief Whether \p BB is part of the region.
bool inRegion(const BasicBlock &BB) const;
/// \brief Whether \p I is part of the region.
bool inRegion(const Instruction &I) const;

/// \brief Mark \p UniVal as a value that is always uniform.
void addUniformOverride(const Value &UniVal);

/// \brief Mark \p DivVal as a value that is always divergent.
void markDivergent(const Value &DivVal);

/// \brief Propagate divergence to all instructions in the region.
/// Divergence is seeded by calls to \p markDivergent.
void compute();

/// \brief Whether any value was marked or analyzed to be divergent.
bool hasDetectedDivergence() const { return !DivergentValues.empty(); }

/// \brief Whether \p Val will always return a uniform value regardless of its
/// operands
bool isAlwaysUniform(const Value &Val) const;

/// \brief Whether \p Val is a divergent value
bool isDivergent(const Value &Val) const;

void print(raw_ostream &OS, const Module *) const;

private:
bool updateTerminator(const TerminatorInst &Term) const;
bool updatePHINode(const PHINode &Phi) const;

/// \brief Computes whether \p Inst is divergent based on the
/// divergence of its operands.
///
/// \returns Whether \p Inst is divergent.
///
/// This should only be called for non-phi, non-terminator instructions.
bool updateNormalInstruction(const Instruction &Inst) const;

/// \brief Mark users of live-out users as divergent.
///
/// \param LoopHeader the header of the divergent loop.
///
/// Marks all users of live-out values of the loop headed by \p LoopHeader
/// as divergent and puts them on the worklist.
void taintLoopLiveOuts(const BasicBlock &LoopHeader);

/// \brief Push all users of \p Val (in the region) to the worklist
void pushUsers(const Value &I);

/// \brief Push all phi nodes in @block to the worklist
void pushPHINodes(const BasicBlock &Block);

/// \brief Mark \p Block as join divergent
///
/// A block is join divergent if two threads may reach it from different
/// incoming blocks at the same time.
void markBlockJoinDivergent(const BasicBlock &Block) {
DivergentJoinBlocks.insert(&Block);
}

/// \brief Whether \p Val is divergent when read in \p ObservingBlock.
bool isTemporalDivergent(const BasicBlock &ObservingBlock,
const Value &Val) const;

/// \brief Whether \p Block is join divergent
///
/// (see markBlockJoinDivergent).
bool isJoinDivergent(const BasicBlock &Block) const {
return DivergentJoinBlocks.find(&Block) != DivergentJoinBlocks.end();
}

/// \brief Propagate control-induced divergence to users (phi nodes and
/// instructions).
//
// \param JoinBlock is a divergent loop exit or join point of two disjoint
// paths.
// \returns Whether \p JoinBlock is a divergent loop exit of \p TermLoop.
bool propagateJoinDivergence(const BasicBlock &JoinBlock,
const Loop *TermLoop);

/// \brief Propagate induced value divergence due to control divergence in \p
/// Term.
void propagateBranchDivergence(const TerminatorInst &Term);

/// \brief Propagate divergent caused by a divergent loop exit.
///
/// \param ExitingLoop is a divergent loop.
void propagateLoopDivergence(const Loop &ExitingLoop);

private:
const Function &F;
// If regionLoop != nullptr, analysis is only performed within \p RegionLoop.
// Otw, analyze the whole function
const Loop *RegionLoop;

const DominatorTree &DT;
const LoopInfo &LI;

// Recognized divergent loops
DenseSet<const Loop *> DivergentLoops;

// The SDA links divergent branches to divergent control-flow joins.
SyncDependenceAnalysis &SDA;

// Use simplified code path for LCSSA form.
bool IsLCSSAForm;

// Set of known-uniform values.
DenseSet<const Value *> UniformOverrides;

// Blocks with joining divergent control from different predecessors.
DenseSet<const BasicBlock *> DivergentJoinBlocks;

// Detected/marked divergent values.
DenseSet<const Value *> DivergentValues;

// Internal worklist for divergence propagation.
std::vector<const Instruction *> Worklist;
};

} // namespace llvm

#endif // LLVM_ANALYSIS_DIVERGENCE_ANALYSIS_H
88 changes: 88 additions & 0 deletions llvm/include/llvm/Analysis/SyncDependenceAnalysis.h
@@ -0,0 +1,88 @@
//===- SyncDependenceAnalysis.h - Divergent Branch Dependence -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// \file
// This file defines the SyncDependenceAnalysis class, which computes for
// every divergent branch the set of phi nodes that the branch will make
// divergent.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ANALYSIS_SYNC_DEPENDENCE_ANALYSIS_H
#define LLVM_ANALYSIS_SYNC_DEPENDENCE_ANALYSIS_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/LoopInfo.h"
#include <memory>

namespace llvm {

class BasicBlock;
class DominatorTree;
class Loop;
class PostDominatorTree;
class TerminatorInst;
class TerminatorInst;

using ConstBlockSet = SmallPtrSet<const BasicBlock *, 4>;

/// \brief Relates points of divergent control to join points in
/// reducible CFGs.
///
/// This analysis relates points of divergent control to points of converging
/// divergent control. The analysis requires all loops to be reducible.
class SyncDependenceAnalysis {
void visitSuccessor(const BasicBlock &succBlock, const Loop *termLoop,
const BasicBlock *defBlock);

public:
bool inRegion(const BasicBlock &BB) const;

~SyncDependenceAnalysis();
SyncDependenceAnalysis(const DominatorTree &DT, const PostDominatorTree &PDT,
const LoopInfo &LI);

/// \brief Computes divergent join points and loop exits caused by branch
/// divergence in \p Term.
///
/// The set of blocks which are reachable by disjoint paths from \p Term.
/// The set also contains loop exits if there two disjoint paths:
/// one from \p Term to the loop exit and another from \p Term to the loop
/// header. Those exit blocks are added to the returned set.
/// If L is the parent loop of \p Term and an exit of L is in the returned
/// set then L is a divergent loop.
const ConstBlockSet &join_blocks(const TerminatorInst &Term);

/// \brief Computes divergent join points and loop exits (in the surrounding
/// loop) caused by the divergent loop exits of\p Loop.
///
/// The set of blocks which are reachable by disjoint paths from the
/// loop exits of \p Loop.
/// This treats the loop as a single node in \p Loop's parent loop.
/// The returned set has the same properties as for join_blocks(TermInst&).
const ConstBlockSet &join_blocks(const Loop &Loop);

private:
static ConstBlockSet EmptyBlockSet;

ReversePostOrderTraversal<const Function *> FuncRPOT;
const DominatorTree &DT;
const PostDominatorTree &PDT;
const LoopInfo &LI;

std::map<const Loop *, std::unique_ptr<ConstBlockSet>> CachedLoopExitJoins;
std::map<const TerminatorInst *, std::unique_ptr<ConstBlockSet>>
CachedBranchJoins;
};

} // namespace llvm

#endif // LLVM_ANALYSIS_SYNC_DEPENDENCE_ANALYSIS_H
2 changes: 2 additions & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Expand Up @@ -25,6 +25,7 @@ add_llvm_library(LLVMAnalysis
Delinearization.cpp
DemandedBits.cpp
DependenceAnalysis.cpp
DivergenceAnalysis.cpp
DomPrinter.cpp
DominanceFrontier.cpp
EHPersonalities.cpp
Expand Down Expand Up @@ -80,6 +81,7 @@ add_llvm_library(LLVMAnalysis
ScalarEvolutionAliasAnalysis.cpp
ScalarEvolutionExpander.cpp
ScalarEvolutionNormalization.cpp
SyncDependenceAnalysis.cpp
SyntheticCountsUtils.cpp
TargetLibraryInfo.cpp
TargetTransformInfo.cpp
Expand Down

0 comments on commit 5904168

Please sign in to comment.