aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Scalar/GVN.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2022-07-03 14:10:23 +0000
committerDimitry Andric <dim@FreeBSD.org>2022-07-03 14:10:23 +0000
commit145449b1e420787bb99721a429341fa6be3adfb6 (patch)
tree1d56ae694a6de602e348dd80165cf881a36600ed /llvm/lib/Transforms/Scalar/GVN.cpp
parentecbca9f5fb7d7613d2b94982c4825eb0d33d6842 (diff)
downloadsrc-145449b1e420787bb99721a429341fa6be3adfb6.tar.gz
src-145449b1e420787bb99721a429341fa6be3adfb6.zip
Diffstat (limited to 'llvm/lib/Transforms/Scalar/GVN.cpp')
-rw-r--r--llvm/lib/Transforms/Scalar/GVN.cpp231
1 files changed, 180 insertions, 51 deletions
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 398c93e8758c..783301fe589e 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -19,7 +19,6 @@
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
@@ -32,6 +31,7 @@
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/InstructionPrecedenceTracking.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemoryBuiltins.h"
@@ -42,12 +42,10 @@
#include "llvm/Analysis/PHITransAddr.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
-#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
-#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
@@ -55,11 +53,9 @@
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
-#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
-#include "llvm/IR/Operator.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Type.h"
@@ -72,7 +68,6 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
@@ -112,16 +107,16 @@ static cl::opt<bool> GVNEnableLoadInLoopPRE("enable-load-in-loop-pre",
cl::init(true));
static cl::opt<bool>
GVNEnableSplitBackedgeInLoadPRE("enable-split-backedge-in-load-pre",
- cl::init(true));
+ cl::init(false));
static cl::opt<bool> GVNEnableMemDep("enable-gvn-memdep", cl::init(true));
static cl::opt<uint32_t> MaxNumDeps(
- "gvn-max-num-deps", cl::Hidden, cl::init(100), cl::ZeroOrMore,
+ "gvn-max-num-deps", cl::Hidden, cl::init(100),
cl::desc("Max number of dependences to attempt Load PRE (default = 100)"));
// This is based on IsValueFullyAvailableInBlockNumSpeculationsMax stat.
static cl::opt<uint32_t> MaxBBSpeculations(
- "gvn-max-block-speculations", cl::Hidden, cl::init(600), cl::ZeroOrMore,
+ "gvn-max-block-speculations", cl::Hidden, cl::init(600),
cl::desc("Max number of blocks we're willing to speculate on (and recurse "
"into) when deducing if a value is fully available or not in GVN "
"(default = 600)"));
@@ -129,6 +124,8 @@ static cl::opt<uint32_t> MaxBBSpeculations(
struct llvm::GVNPass::Expression {
uint32_t opcode;
bool commutative = false;
+ // The type is not necessarily the result type of the expression, it may be
+ // any additional type needed to disambiguate the expression.
Type *type = nullptr;
SmallVector<uint32_t, 4> varargs;
@@ -178,70 +175,88 @@ template <> struct DenseMapInfo<GVNPass::Expression> {
/// implicitly associated with a rematerialization point which is the
/// location of the instruction from which it was formed.
struct llvm::gvn::AvailableValue {
- enum ValType {
+ enum class ValType {
SimpleVal, // A simple offsetted value that is accessed.
LoadVal, // A value produced by a load.
MemIntrin, // A memory intrinsic which is loaded from.
- UndefVal // A UndefValue representing a value from dead block (which
+ UndefVal, // A UndefValue representing a value from dead block (which
// is not yet physically removed from the CFG).
+ SelectVal, // A pointer select which is loaded from and for which the load
+ // can be replace by a value select.
};
- /// V - The value that is live out of the block.
- PointerIntPair<Value *, 2, ValType> Val;
+ /// Val - The value that is live out of the block.
+ Value *Val;
+ /// Kind of the live-out value.
+ ValType Kind;
/// Offset - The byte offset in Val that is interesting for the load query.
unsigned Offset = 0;
static AvailableValue get(Value *V, unsigned Offset = 0) {
AvailableValue Res;
- Res.Val.setPointer(V);
- Res.Val.setInt(SimpleVal);
+ Res.Val = V;
+ Res.Kind = ValType::SimpleVal;
Res.Offset = Offset;
return Res;
}
static AvailableValue getMI(MemIntrinsic *MI, unsigned Offset = 0) {
AvailableValue Res;
- Res.Val.setPointer(MI);
- Res.Val.setInt(MemIntrin);
+ Res.Val = MI;
+ Res.Kind = ValType::MemIntrin;
Res.Offset = Offset;
return Res;
}
static AvailableValue getLoad(LoadInst *Load, unsigned Offset = 0) {
AvailableValue Res;
- Res.Val.setPointer(Load);
- Res.Val.setInt(LoadVal);
+ Res.Val = Load;
+ Res.Kind = ValType::LoadVal;
Res.Offset = Offset;
return Res;
}
static AvailableValue getUndef() {
AvailableValue Res;
- Res.Val.setPointer(nullptr);
- Res.Val.setInt(UndefVal);
+ Res.Val = nullptr;
+ Res.Kind = ValType::UndefVal;
Res.Offset = 0;
return Res;
}
- bool isSimpleValue() const { return Val.getInt() == SimpleVal; }
- bool isCoercedLoadValue() const { return Val.getInt() == LoadVal; }
- bool isMemIntrinValue() const { return Val.getInt() == MemIntrin; }
- bool isUndefValue() const { return Val.getInt() == UndefVal; }
+ static AvailableValue getSelect(SelectInst *Sel) {
+ AvailableValue Res;
+ Res.Val = Sel;
+ Res.Kind = ValType::SelectVal;
+ Res.Offset = 0;
+ return Res;
+ }
+
+ bool isSimpleValue() const { return Kind == ValType::SimpleVal; }
+ bool isCoercedLoadValue() const { return Kind == ValType::LoadVal; }
+ bool isMemIntrinValue() const { return Kind == ValType::MemIntrin; }
+ bool isUndefValue() const { return Kind == ValType::UndefVal; }
+ bool isSelectValue() const { return Kind == ValType::SelectVal; }
Value *getSimpleValue() const {
assert(isSimpleValue() && "Wrong accessor");
- return Val.getPointer();
+ return Val;
}
LoadInst *getCoercedLoadValue() const {
assert(isCoercedLoadValue() && "Wrong accessor");
- return cast<LoadInst>(Val.getPointer());
+ return cast<LoadInst>(Val);
}
MemIntrinsic *getMemIntrinValue() const {
assert(isMemIntrinValue() && "Wrong accessor");
- return cast<MemIntrinsic>(Val.getPointer());
+ return cast<MemIntrinsic>(Val);
+ }
+
+ SelectInst *getSelectValue() const {
+ assert(isSelectValue() && "Wrong accessor");
+ return cast<SelectInst>(Val);
}
/// Emit code at the specified insertion point to adjust the value defined
@@ -275,6 +290,10 @@ struct llvm::gvn::AvailableValueInBlock {
return get(BB, AvailableValue::getUndef());
}
+ static AvailableValueInBlock getSelect(BasicBlock *BB, SelectInst *Sel) {
+ return get(BB, AvailableValue::getSelect(Sel));
+ }
+
/// Emit code at the end of this block to adjust the value defined here to
/// the specified type. This handles various coercion cases.
Value *MaterializeAdjustedValue(LoadInst *Load, GVNPass &gvn) const {
@@ -379,6 +398,39 @@ GVNPass::ValueTable::createExtractvalueExpr(ExtractValueInst *EI) {
return e;
}
+GVNPass::Expression GVNPass::ValueTable::createGEPExpr(GetElementPtrInst *GEP) {
+ Expression E;
+ Type *PtrTy = GEP->getType()->getScalarType();
+ const DataLayout &DL = GEP->getModule()->getDataLayout();
+ unsigned BitWidth = DL.getIndexTypeSizeInBits(PtrTy);
+ MapVector<Value *, APInt> VariableOffsets;
+ APInt ConstantOffset(BitWidth, 0);
+ if (PtrTy->isOpaquePointerTy() &&
+ GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) {
+ // For opaque pointers, convert into offset representation, to recognize
+ // equivalent address calculations that use different type encoding.
+ LLVMContext &Context = GEP->getContext();
+ E.opcode = GEP->getOpcode();
+ E.type = nullptr;
+ E.varargs.push_back(lookupOrAdd(GEP->getPointerOperand()));
+ for (const auto &Pair : VariableOffsets) {
+ E.varargs.push_back(lookupOrAdd(Pair.first));
+ E.varargs.push_back(lookupOrAdd(ConstantInt::get(Context, Pair.second)));
+ }
+ if (!ConstantOffset.isZero())
+ E.varargs.push_back(
+ lookupOrAdd(ConstantInt::get(Context, ConstantOffset)));
+ } else {
+ // If converting to offset representation fails (for typed pointers and
+ // scalable vectors), fall back to type-based implementation:
+ E.opcode = GEP->getOpcode();
+ E.type = GEP->getSourceElementType();
+ for (Use &Op : GEP->operands())
+ E.varargs.push_back(lookupOrAdd(Op));
+ }
+ return E;
+}
+
//===----------------------------------------------------------------------===//
// ValueTable External Functions
//===----------------------------------------------------------------------===//
@@ -562,9 +614,11 @@ uint32_t GVNPass::ValueTable::lookupOrAdd(Value *V) {
case Instruction::InsertElement:
case Instruction::ShuffleVector:
case Instruction::InsertValue:
- case Instruction::GetElementPtr:
exp = createExpr(I);
break;
+ case Instruction::GetElementPtr:
+ exp = createGEPExpr(cast<GetElementPtrInst>(I));
+ break;
case Instruction::ExtractValue:
exp = createExtractvalueExpr(cast<ExtractValueInst>(I));
break;
@@ -639,24 +693,24 @@ void GVNPass::ValueTable::verifyRemoved(const Value *V) const {
//===----------------------------------------------------------------------===//
bool GVNPass::isPREEnabled() const {
- return Options.AllowPRE.getValueOr(GVNEnablePRE);
+ return Options.AllowPRE.value_or(GVNEnablePRE);
}
bool GVNPass::isLoadPREEnabled() const {
- return Options.AllowLoadPRE.getValueOr(GVNEnableLoadPRE);
+ return Options.AllowLoadPRE.value_or(GVNEnableLoadPRE);
}
bool GVNPass::isLoadInLoopPREEnabled() const {
- return Options.AllowLoadInLoopPRE.getValueOr(GVNEnableLoadInLoopPRE);
+ return Options.AllowLoadInLoopPRE.value_or(GVNEnableLoadInLoopPRE);
}
bool GVNPass::isLoadPRESplitBackedgeEnabled() const {
- return Options.AllowLoadPRESplitBackedge.getValueOr(
+ return Options.AllowLoadPRESplitBackedge.value_or(
GVNEnableSplitBackedgeInLoadPRE);
}
bool GVNPass::isMemDepEnabled() const {
- return Options.AllowMemDep.getValueOr(GVNEnableMemDep);
+ return Options.AllowMemDep.value_or(GVNEnableMemDep);
}
PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
@@ -897,6 +951,17 @@ ConstructSSAForLoadSet(LoadInst *Load,
return SSAUpdate.GetValueInMiddleOfBlock(Load->getParent());
}
+static LoadInst *findDominatingLoad(Value *Ptr, Type *LoadTy, SelectInst *Sel,
+ DominatorTree &DT) {
+ for (Value *U : Ptr->users()) {
+ auto *LI = dyn_cast<LoadInst>(U);
+ if (LI && LI->getType() == LoadTy && LI->getParent() == Sel->getParent() &&
+ DT.dominates(LI, Sel))
+ return LI;
+ }
+ return nullptr;
+}
+
Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
Instruction *InsertPt,
GVNPass &gvn) const {
@@ -937,6 +1002,17 @@ Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
<< " " << *getMemIntrinValue() << '\n'
<< *Res << '\n'
<< "\n\n\n");
+ } else if (isSelectValue()) {
+ // Introduce a new value select for a load from an eligible pointer select.
+ SelectInst *Sel = getSelectValue();
+ LoadInst *L1 = findDominatingLoad(Sel->getOperand(1), LoadTy, Sel,
+ gvn.getDominatorTree());
+ LoadInst *L2 = findDominatingLoad(Sel->getOperand(2), LoadTy, Sel,
+ gvn.getDominatorTree());
+ assert(L1 && L2 &&
+ "must be able to obtain dominating loads for both value operands of "
+ "the select");
+ Res = SelectInst::Create(Sel->getCondition(), L1, L2, "", Sel);
} else {
llvm_unreachable("Should not materialize value from dead block");
}
@@ -1023,8 +1099,54 @@ static void reportMayClobberedLoad(LoadInst *Load, MemDepResult DepInfo,
ORE->emit(R);
}
+/// Check if a load from pointer-select \p Address in \p DepBB can be converted
+/// to a value select. The following conditions need to be satisfied:
+/// 1. The pointer select (\p Address) must be defined in \p DepBB.
+/// 2. Both value operands of the pointer select must be loaded in the same
+/// basic block, before the pointer select.
+/// 3. There must be no instructions between the found loads and \p End that may
+/// clobber the loads.
+static Optional<AvailableValue>
+tryToConvertLoadOfPtrSelect(BasicBlock *DepBB, BasicBlock::iterator End,
+ Value *Address, Type *LoadTy, DominatorTree &DT,
+ AAResults *AA) {
+
+ auto *Sel = dyn_cast_or_null<SelectInst>(Address);
+ if (!Sel || DepBB != Sel->getParent())
+ return None;
+
+ LoadInst *L1 = findDominatingLoad(Sel->getOperand(1), LoadTy, Sel, DT);
+ LoadInst *L2 = findDominatingLoad(Sel->getOperand(2), LoadTy, Sel, DT);
+ if (!L1 || !L2)
+ return None;
+
+ // Ensure there are no accesses that may modify the locations referenced by
+ // either L1 or L2 between L1, L2 and the specified End iterator.
+ Instruction *EarlierLoad = L1->comesBefore(L2) ? L1 : L2;
+ MemoryLocation L1Loc = MemoryLocation::get(L1);
+ MemoryLocation L2Loc = MemoryLocation::get(L2);
+ if (any_of(make_range(EarlierLoad->getIterator(), End), [&](Instruction &I) {
+ return isModSet(AA->getModRefInfo(&I, L1Loc)) ||
+ isModSet(AA->getModRefInfo(&I, L2Loc));
+ }))
+ return None;
+
+ return AvailableValue::getSelect(Sel);
+}
+
bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo,
Value *Address, AvailableValue &Res) {
+ if (!DepInfo.isDef() && !DepInfo.isClobber()) {
+ assert(isa<SelectInst>(Address));
+ if (auto R = tryToConvertLoadOfPtrSelect(
+ Load->getParent(), Load->getIterator(), Address, Load->getType(),
+ getDominatorTree(), getAliasAnalysis())) {
+ Res = *R;
+ return true;
+ }
+ return false;
+ }
+
assert((DepInfo.isDef() || DepInfo.isClobber()) &&
"expected a local dependence");
assert(Load->isUnordered() && "rules below are incorrect for ordered access");
@@ -1066,9 +1188,7 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo,
canCoerceMustAliasedValueToLoad(DepLoad, LoadType, DL)) {
const auto ClobberOff = MD->getClobberOffset(DepLoad);
// GVN has no deal with a negative offset.
- Offset = (ClobberOff == None || ClobberOff.getValue() < 0)
- ? -1
- : ClobberOff.getValue();
+ Offset = (ClobberOff == None || *ClobberOff < 0) ? -1 : *ClobberOff;
}
if (Offset == -1)
Offset =
@@ -1092,6 +1212,7 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo,
}
}
}
+
// Nothing known about this clobber, have to be conservative
LLVM_DEBUG(
// fast print dep, using operator<< on instruction is too slow.
@@ -1111,12 +1232,11 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo,
return true;
}
- if (isAllocationFn(DepInst, TLI))
- if (auto *InitVal = getInitialValueOfAllocation(cast<CallBase>(DepInst),
- TLI, Load->getType())) {
- Res = AvailableValue::get(InitVal);
- return true;
- }
+ if (Constant *InitVal =
+ getInitialValueOfAllocation(DepInst, TLI, Load->getType())) {
+ Res = AvailableValue::get(InitVal);
+ return true;
+ }
if (StoreInst *S = dyn_cast<StoreInst>(DepInst)) {
// Reject loads and stores that are to the same address but are of
@@ -1176,16 +1296,23 @@ void GVNPass::AnalyzeLoadAvailability(LoadInst *Load, LoadDepVect &Deps,
continue;
}
- if (!DepInfo.isDef() && !DepInfo.isClobber()) {
- UnavailableBlocks.push_back(DepBB);
- continue;
- }
-
// The address being loaded in this non-local block may not be the same as
// the pointer operand of the load if PHI translation occurs. Make sure
// to consider the right address.
Value *Address = Deps[i].getAddress();
+ if (!DepInfo.isDef() && !DepInfo.isClobber()) {
+ if (auto R = tryToConvertLoadOfPtrSelect(
+ DepBB, DepBB->end(), Address, Load->getType(), getDominatorTree(),
+ getAliasAnalysis())) {
+ ValuesPerBlock.push_back(
+ AvailableValueInBlock::get(DepBB, std::move(*R)));
+ continue;
+ }
+ UnavailableBlocks.push_back(DepBB);
+ continue;
+ }
+
AvailableValue AV;
if (AnalyzeLoadAvailability(Load, DepInfo, Address, AV)) {
// subtlety: because we know this was a non-local dependency, we know
@@ -1923,8 +2050,9 @@ bool GVNPass::processLoad(LoadInst *L) {
if (Dep.isNonLocal())
return processNonLocalLoad(L);
+ Value *Address = L->getPointerOperand();
// Only handle the local case below
- if (!Dep.isDef() && !Dep.isClobber()) {
+ if (!Dep.isDef() && !Dep.isClobber() && !isa<SelectInst>(Address)) {
// This might be a NonFuncLocal or an Unknown
LLVM_DEBUG(
// fast print dep, using operator<< on instruction is too slow.
@@ -1934,7 +2062,7 @@ bool GVNPass::processLoad(LoadInst *L) {
}
AvailableValue AV;
- if (AnalyzeLoadAvailability(L, Dep, L->getPointerOperand(), AV)) {
+ if (AnalyzeLoadAvailability(L, Dep, Address, AV)) {
Value *AvailableValue = AV.MaterializeAdjustedValue(L, L, *this);
// Replace the load!
@@ -2324,7 +2452,7 @@ bool GVNPass::processInstruction(Instruction *I) {
// example if it determines that %y is equal to %x then the instruction
// "%z = and i32 %x, %y" becomes "%z = and i32 %x, %x" which we now simplify.
const DataLayout &DL = I->getModule()->getDataLayout();
- if (Value *V = SimplifyInstruction(I, {DL, TLI, DT, AC})) {
+ if (Value *V = simplifyInstruction(I, {DL, TLI, DT, AC})) {
bool Changed = false;
if (!I->use_empty()) {
// Simplification can cause a special instruction to become not special.
@@ -2491,6 +2619,7 @@ bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
unsigned Iteration = 0;
while (ShouldContinue) {
LLVM_DEBUG(dbgs() << "GVN iteration: " << Iteration << "\n");
+ (void) Iteration;
ShouldContinue = iterateOnFunction(F);
Changed |= ShouldContinue;
++Iteration;