diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
commit | 145449b1e420787bb99721a429341fa6be3adfb6 (patch) | |
tree | 1d56ae694a6de602e348dd80165cf881a36600ed /llvm/lib/Transforms/Scalar/GVN.cpp | |
parent | ecbca9f5fb7d7613d2b94982c4825eb0d33d6842 (diff) | |
download | src-145449b1e420787bb99721a429341fa6be3adfb6.tar.gz src-145449b1e420787bb99721a429341fa6be3adfb6.zip |
Diffstat (limited to 'llvm/lib/Transforms/Scalar/GVN.cpp')
-rw-r--r-- | llvm/lib/Transforms/Scalar/GVN.cpp | 231 |
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; |