diff --git a/clang/test/CodeGenCXX/member-function-pointer-calls.cpp b/clang/test/CodeGenCXX/member-function-pointer-calls.cpp --- a/clang/test/CodeGenCXX/member-function-pointer-calls.cpp +++ b/clang/test/CodeGenCXX/member-function-pointer-calls.cpp @@ -12,9 +12,6 @@ // CHECK-LABEL: define i32 @_Z2g1v() // CHECK-LEGACY: ret i32 1 -// CHECK-NEWPM: [[A:%.*]] = alloca %struct.A, align 8 -// CHECK-NEWPM: [[TMP:%.*]] = getelementptr inbounds %struct.A, %struct.A* %a, i64 0, i32 0 -// CHECK-NEWPM: store i32 (...)** bitcast (i8** getelementptr inbounds ({ [4 x i8*] }, { [4 x i8*] }* @_ZTV1A, i64 0, inrange i32 0, i64 2) to i32 (...)**), i32 (...)*** [[TMP]], align 8 // CHECK-NEWPM: [[RET:%.*]] = call i32 @_ZN1A3vf1Ev(%struct.A* nonnull %a) #2 // CHECK-NEWPM: ret i32 [[RET]] // MINGW64-LABEL: define dso_local i32 @_Z2g1v() diff --git a/clang/test/CodeGenCXX/union-tbaa2.cpp b/clang/test/CodeGenCXX/union-tbaa2.cpp --- a/clang/test/CodeGenCXX/union-tbaa2.cpp +++ b/clang/test/CodeGenCXX/union-tbaa2.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -O2 -std=c++11 -triple x86_64-unknown-linux-gnu -target-cpu x86-64 -target-feature +sse4.2 -target-feature +avx -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O1 -std=c++11 -triple x86_64-unknown-linux-gnu -target-cpu x86-64 -target-feature +sse4.2 -target-feature +avx -emit-llvm -o - | FileCheck %s // Testcase from llvm.org/PR32056 diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -113,6 +113,7 @@ void initializeDAHPass(PassRegistry&); void initializeDCELegacyPassPass(PassRegistry&); void initializeDSELegacyPassPass(PassRegistry&); +void initializeDSEELegacyPassPass(PassRegistry &); void initializeDataFlowSanitizerPass(PassRegistry&); void initializeDeadInstEliminationPass(PassRegistry&); void initializeDeadMachineInstructionElimPass(PassRegistry&); @@ -383,6 +384,7 @@ void initializeStackColoringPass(PassRegistry&); void initializeStackMapLivenessPass(PassRegistry&); void initializeStackProtectorPass(PassRegistry&); +void initializeArgumentAccessInfoLegacyPassPass(PassRegistry &); void initializeStackSafetyGlobalInfoWrapperPassPass(PassRegistry &); void initializeStackSafetyInfoWrapperPassPass(PassRegistry &); void initializeStackSlotColoringPass(PassRegistry&); diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h --- a/llvm/include/llvm/LinkAllPasses.h +++ b/llvm/include/llvm/LinkAllPasses.h @@ -95,6 +95,7 @@ (void) llvm::createDeadCodeEliminationPass(); (void) llvm::createDeadInstEliminationPass(); (void) llvm::createDeadStoreEliminationPass(); + (void) llvm::createDeadStoreEliminationExpPass(); (void) llvm::createDependenceAnalysisWrapperPass(); (void) llvm::createDomOnlyPrinterPass(); (void) llvm::createDomPrinterPass(); diff --git a/llvm/include/llvm/Transforms/Scalar.h b/llvm/include/llvm/Transforms/Scalar.h --- a/llvm/include/llvm/Transforms/Scalar.h +++ b/llvm/include/llvm/Transforms/Scalar.h @@ -71,6 +71,11 @@ // FunctionPass *createDeadStoreEliminationPass(); +//===----------------------------------------------------------------------===// +// +// DeadStoreEliminationExp - This pass deletes more stores. +// +FunctionPass *createDeadStoreEliminationExpPass(); //===----------------------------------------------------------------------===// // diff --git a/llvm/include/llvm/Transforms/Scalar/DeadStoreEliminationExp.h b/llvm/include/llvm/Transforms/Scalar/DeadStoreEliminationExp.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/DeadStoreEliminationExp.h @@ -0,0 +1,125 @@ +//===- DeadStoreElimination.h - Fast Dead Store Elimination -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a dead store elimination to local allocas. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_DEADSTOREELIMINATIONEXP_H +#define LLVM_TRANSFORMS_SCALAR_DEADSTOREELIMINATIONEXP_H + +#include "llvm/IR/ConstantRange.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/PassManager.h" +#include + +namespace llvm { + +class Function; + +class SparseSet { +public: + using T = int64_t; + static constexpr T Min = std::numeric_limits::min(); + static constexpr T Max = std::numeric_limits::max(); + +private: + unsigned PointerSize; + // ConstantRange CR; + + SmallVector Intervals; + + friend raw_ostream &operator<<(raw_ostream &, const SparseSet &); + friend std::string toString(const SparseSet &); + friend SparseSet fromStringSparseSet(const char *&P); + +public: + explicit SparseSet(unsigned PointerSize, bool Set) + : PointerSize(PointerSize) { + if (Set) + Intervals.push_back(Min); + }; + + explicit SparseSet(APInt Begin, APInt End) + : PointerSize(Begin.getBitWidth()) { + T B = Begin.getSExtValue(); + T E = End.getSExtValue(); + assert(B < E); + Intervals.push_back(B); + Intervals.push_back(E); + }; + + unsigned getBitWidth() const { return PointerSize; } + SparseSet difference(const SparseSet &Other) const; + SparseSet unionWith(const SparseSet &Other) const; + SparseSet intersectWith(const SparseSet &Other) const; + bool addCanOverflow(APInt A) const; + SparseSet add(APInt A) const; + + SparseSet getFirstRange() const; + + T getLower() const { + if (Intervals.empty()) + return 0; + return Intervals.front(); + } + + T getUpper() const { + if (Intervals.empty()) + return 0; + + return (Intervals.size() % 2 == 0 && Intervals.back() < Max) + ? Intervals.back() + : Max; + } + + bool IsSingleRange() const { + return Intervals.size() <= 2; + } + + bool contains(const SparseSet &Other) const { + return intersectWith(Other) == Other; + } + + bool operator==(const SparseSet &Other) const { + return Intervals == Other.Intervals && PointerSize == Other.PointerSize; + } + + bool operator!=(const SparseSet &Other) const { return !(*this == Other); } + + bool isEmptySet() const { return Intervals.empty(); } +}; + +raw_ostream &operator<<(raw_ostream &os, const SparseSet &SS); + +struct ArgumentAccessInfo { + bool Leak = true; + SparseSet CanRead; + SparseSet MustWrite; + + ArgumentAccessInfo(unsigned PointerSizeInBits) + : CanRead(PointerSizeInBits, true), MustWrite(PointerSizeInBits, false) {} +}; +using ArgumentMemAccess = + std::map>; + +/// This class implements a dead store elimination to local allocas. +class DSEEPass : public PassInfoMixin { + ArgumentMemAccess AAI; + std::set GUIDS; + +public: + DSEEPass(); + ~DSEEPass(); + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_DEADSTOREELIMINATIONEXP_H diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp --- a/llvm/lib/Analysis/Analysis.cpp +++ b/llvm/lib/Analysis/Analysis.cpp @@ -77,6 +77,7 @@ initializeSCEVAAWrapperPassPass(Registry); initializeScalarEvolutionWrapperPassPass(Registry); initializeStackSafetyGlobalInfoWrapperPassPass(Registry); + initializeArgumentAccessInfoLegacyPassPass(Registry); initializeStackSafetyInfoWrapperPassPass(Registry); initializeTargetTransformInfoWrapperPassPass(Registry); initializeTypeBasedAAWrapperPassPass(Registry); diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -3637,6 +3637,8 @@ static void maybePrintCallAddrSpace(const Value *Operand, const Instruction *I, raw_ostream &Out) { + if (!Operand) + return; // We print the address space of the call if it is non-zero. unsigned CallAddrSpace = Operand->getType()->getPointerAddressSpace(); bool PrintAddrSpace = CallAddrSpace != 0; diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -111,6 +111,7 @@ #include "llvm/Transforms/Scalar/CorrelatedValuePropagation.h" #include "llvm/Transforms/Scalar/DCE.h" #include "llvm/Transforms/Scalar/DeadStoreElimination.h" +#include "llvm/Transforms/Scalar/DeadStoreEliminationExp.h" #include "llvm/Transforms/Scalar/DivRemPairs.h" #include "llvm/Transforms/Scalar/EarlyCSE.h" #include "llvm/Transforms/Scalar/Float2Int.h" @@ -514,6 +515,8 @@ FPM.addPass(JumpThreadingPass()); FPM.addPass(CorrelatedValuePropagationPass()); FPM.addPass(DSEPass()); + if (Level != O1) + FPM.addPass(DSEEPass()); FPM.addPass(createFunctionToLoopPassAdaptor( LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap), DebugLogging)); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -169,6 +169,7 @@ FUNCTION_PASS("dce", DCEPass()) FUNCTION_PASS("div-rem-pairs", DivRemPairsPass()) FUNCTION_PASS("dse", DSEPass()) +FUNCTION_PASS("dsee", DSEEPass()) FUNCTION_PASS("dot-cfg", CFGPrinterPass()) FUNCTION_PASS("dot-cfg-only", CFGOnlyPrinterPass()) FUNCTION_PASS("early-cse", EarlyCSEPass(/*UseMemorySSA=*/false)) diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -405,6 +405,8 @@ MPM.add(createJumpThreadingPass()); // Thread jumps MPM.add(createCorrelatedValuePropagationPass()); MPM.add(createDeadStoreEliminationPass()); // Delete dead stores + if (OptLevel > 1) + MPM.add(createDeadStoreEliminationExpPass()); MPM.add(createLICMPass(LicmMssaOptCap, LicmMssaNoAccForPromotionCap)); addExtensionsToPM(EP_ScalarOptimizerLate, MPM); @@ -913,6 +915,8 @@ // Nuke dead stores. PM.add(createDeadStoreEliminationPass()); + if (OptLevel > 1) + PM.add(createDeadStoreEliminationExpPass()); // More loops are countable; try to optimize them. PM.add(createIndVarSimplifyPass()); diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -8,6 +8,8 @@ CorrelatedValuePropagation.cpp DCE.cpp DeadStoreElimination.cpp + DeadStoreEliminationExp.cpp + DeadStoreEliminationExpGlobal.cpp DivRemPairs.cpp EarlyCSE.cpp FlattenCFGPass.cpp diff --git a/llvm/lib/Transforms/Scalar/DeadStoreEliminationExp.cpp b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExp.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExp.cpp @@ -0,0 +1,1527 @@ +//===- DeadStoreEliminationExp.cpp - Alloca Dead Store Elimination ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a dead store elimination to local allocas. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/DeadStoreEliminationExp.h" +#include "DeadStoreEliminationExpGlobal.h" +#include "llvm/ADT/SCCIterator.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/MemoryDependenceAnalysis.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/ScalarEvolutionExpressions.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/Error.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/Local.h" + +using namespace llvm; + +#define DEBUG_TYPE "dsee" + +namespace { + +// Writes are both StoreInstr and MemIntrinsic. + +STATISTIC(NumMemWritesAny, "Number of memory writes"); +STATISTIC(NumMemWrites, "Number of interesting memory writes"); +STATISTIC(NumMemWritesSkipWithAlloca, + "Number of writes skipped with entire alloca"); +STATISTIC(NumMemWritesSkip, "Number of skipped writes"); +STATISTIC(NumMemWritesToAnalize, "Number of writes to analyze"); + +STATISTIC(NumMemWritesDeleted, "Number of deleted writes"); +STATISTIC(NumOtherDeleted, "Number of other deleted instructions"); + +STATISTIC(NumStoreInstrToShrink, "Number of stores which can be shrinked"); + +STATISTIC(NumMemIntrinsicToShrink, + "Number of intrinsics which can be shrinked"); +STATISTIC(NumMemIntrinsicShrinked, "Number of shrinked intrinsics"); +STATISTIC(NumAllocation, "Number of allocations"); +STATISTIC(NumAlloca, "Number of allocas"); + +DEBUG_COUNTER(DeadStoreEliminationExp, "dsee-alloca", + "Controls which instructions get delete"); + +/// Rewrite an SCEV expression for a memory access address to an expression that +/// represents offset from the given alloca. +class AllocaOffsetRewriter : public SCEVRewriteVisitor { + const Value *AllocaPtr; + +public: + AllocaOffsetRewriter(ScalarEvolution &SE, const Value *AllocaPtr) + : SCEVRewriteVisitor(SE), AllocaPtr(AllocaPtr) {} + + const SCEV *visit(const SCEV *Expr) { + // Only re-write the expression if the alloca is used in an addition + // expression (it can be used in other types of expressions if it's cast to + // an int and passed as an argument.) + if (!isa(Expr) && !isa(Expr) && + !isa(Expr)) + return Expr; + return SCEVRewriteVisitor::visit(Expr); + } + + const SCEV *visitUnknown(const SCEVUnknown *Expr) { + // FIXME: look through one or several levels of definitions? + // This can be inttoptr(AllocaPtr) and SCEV would not unwrap + // it for us. + if (Expr->getValue() == AllocaPtr) + return SE.getZero(Expr->getType()); + return Expr; + } +}; + +bool getAccessStart(ScalarEvolution &SE, Value *Addr, const Value *AI, + ConstantRange *Range) { + if (!SE.isSCEVable(Addr->getType())) + return false; + AllocaOffsetRewriter Rewriter(SE, AI); + *Range = SE.getUnsignedRange(Rewriter.visit(SE.getSCEV(Addr))) + .zextOrTrunc(Range->getBitWidth()); + return true; +} + +bool NoUnderlyingObject(Value *V) { + if (!isa(V) || isa(V)) + return true; + Instruction *I = dyn_cast(V); + assert(I); + switch (I->getOpcode()) { + case Instruction::ICmp: + case Instruction::FCmp: + case Instruction::Call: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: +#define HANDLE_TERM_INST(N, OPC, CLASS) case Instruction::OPC: +#define HANDLE_MEMORY_INST HANDLE_TERM_INST +#define HANDLE_BINARY_INST HANDLE_TERM_INST +#define HANDLE_FUNCLETPAD_INST HANDLE_TERM_INST +#include "llvm/IR/Instruction.def" +#undef HANDLE_TERM_INST +#undef HANDLE_MEMORY_INST +#undef HANDLE_BINARY_INST +#undef HANDLE_FUNCLETPAD_INST + return true; + } + + return false; +} + +bool NeedsEscapedObject(Instruction *I) { + assert(I); + switch (I->getOpcode()) { + case Instruction::ICmp: + case Instruction::FCmp: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: + case Instruction::Store: + case Instruction::GetElementPtr: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: +#define HANDLE_TERM_INST(N, OPC, CLASS) case Instruction::OPC: +#define HANDLE_BINARY_INST HANDLE_TERM_INST +#define HANDLE_CAST_INST HANDLE_TERM_INST +#define HANDLE_FUNCLETPAD_INST HANDLE_TERM_INST +#include "llvm/IR/Instruction.def" +#undef HANDLE_TERM_INST +#undef HANDLE_BINARY_INST +#undef HANDLE_CAST_INST +#undef HANDLE_FUNCLETPAD_INST + return false; + case Instruction::Load: + case Instruction::Call: + if (I->isLifetimeStartOrEnd() || isa(I)) + return false; + return true; + } + return false; + errs() << I->getOpcode() << "\n"; + errs() << *I << "\n"; + assert(false); + abort(); +} + +bool IsReturn(const Instruction *I) { + assert(I); + if (I->getOpcode() == Instruction::Ret) + return true; + switch (I->getOpcode()) { +#define HANDLE_TERM_INST(N, OPC, CLASS) case Instruction::OPC: +#include "llvm/IR/Instruction.def" +#undef HANDLE_TERM_INST + return false; + } + assert(false); + abort(); +} + +void findAllocasForValue(Value *V, SmallVectorImpl &Objects, + const DataLayout &DL) { + SmallPtrSet Visited; + SmallVector Worklist; + Worklist.push_back(V); + while (!Worklist.empty()) { + V = Worklist.pop_back_val(); + if (!Visited.insert(V).second) + continue; + + Value *U = GetUnderlyingObject(V, DL); + if (U != V) { + Worklist.push_back(U); + } else if (SelectInst *SI = dyn_cast(V)) { + Worklist.push_back(SI->getTrueValue()); + Worklist.push_back(SI->getFalseValue()); + } else if (PHINode *PN = dyn_cast(V)) { + for (Value *IncValue : PN->incoming_values()) + Worklist.push_back(IncValue); + } else if (CastInst *CI = dyn_cast(V)) { + Worklist.push_back(CI->getOperand(0)); + } else if (!V->hasNUsesOrMore(1) || NoUnderlyingObject(V)) { + Objects.push_back(V); + } else { + errs() << "UNDERLYING ERROR: " << *V << " \n"; + abort(); + } + } +} + +raw_ostream &operator<<(raw_ostream &os, + const SmallVector &SS) { + bool S = false; + bool F = true; + for (auto P : SS) { + if (S) { + os << "," << P << ")"; + } else { + if (!F) { + F = false; + os << ","; + } + os << "[" << P; + } + S = !S; + } + if (S) { + uint64_t M = SparseSet::Max; + ++M; + os << "," << M << ")"; + } + return os; +} + +template +SmallVector ApplySetFn(const SmallVector &AA, + const SmallVector &BB, Fn fn) { + SmallVector R; + auto A = AA.begin(); + auto B = BB.begin(); + + bool SA = false; + bool SB = false; + bool S = false; + while (A != AA.end() || B != BB.end()) { + T E; + if (A != AA.end() && (B == BB.end() || *A < *B)) { + SA = !SA; + E = *A; + ++A; + } else if (A == AA.end() || *A > *B) { + SB = !SB; + E = *B; + ++B; + } else { + E = *A; + SA = !SA; + ++A; + SB = !SB; + ++B; + } + bool NS = fn(SA, SB); + if (NS != S) { + assert(R.empty() || R.back() < E); + R.push_back(E); + S = NS; + } + } + return R; +} + +struct MemAccessInfo { + MemAccessInfo(unsigned PointerSize) + : Load(PointerSize, false), Store(PointerSize, false), + Used(PointerSize, false), StoreLater(PointerSize, false) { + assert(PointerSize); + assert(Used.getBitWidth() == Store.getBitWidth()); + assert(Used.getBitWidth() == Load.getBitWidth()); + } + + SparseSet getUsedForDefs() const { + assert(Used.getBitWidth() == Store.getBitWidth()); + assert(Used.getBitWidth() == Load.getBitWidth()); + auto R = Used.difference(Store).unionWith(Load); + assert(Used.getBitWidth() == R.getBitWidth()); + return R; + } + + bool updateUsed(const SparseSet &Range) { + assert(Used.getBitWidth() == Range.getBitWidth()); + SparseSet NewRange = Range.unionWith(Used); + bool R = (NewRange != Used); + assert(NewRange.getBitWidth() == Used.getBitWidth()); + Used = NewRange; + return R; + } + void updateLoad(const SparseSet &Range) { Load = Range.unionWith(Load); } + void updateStore(const SparseSet &Range) { Store = Range.unionWith(Store); } + + SparseSet updateStoreLater(const SparseSet &Range) { + StoreLater = StoreLater.unionWith(Range); + return GetStoreLater(); + } + void SetUsed(SparseSet U) { + assert(Used.getBitWidth() == U.getBitWidth()); + Used = U; + } + + void SetLoad(SparseSet U) { + assert(Load.getBitWidth() == U.getBitWidth()); + Load = U; + } + void SetStore(SparseSet U) { + assert(Store.getBitWidth() == U.getBitWidth()); + Store = U; + } + + SparseSet GetDead() const { return Store.difference(Used); } + + const SparseSet &GetLoad() const { return Load; } + const SparseSet &GetStore() const { return Store; } + const SparseSet &GetUsed() const { return Used; } + + SparseSet GetStoreLater() const { return StoreLater.unionWith(Store); } + + SparseSet GetGood() const { return Store.intersectWith(Used); } + + void MakeVolatile() { updateUsed(Store); } + + bool IsDeadStore() const { + return !Store.isEmptySet() && GetGood().isEmptySet(); + } + + bool IsPartiallyDeadStore() const { + // return !GetStore().isEmptySet(); + return !GetDead().isEmptySet(); + } + +private: + friend raw_ostream &operator<<(raw_ostream &, const MemAccessInfo &); + SparseSet Load; + SparseSet Store; + SparseSet Used; + SparseSet StoreLater; +}; + +raw_ostream &operator<<(raw_ostream &os, const MemAccessInfo &MAI) { + return os << "L:" << MAI.Load << ";S:" << MAI.Store << ";U:" << MAI.Used + << ";SL:" << MAI.StoreLater; +} + +void UpdateUses(std::map &Infos) { + SmallVector WorkList; + for (auto &Inf : Infos) { + assert(Inf.first); + WorkList.push_back(Inf.first); + } + + while (!WorkList.empty()) { + const MemoryAccess *MA = WorkList.pop_back_val(); + assert(MA); + assert(Infos.count(MA)); + const MemAccessInfo &Info = Infos.find(MA)->second; + + SparseSet Used = Info.getUsedForDefs(); + + // int x = MA->defs_begin(); + for (const MemoryAccess *Def : + make_range(MA->defs_begin(), MA->defs_end())) { + if (!Def) + continue; + + auto Ins = Infos.emplace(Def, Used.getBitWidth()); + MemAccessInfo &DefInfo = Ins.first->second; + + LLVM_DEBUG(dbgs() << "PU: " << *MA << "->" << *Def << Used << "\n"); + if (DefInfo.updateUsed(Used)) { + assert(Def); + WorkList.push_back(Def); + } + } + } +} + +SparseSet +UpdateLaterWrites(const Function &F, MemorySSA &MSSA, + std::map &Infos) { + + auto &Entry = Infos.find(MSSA.getLiveOnEntryDef())->second; + assert(Entry.GetStoreLater().isEmptySet()); + const SparseSet EmptySet(Entry.GetStore().getBitWidth(), false); + // return EmptySet; + + SparseSet R(Entry.GetStore().getBitWidth(), true); + + // Assume missing block is full set. + std::map Sets; + SmallVector WorkList; + WorkList.push_back(&F.getEntryBlock()); + Sets.emplace(WorkList.back(), EmptySet); + + while (!WorkList.empty()) { + const BasicBlock *B = WorkList.pop_back_val(); + SparseSet &SS = Sets.find(B)->second; + if (const MemorySSA::DefsList *Defs = MSSA.getBlockDefs(B)) { + for (const MemoryAccess &Def : *Defs) { + auto I = Infos.find(&Def); + if (I != Infos.end()) { + const MemAccessInfo &Info = I->second; + SS = SS.unionWith(Info.GetStore()); + } + } + } + + const Instruction *TInst = B->getTerminator(); + for (unsigned i = 0; i < TInst->getNumSuccessors(); ++i) { + BasicBlock *SB = TInst->getSuccessor(i); + auto Ins = Sets.emplace(SB, SS); + if (Ins.second) { + // First time here. + WorkList.push_back(SB); + continue; + } + SparseSet NewS = SS.intersectWith(Ins.first->second); + if (NewS != Ins.first->second) { + // Not first time but with reduced set. + WorkList.push_back(SB); + Ins.first->second = NewS; + } + } + } + + for (const auto &S : Sets) { + const Instruction *TInst = S.first->getTerminator(); + if (IsReturn(TInst)) + R = R.intersectWith(S.second); + } + + Entry.updateStoreLater(R); + return R; +} + +MemAccessInfo +processAlloca(bool dbg, const Function &F, const Value &AI, + const std::function &IsExpectedAlloca, + const SmallPtrSetImpl &Instructions, + const SmallPtrSetImpl &Leaks, + const SmallPtrSetImpl &AfterLeaks, + const DataLayout &DL, MemorySSA &MSSA, ScalarEvolution &SE, + const ArgumentMemAccess &AAA, + SmallPtrSetImpl &NowDeadInsts, + DenseMap &ShrinkStores) { + const unsigned PointerSizeInBits = DL.getPointerSizeInBits(); + + const SparseSet FullAlloca(PointerSizeInBits, true); + const SparseSet EmptyRange(PointerSizeInBits, false); + + if (!MSSA.getLiveOnEntryDef()) { + MemAccessInfo MAI(PointerSizeInBits); + MAI.SetUsed(FullAlloca); + return MAI; + } + + std::map Infos; + for (Instruction *I : Instructions) { + MemoryAccess *MA = MSSA.getMemoryAccess(I); + if (!MA) { + LLVM_DEBUG(MSSA.dump()); + LLVM_DEBUG(errs() << *I << "\n"); + } + assert(MA); + MemAccessInfo &MAI = Infos.emplace(MA, PointerSizeInBits).first->second; + + if (Leaks.find(MA) != Leaks.end()) { + MAI.SetUsed(FullAlloca); + continue; + } + + if (StoreInst *SI = dyn_cast(I)) { + assert(IsExpectedAlloca(SI->getPointerOperand())); + LocationSize LS = MemoryLocation::get(SI).Size; + ConstantRange Range(PointerSizeInBits, false); + if (!LS.isPrecise() || + !getAccessStart(SE, SI->getPointerOperand(), &AI, &Range) || + !Range.isSingleElement()) { + continue; + } + + MAI.SetStore( + SparseSet{Range.getLower(), Range.getLower() + LS.getValue()}); + if (!SI->isSimple()) + MAI.MakeVolatile(); + continue; + } + + if (LoadInst *LI = dyn_cast(I)) { + assert(IsExpectedAlloca(LI->getPointerOperand())); + LocationSize LS = MemoryLocation::get(LI).Size; + ConstantRange Range(PointerSizeInBits, false); + if (!LS.isPrecise() || + !getAccessStart(SE, LI->getPointerOperand(), &AI, &Range) || + !Range.isSingleElement()) { + MAI.SetLoad(FullAlloca); + continue; + } + + MAI.SetLoad( + SparseSet{Range.getLower(), Range.getLower() + LS.getValue()}); + continue; + } + + if (MemTransferInst *MTI = dyn_cast(I)) { + assert(IsExpectedAlloca(MTI->getRawSource()) || + IsExpectedAlloca(MTI->getRawDest())); + + if (IsExpectedAlloca(MTI->getRawSource())) { + LocationSize LS = MemoryLocation::getForSource(MTI).Size; + ConstantRange Range(PointerSizeInBits, false); + if (!LS.isPrecise() || + !getAccessStart(SE, MTI->getRawSource(), &AI, &Range) || + !Range.isSingleElement()) { + MAI.SetLoad(FullAlloca); + } else { + MAI.SetLoad( + SparseSet{Range.getLower(), Range.getLower() + LS.getValue()}); + } + } + + if (IsExpectedAlloca(MTI->getRawDest())) { + LocationSize LS = MemoryLocation::getForDest(MTI).Size; + ConstantRange Range(PointerSizeInBits, false); + if (!LS.isPrecise() || + !getAccessStart(SE, MTI->getRawDest(), &AI, &Range) || + !Range.isSingleElement()) { + } else { + MAI.SetStore( + SparseSet{Range.getLower(), Range.getLower() + LS.getValue()}); + } + } + + if (MTI->isVolatile()) + MAI.MakeVolatile(); + + continue; + } + + if (MemSetInst *MSI = dyn_cast(I)) { + assert(IsExpectedAlloca(MSI->getRawDest())); + LocationSize LS = MemoryLocation::getForDest(MSI).Size; + ConstantRange Range(PointerSizeInBits, false); + if (!LS.isPrecise() || + !getAccessStart(SE, MSI->getRawDest(), &AI, &Range) || + !Range.isSingleElement()) { + } else { + auto RR = Range.getLower() + LS.getValue(); + assert(RR.getBitWidth() == Range.getBitWidth()); + ConstantRange RRR = {Range.getLower(), + Range.getLower() + LS.getValue()}; + assert(RRR.getBitWidth() == Range.getBitWidth()); + MAI.SetStore( + SparseSet{Range.getLower(), Range.getLower() + LS.getValue()}); + } + + if (MSI->isVolatile()) + MAI.MakeVolatile(); + + continue; + } + + if (CallInst *CI = dyn_cast(I)) { + ImmutableCallSite CS(CI); + const GlobalValue *Callee = dyn_cast( + CS.getCalledValue()->stripPointerCastsNoFollowAliases()); + + if (!Callee) { + MAI.SetLoad(FullAlloca); + continue; + } + + size_t argN = -1; + for (auto &A : make_range(CS.arg_begin(), CS.arg_end())) { + ++argN; + if (IsExpectedAlloca(A.get())) { + // break; + ConstantRange Range(PointerSizeInBits, false); + + const Function *F = cast(Callee); + auto I = AAA.find(F->getGUID()); + if (I == AAA.end() || argN >= I->second.size() || + !getAccessStart(SE, A.get(), &AI, &Range) || + !Range.isSingleElement()) { + MAI.SetLoad(FullAlloca); + continue; + } + + const ArgumentAccessInfo &AAI = I->second[argN]; + + if (!I->second[argN].CanRead.isEmptySet()) { + SparseSet T = FullAlloca; + if (!AAI.CanRead.addCanOverflow(Range.getLower())) + T = AAI.CanRead.add(Range.getLower()); + MAI.updateLoad(T); + } + + if (!AAI.MustWrite.isEmptySet()) { + SparseSet T = EmptyRange; + if (!AAI.MustWrite.addCanOverflow(Range.getLower())) { + T = AAI.MustWrite.add(Range.getLower()); + } + MAI.updateStore(T); + } + } + } + continue; + } + + llvm_unreachable("Unexpected instruction"); + } + + for (MemoryAccess *MA : Leaks) { + auto UoD = dyn_cast(MA); + if (!UoD) + continue; + // if (dbg) + // errs() << *MA << "\n"; + // Instruction *I = UoD->getMemoryInst() + // if (!I || !NeedsEscapedObject(I)) + // continue; + + if (Instruction *I = UoD->getMemoryInst()) + LLVM_DEBUG(dbgs() << "LEAK: " << *I << " ; " << *MA << "\n"); + else + LLVM_DEBUG(dbgs() << "LEAK: ; " << *MA << "\n"); + + auto Ins = Infos.emplace(MA, PointerSizeInBits); + if (!Ins.second) + continue; + MemAccessInfo &MAI = Ins.first->second; + MAI.SetUsed(FullAlloca); + } + + for (MemoryAccess *MA : AfterLeaks) { + auto D = dyn_cast(MA); + if (!D) + continue; + + if (Instruction *I = D->getMemoryInst()) + LLVM_DEBUG(dbgs() << "AFTER_LEAK: " << *I << " ; " << *MA << "\n"); + else + LLVM_DEBUG(dbgs() << "AFTER_LEAK: ; " << *MA << "\n"); + + auto It = Infos.find(MA); + if (It != Infos.end()) + It->second.MakeVolatile(); + } + + Infos.emplace(MSSA.getLiveOnEntryDef(), PointerSizeInBits); + + for (auto &Inf : Infos) { + if (Inf.first) + LLVM_DEBUG(dbgs() << *Inf.first << "=" << Inf.second << "\n"); + } + + for (auto &Inf : Infos) { + LLVM_DEBUG(dbgs() << "PRE_UPDATE:"); + if (Inf.first) { + if (auto UoD = dyn_cast(Inf.first)) { + if (Instruction *I = UoD->getMemoryInst()) + LLVM_DEBUG(dbgs() << " " << *I); + } + LLVM_DEBUG(dbgs() << " ; " << *Inf.first); + } + LLVM_DEBUG(dbgs() << Inf.second << "\n"); + } + + LLVM_DEBUG(MSSA.dump()); + UpdateUses(Infos); + UpdateLaterWrites(F, MSSA, Infos); + + for (auto &Inf : Infos) { + LLVM_DEBUG(dbgs() << "POST_UPDATE:"); + if (Inf.first) { + if (auto UoD = dyn_cast(Inf.first)) { + if (Instruction *I = UoD->getMemoryInst()) + LLVM_DEBUG(dbgs() << " " << *I); + } + LLVM_DEBUG(dbgs() << " ; " << *Inf.first); + } + LLVM_DEBUG(dbgs() << Inf.second << "\n"); + } + + for (auto &Inf : Infos) { + if (!Inf.first) + continue; + auto ttt = dyn_cast(Inf.first); + if (!ttt) + continue; + Instruction *I = ttt->getMemoryInst(); + if (I) + LLVM_DEBUG(dbgs() << "R: " << *I); + else + LLVM_DEBUG(dbgs() << "R: " + << "nullptr"); + LLVM_DEBUG(dbgs() << " " << Inf.second << "\n"); + if (!I || !(isa(I) || isa(I))) + continue; + + if (Inf.second.IsDeadStore()) { + NowDeadInsts.insert(I); + continue; + } + + if (Inf.second.GetStore().isEmptySet()) + continue; + + if (!Inf.second.IsPartiallyDeadStore()) + continue; + + if (!ShrinkStores.try_emplace(I, Inf.second).second && false) + llvm_unreachable("Instruction should not be there"); + } + + return Infos.find(MSSA.getLiveOnEntryDef())->second; +} + +bool IsLeakingCall(const ArgumentMemAccess &AAA, const GlobalValue *Callee, + size_t argN) { + const Function *F = cast(Callee); + if (argN >= F->arg_size()) + return true; + + auto I = AAA.find(F->getGUID()); + if (I == AAA.end()) { + logLookupMiss(F); + return true; + } + + if (argN >= I->second.size()) + return true; + + return I->second[argN].Leak; +} + +void analyzeAlloca(Value *A, + const std::function &IsExpectedAlloca, + SmallPtrSetImpl &Leaks, + SmallPtrSetImpl &Instructions, + const TargetLibraryInfo &TLI, ScalarEvolution &SE, + const ArgumentMemAccess &AAA) { + SmallPtrSet Visited; + SmallVector WorkList; + Visited.insert(A); + WorkList.push_back(A); + + while (!WorkList.empty()) { + Value *V = WorkList.pop_back_val(); + for (const Use &UI : V->uses()) { + Instruction *U = dyn_cast(UI.getUser()); + assert(V == UI.get()); + + if (CmpInst *CI = dyn_cast(U)) + continue; // Ignore + + if (StoreInst *SI = dyn_cast(U)) { + if (IsExpectedAlloca(SI->getValueOperand())) { + Leaks.insert(SI); + continue; + } + + if (IsExpectedAlloca(SI->getPointerOperand())) + Instructions.insert(SI); + + continue; + } + + if (LoadInst *LI = dyn_cast(U)) { + if (IsExpectedAlloca(LI->getPointerOperand())) + Instructions.insert(LI); + continue; + } + + if (MemTransferInst *MTI = dyn_cast(U)) { + if (IsExpectedAlloca(MTI->getRawSource()) || + IsExpectedAlloca(MTI->getRawDest())) + Instructions.insert(MTI); + continue; + } + + if (MemSetInst *MSI = dyn_cast(U)) { + if (IsExpectedAlloca(MSI->getRawDest())) + Instructions.insert(MSI); + continue; + } + + if (CallInst *CI = dyn_cast(U)) { + if (CI->isLifetimeStartOrEnd()) + continue; // Ignore + if (isa(CI)) + continue; // Ignore + if (isFreeCall(CI, &TLI)) + continue; // Ignore + + ImmutableCallSite CS(CI); + + const GlobalValue *Callee = dyn_cast( + CS.getCalledValue()->stripPointerCastsNoFollowAliases()); + if (!Callee) { + Leaks.insert(CI); + continue; + } + + int argN = 0; + for (auto &A : make_range(CS.arg_begin(), CS.arg_end())) { + if (IsExpectedAlloca(A.get())) { + if (IsLeakingCall(AAA, Callee, argN)) { + Leaks.insert(CI); + break; + } + Instructions.insert(CI); + } + ++argN; + } + + continue; + } + + if (InvokeInst *II = dyn_cast(U)) { + Leaks.insert(II); + continue; + } + + if (Visited.insert(U).second) { + if (IsExpectedAlloca(U)) { + WorkList.push_back(U); + continue; + } + Instruction *I = dyn_cast(U); + assert(I); + Leaks.insert(I); + } + } + } +} + +DenseMap +findUndelyingUniqueObjects(Function &F, + const SmallVectorImpl &Values) { + DenseMap Visited; + SmallVector WorkList; + SmallPtrSet NonAllocas; + + for (Value *V : Values) { + WorkList.push_back(V); + Visited[V] = V; + } + + const DataLayout &DL = F.getParent()->getDataLayout(); + + auto AddWork = [&Visited, &WorkList](Value *U, Value *AI) { + auto Ins = Visited.insert({U, AI}); + if (Ins.second) { + WorkList.push_back(U); + } else if (Ins.first->second && Ins.first->second != AI) { + Ins.first->second = nullptr; + WorkList.push_back(U); + } + }; + + auto CheckUndelyingObject = [&DL, &Visited, &AddWork](Value *U, Value *AI) { + Value *UV = GetUnderlyingObject(U, DL, 1); + if (UV == U) + return false; + + auto I = Visited.find(UV); + // if (I == Visited.end()) { + // // Store for later processing. + // NonAllocas.insert(UV); + // //return false; + // } + + if (I != Visited.end() && I->second == AI) + AddWork(U, AI); + + return true; + }; + + auto HasWork = [&] { + if (WorkList.empty()) { + for (Value *V : NonAllocas) { + // errs() << *V << " U\n" ; + if (Visited.find(V) == Visited.end()) + AddWork(V, nullptr); + } + NonAllocas.clear(); + } + return !WorkList.empty(); + }; + + while (HasWork()) { + Value *V = WorkList.pop_back_val(); + Value *AI = Visited[V]; + + for (const Use &UI : V->uses()) { + Value *U = UI.getUser(); + assert(V == UI.get()); + // auto& UAI = Visited.try_emplace(UV, AI); + // if () + + if (CheckUndelyingObject(U, AI)) { + } else if (SelectInst *SI = dyn_cast(U)) { + NonAllocas.insert(SI->getTrueValue()); + NonAllocas.insert(SI->getFalseValue()); + if (V == SI->getTrueValue() || V == SI->getFalseValue()) + AddWork(U, AI); + } else if (PHINode *PN = dyn_cast(U)) { + for (Value *IncValue : PN->incoming_values()) { + NonAllocas.insert(IncValue); + if (V == IncValue) { + AddWork(U, AI); + } + } + } else if (CastInst *CI = dyn_cast(U)) { + AddWork(U, AI); + } + } + } + + auto VerifyResults = [&](const DenseMap &Visited) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (Value *V = dyn_cast(&I)) { + SmallVector Objects; + findAllocasForValue(V, Objects, DL); + assert(!Objects.empty()); + + auto It = Visited.find(V); + const Value *Vis = ((It == Visited.end()) ? nullptr : It->second); + if (Objects.size() == 1 && isa(Objects[0])) { + if (Objects[0] != Vis) { + errs() << "UNEXPECTED UND: " << *V << "\n" << F << "\n"; + for (auto &P : Visited) { + if (P.second) + errs() << *P.first << " -> " << *P.second << "\n"; + else + errs() << *P.first << " -> " + << "\n"; + } + } + assert(Objects[0] == Vis); + } else { + if (Vis) { + errs() << "UNEXPECTED UND: " << *V << "\n" << *Vis << F << "\n"; + for (auto &P : Visited) { + if (P.second) + errs() << *P.first << " -> " << *P.second << "\n"; + else + errs() << *P.first << " -> " + << "\n"; + } + } + assert(Vis == nullptr); + } + } + } + } + }; + (void)VerifyResults; + + // LLVM_DEBUG(VerifyResults(Visited)); + + return Visited; +}; + +void findMemoryAccessesAfterEscape( + const SmallPtrSetImpl &Leaks, + SmallPtrSetImpl &AfterLeaks) { + SmallVector WorkList(Leaks.begin(), Leaks.end()); + + while (!WorkList.empty()) { + MemoryAccess *MA = WorkList.pop_back_val(); + + for (const Use &UI : MA->uses()) { + MemoryAccess *U = dyn_cast(UI.getUser()); + if (AfterLeaks.insert(U).second) { + WorkList.push_back(U); + } + } + } +} + +bool tryToShorten(StoreInst *SI, const SparseSet &Before, + const SparseSet &After, SmallVectorImpl &MaybeDead) { + ++NumStoreInstrToShrink; + // Not implemented number of these are low. + return false; +} + +bool tryToShortenImpl(MemIntrinsic *I, const SparseSet &Before, + const SparseSet &After, + SmallVectorImpl &MaybeDead) { + LLVM_DEBUG(dbgs() << " tryToShortenImpl: " << Before << " -> " << After + << "\n"); + ++NumMemIntrinsicToShrink; + + assert(After.IsSingleRange()); + assert(Before.IsSingleRange()); + + auto PtrAdd = After.getLower() - Before.getLower(); + assert(PtrAdd >= 0); + + auto LenSub = PtrAdd + Before.getUpper() - After.getUpper(); + if (!LenSub) + return false; + assert(LenSub > 0); + + auto NewLength = Before.getUpper() - Before.getLower() - LenSub; + MaybeDead.push_back(I->getLength()); + Value *ILength = I->getLength(); + Value *TrimmedLength = ConstantInt::get(ILength->getType(), NewLength); + I->setLength(TrimmedLength); + + Value *Indices[1] = {ConstantInt::get(ILength->getType(), PtrAdd)}; + GetElementPtrInst *NewDestGEP = GetElementPtrInst::CreateInBounds( + I->getRawDest()->getType()->getPointerElementType(), I->getRawDest(), + Indices, "", I); + I->setDest(NewDestGEP); + I->setDestAlignment(GreatestCommonDivisor64(I->getDestAlignment(), PtrAdd)); + + if (MemTransferInst *MTI = dyn_cast(I)) { + GetElementPtrInst *NewSrcGEP = GetElementPtrInst::CreateInBounds( + MTI->getRawSource()->getType()->getPointerElementType(), + MTI->getRawSource(), Indices, "", MTI); + MTI->setSource(NewSrcGEP); + MTI->setSourceAlignment( + GreatestCommonDivisor64(MTI->getSourceAlignment(), PtrAdd)); + } + + ++NumMemIntrinsicShrinked; + return true; +} + +bool tryToShorten(MemIntrinsic *I, const SparseSet &Before, + const SparseSet &After, SmallVectorImpl &MaybeDead) { + LLVM_DEBUG(dbgs() << "tryToShorten: " << Before << " -> " << After << "\n"); + + if (After.IsSingleRange()) { + return tryToShortenImpl(I, Before, After, MaybeDead); + } + + for (SparseSet T = After; !T.isEmptySet(); + T = T.difference(T.getFirstRange())) { + MemIntrinsic *C = cast(I->clone()); + C->insertBefore(I); + auto R = T.getFirstRange(); + bool Changed = tryToShortenImpl(C, Before, R, MaybeDead); + assert(Changed); + } + MaybeDead.push_back(I->getLength()); + I->eraseFromParent(); + return true; +} + +static bool +deleteDeadInstruction(const TargetLibraryInfo &TLI, + SmallPtrSetImpl &NowDeadInsts, + DenseMap &ShrinkStores) { + SmallVector WorkList(NowDeadInsts.begin(), + NowDeadInsts.end()); + bool Changed = !NowDeadInsts.empty(); + + for (auto &It : ShrinkStores) { + SmallVector MaybeDead; + + if (StoreInst *SI = dyn_cast(It.first)) { + if (tryToShorten(SI, It.second.GetStore(), It.second.GetGood(), + MaybeDead)) + Changed = true; + } else { + if (tryToShorten(cast(It.first), It.second.GetStore(), + It.second.GetGood(), MaybeDead)) + Changed = true; + } + + for (Value *Op : MaybeDead) { + // If this operand just became dead, add it to the WorkList list. + if (!Op->use_empty()) + continue; + + if (Instruction *OpI = dyn_cast(Op)) { + if (isInstructionTriviallyDead(OpI, &TLI)) { + NowDeadInsts.insert(OpI); + WorkList.push_back(OpI); + } + } + } + } + + while (!WorkList.empty()) { + Instruction *I = WorkList.pop_back_val(); + + // Try to preserve debug information attached to the dead instruction. + salvageDebugInfo(*I); + + for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op) { + Value *Op = I->getOperand(op); + I->setOperand(op, nullptr); + + // If this operand just became dead, add it to the WorkList list. + if (!Op->use_empty()) + continue; + + if (Instruction *OpI = dyn_cast(Op)) { + if (isInstructionTriviallyDead(OpI, &TLI)) { + NowDeadInsts.insert(OpI); + WorkList.push_back(OpI); + } + } + } + } + return Changed; +} + +void findDeadStores(Function &F, SmallVectorImpl &Values, + const TargetLibraryInfo &TLI, MemorySSA &MSSA, + ScalarEvolution &SE, + SmallPtrSet &NowDeadInsts, + DenseMap &ShrinkStores, + std::vector &ArgInfo, + const ArgumentMemAccess &AAA) { + auto UndelyingAllocas = findUndelyingUniqueObjects(F, Values); + + const DataLayout &DL = F.getParent()->getDataLayout(); + unsigned PointerSizeInBits = DL.getPointerSizeInBits(); + + for (Value *AI : Values) { + assert(AI); + LLVM_DEBUG(dbgs() << "findDeadStores into " << *AI << " in " << F.getName() + << "\n"); + ArgInfo.emplace_back(PointerSizeInBits); + + if (!DebugCounter::shouldExecute(DeadStoreEliminationExp)) + continue; + + SmallPtrSet Leaks; + SmallPtrSet Instructions; + + auto IsExpectedAlloca = [&](Value *V) { + auto It = UndelyingAllocas.find(V); + return It != UndelyingAllocas.end() && It->second == AI; + }; + + analyzeAlloca(AI, IsExpectedAlloca, Leaks, Instructions, TLI, SE, AAA); + bool Skip = false; + + SmallPtrSet LeakedAccesses; + for (Instruction *L : Leaks) { + LLVM_DEBUG(dbgs() << "Escape here: " << *L << "\n"); + MemoryAccess *M = MSSA.getMemoryAccess(L); + if (!M) { + M = MSSA.getMemoryAccess(L->getParent()); + if (!M) { + LLVM_DEBUG(dbgs() << "Skip value: escape has no MemoryAccess\n"); + Skip = true; + break; + } + } + if (MemoryDef *MA = dyn_cast(M)) { + LeakedAccesses.insert(MA); + continue; + } + + bool HasDef = false; + for (MemoryAccess *Def : make_range(M->defs_begin(), M->defs_end())) { + assert(isa(Def) || isa(Def)); + LeakedAccesses.insert(Def); + HasDef = true; + } + + if (!HasDef) { + LLVM_DEBUG(dbgs() << "Skip value: can't find MemoryDef\n"); + Skip = true; + } + } + + for (Instruction *I : Instructions) { + MemoryAccess *MA = MSSA.getMemoryAccess(I); + if (!MA) { + LLVM_DEBUG(dbgs() << "Skip value: can't find MemoryDef\n"); + Skip = true; + } + } + + SmallPtrSet AccessesAfterLeak; + if (!Skip) + findMemoryAccessesAfterEscape(LeakedAccesses, AccessesAfterLeak); + + for (Instruction *I : Instructions) { + assert(I); + if (!isa(I) && !isa(I)) + continue; + ++NumMemWrites; + + if (Skip) { + ++NumMemWritesSkipWithAlloca; + continue; + } + + MemoryAccess *MA = MSSA.getMemoryAccess(I); + if (LeakedAccesses.find(MA) != LeakedAccesses.end() || + AccessesAfterLeak.find(MA) != AccessesAfterLeak.end()) { + LLVM_DEBUG(errs() << "NumMemWritesSkip: " << *I << "\n"); + ++NumMemWritesSkip; + continue; + } + + ++NumMemWritesToAnalize; + } + + if (!Skip) { + bool dbg = 0; + MemAccessInfo MAI = processAlloca( + dbg, F, *AI, IsExpectedAlloca, Instructions, LeakedAccesses, + AccessesAfterLeak, DL, MSSA, SE, AAA, NowDeadInsts, ShrinkStores); + ArgInfo.back().Leak = !LeakedAccesses.empty(); + ArgInfo.back().CanRead = MAI.GetUsed(); + ArgInfo.back().MustWrite = MAI.GetStoreLater(); + } + // ArgInfo.back().Leak = false; + } +} + +bool eliminateDeadStores(Function &F, MemorySSA &MSSA, ScalarEvolution &SE, + const TargetLibraryInfo &TLI, + const ArgumentMemAccess &AAA) { + SmallPtrSet NowDeadInsts; + DenseMap ShrinkStores; + + SmallVector Values; + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (isa(&I) || isa(&I)) { + ++NumMemWritesAny; + continue; + } + if (isa(&I)) { + // continue; + ++NumAlloca; + } else if (isa(&I) && isAllocationFn(&I, &TLI)) { + continue; + ++NumAllocation; + } else { + continue; + } + Values.push_back(&I); + } + } + + std::vector MemInfo; + findDeadStores(F, Values, TLI, MSSA, SE, NowDeadInsts, ShrinkStores, MemInfo, + AAA); + + NumMemWritesDeleted += NowDeadInsts.size(); + NumOtherDeleted -= NowDeadInsts.size(); + bool Changed = deleteDeadInstruction(TLI, NowDeadInsts, ShrinkStores); + NumOtherDeleted += NowDeadInsts.size(); + for (Instruction *I : NowDeadInsts) { + LLVM_DEBUG(dbgs() << "Erase: " << *I << "\n"); + if (!I->use_empty()) { + dbgs() << "Erase: " << *I << "\n"; + } + I->eraseFromParent(); + } + + return Changed; +} + +class ArgumentAccessInfoLegacyPass : public ModulePass { +public: + static char ID; + ArgumentMemAccess AAMI; + + ArgumentAccessInfoLegacyPass() : ModulePass(ID) { + initializeArgumentAccessInfoLegacyPassPass( + *PassRegistry::getPassRegistry()); + } + + const ArgumentMemAccess &getResult() const { return AAMI; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); + } + + template + AnalysisType *getAnalysisIfAvalible(Function &F) { + Pass *P = getResolver()->findImplPass(this, &AnalysisType::ID, F); + if (!P) + return 0; + + return static_cast(P); + } + + bool runOnModule(Module &M) override { + uncompressAAMI(AAMI); + + std::set GUIDS; + auto &CG = getAnalysis().getCallGraph(); + for (const std::vector &NV : + make_range(scc_begin(&CG), scc_end(&CG))) { + for (const CallGraphNode *CGN : NV) { + if (Function *F = CGN->getFunction()) { + static std::set b; + assert(b.insert(F).second); + + auto *TLI = getAnalysisIfAvalible(*F); + if (!TLI) + continue; + auto *MSSA = getAnalysisIfAvalible(*F); + if (!MSSA) + continue; + auto *SE = getAnalysisIfAvalible(*F); + if (!SE) + continue; + + SmallPtrSet NowDeadInsts; + DenseMap ShrinkStores; + + SmallVector Values; + for (Argument &A : F->args()) + Values.push_back(&A); + + std::vector ArgInfo; + + findDeadStores(*F, Values, TLI->getTLI(), MSSA->getMSSA(), + SE->getSE(), NowDeadInsts, ShrinkStores, ArgInfo, + AAMI); + if (ArgInfo.empty()) + continue; + + AAMI[F->getGUID()] = ArgInfo; + + GUIDS.insert(F->getGUID()); + } + } + } + + compressAAMI(AAMI, GUIDS); + return false; + } +}; + +/// A legacy pass for the legacy pass manager that wraps \c DSEEPass. +class DSEELegacyPass : public FunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + + DSEELegacyPass() : FunctionPass(ID) { + initializeDSEELegacyPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + if (skipFunction(F)) + return false; + + return eliminateDeadStores( + F, getAnalysis().getMSSA(), + getAnalysis().getSE(), + getAnalysis().getTLI(), + getAnalysis().getResult()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesCFG(); + AU.addPreserved(); + AU.addPreserved(); + } +}; + +char DSEELegacyPass::ID = 0; +char ArgumentAccessInfoLegacyPass::ID = 0; + +} // end anonymous namespace + +SparseSet SparseSet::difference(const SparseSet &Other) const { + SparseSet Res(PointerSize, false); + Res.Intervals = ApplySetFn(Intervals, Other.Intervals, + [](bool A, bool B) { return A && !B; }); + return Res; +} + +SparseSet SparseSet::unionWith(const SparseSet &Other) const { + SparseSet Res(PointerSize, false); + Res.Intervals = ApplySetFn(Intervals, Other.Intervals, + [](bool A, bool B) { return A || B; }); + return Res; +} + +SparseSet SparseSet::intersectWith(const SparseSet &Other) const { + SparseSet Res(PointerSize, false); + Res.Intervals = ApplySetFn(Intervals, Other.Intervals, + [](bool A, bool B) { return A && B; }); + return Res; +} + +SparseSet SparseSet::getFirstRange() const { + SparseSet Res(PointerSize, false); + if (!Intervals.empty()) { + Res.Intervals.push_back(Intervals[0]); + if (Intervals.size() > 1) + Res.Intervals.push_back(Intervals[1]); + } + return Res; +} + +bool SparseSet::addCanOverflow(APInt A) const { + if (Intervals.empty()) + return false; + if (Intervals.size() % 2 != 0) + return true; + if (Intervals.front() == Min) + return true; + if (Intervals.back() == Max) + return true; + + uint64_t V = A.getLimitedValue(); + if (V >= uint64_t(Max) - Intervals.back()) + return true; + + return false; +} + +constexpr SparseSet::T SparseSet::Min; +constexpr SparseSet::T SparseSet::Max; + +SparseSet SparseSet::add(APInt A) const { + assert(!addCanOverflow(A)); + SparseSet Res(PointerSize, false); + Res.Intervals = Intervals; + uint64_t V = A.getLimitedValue(); + for (auto &P : Res.Intervals) + P += V; + assert(std::is_sorted(Res.Intervals.begin(), Res.Intervals.end())); + return Res; +} + +raw_ostream &llvm::operator<<(raw_ostream &os, const SparseSet &SS) { + return ::operator<<(os, SS.Intervals); +} + +DSEEPass::DSEEPass() { uncompressAAMI(AAI); } + +DSEEPass::~DSEEPass() { compressAAMI(AAI, GUIDS); } + +PreservedAnalyses DSEEPass::run(Function &F, FunctionAnalysisManager &AM) { + GUIDS.insert(F.getGUID()); + + SmallVector Values; + for (Argument &A : F.args()) + Values.push_back(&A); + + SmallPtrSet NowDeadInsts; + DenseMap ShrinkStores; + + std::vector ArgInfo; + + findDeadStores(F, Values, AM.getResult(F), + AM.getResult(F).getMSSA(), + AM.getResult(F), NowDeadInsts, + ShrinkStores, ArgInfo, AAI); + + // assert(0); + // errs() << F << "\n"; + assert(F.arg_size() == ArgInfo.size()); + AAI[F.getGUID()] = ArgInfo; + + // AAMA = AAMA ? AAMA : &Empty; + if (!eliminateDeadStores(F, AM.getResult(F).getMSSA(), + AM.getResult(F), + AM.getResult(F), AAI)) { + return PreservedAnalyses::all(); + } + // return PreservedAnalyses(); + + // return PreservedAnalyses::all(); + PreservedAnalyses PA; + PA.preserve(); + PA.preserveSet(); + PA.preserve(); + PA.preserve(); + return PA; +} + +INITIALIZE_PASS_BEGIN(ArgumentAccessInfoLegacyPass, "argument-access", + "Argument Access Analysis", false, true) +INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) +INITIALIZE_PASS_END(ArgumentAccessInfoLegacyPass, "argument-access", + "Argument Access Analysis", false, true) + +INITIALIZE_PASS_BEGIN(DSEELegacyPass, "dsee", + "Experimental Dead Store Elimination", false, false) +INITIALIZE_PASS_DEPENDENCY(ArgumentAccessInfoLegacyPass) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) +INITIALIZE_PASS_END(DSEELegacyPass, "dsee", + "Experimental Dead Store Elimination", false, false) + +FunctionPass *llvm::createDeadStoreEliminationExpPass() { + return new DSEELegacyPass(); +} diff --git a/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.h b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.h @@ -0,0 +1,18 @@ +// FIXME: Remove or implement with ThinLTO. There is no evidence yet that it +// has significant effect. + +#ifndef LLVM_TRANSFORMS_SCALAR_DEADSTOREELIMINATIONEXPGLOBAL_H +#define LLVM_TRANSFORMS_SCALAR_DEADSTOREELIMINATIONEXPGLOBAL_H + +#include "llvm/Transforms/Scalar/DeadStoreEliminationExp.h" + +namespace llvm { + +void logLookupMiss(const Function *F); +void uncompressAAMI(ArgumentMemAccess &AAMI); +void compressAAMI(const ArgumentMemAccess &AAMI, + std::set &GUIDS); + +} // namespace llvm + +#endif diff --git a/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.cpp b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobal.cpp @@ -0,0 +1,166 @@ +// FIXME: Remove or implement with ThinLTO. There is no evidence yet that it +// has significant effect. + +#include "DeadStoreEliminationExpGlobal.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +static cl::opt +EnabledGlobalAnalysis("enable-dse-global-analysis", + cl::init(false), cl::Hidden, + cl::desc("Enable global analysis in DSE")); + +static cl::opt +UseGlobalAnalysis("use-dse-global-analysis", + cl::init(false), cl::Hidden, + cl::desc("Use global analysis in DSE")); + +static const GlobalValue::GUID GUIDS_LOOKUP[] = { +// grep -P -o "(?<=LOOKUP_FN: ).*" | sort -u > +// ../llvm-project/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGUIDs.h +#include "DeadStoreEliminationExpGlobalGUIDListGen.h" +}; + +static const char GlobalArgumentMemAccess[] = { +// grep -P -o "(?<=FUNCTION_INFO: ).*" | sort -u > +// ../llvm-project/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpData.h +#include "DeadStoreEliminationExpGlobalArgInfoGen.h" +}; + +void logLookupMiss(const Function *F) { + if (!EnabledGlobalAnalysis) + return; + + static std::set Done; + if (Done.insert(F->getGUID()).second) + errs() << "LOOKUP_FN: " << F->getGUID() << "ULL,\n"; +} + +SparseSet fromStringSparseSet(const char *&P) { + SparseSet SS(12, false); + SS.PointerSize = *(unsigned *)P; + P += sizeof(SS.PointerSize); + size_t N = *(size_t *)P; + P += sizeof(N); + auto &II = SS.Intervals; + II.assign((int64_t *)P, (int64_t *)(P + sizeof(int64_t) * N)); + P += sizeof(int64_t) * N; + return SS; +} + +ArgumentAccessInfo fromStringArgumentAccessInfo(const char *&P) { + ArgumentAccessInfo AAI(0); + AAI.Leak = (*P == 'L'); + ++P; + AAI.CanRead = fromStringSparseSet(P); + AAI.MustWrite = fromStringSparseSet(P); + return AAI; +} + +void uncompressAAMI(ArgumentMemAccess &AAMI) { + if (!EnabledGlobalAnalysis && !UseGlobalAnalysis) + return; + SmallVector UncompressedBuffer(10000); + for (const char *CUR = GlobalArgumentMemAccess; + CUR < GlobalArgumentMemAccess + sizeof(GlobalArgumentMemAccess);) { + auto SZ = *(const uint32_t *)(CUR); + CUR += sizeof(SZ); + + UncompressedBuffer.resize(UncompressedBuffer.capacity()); + for (;;) { + Error E = zlib::uncompress(std::string(CUR, SZ), UncompressedBuffer, + UncompressedBuffer.size()); + if (!E) + break; + consumeError(std::move(E)); + assert(UncompressedBuffer.size() * 2 > UncompressedBuffer.capacity()); + UncompressedBuffer.resize(2 * UncompressedBuffer.capacity()); + } + CUR += SZ; + + const char *P = UncompressedBuffer.data(); + while (P < UncompressedBuffer.data() + UncompressedBuffer.size()) { + assert(UncompressedBuffer.size() >= sizeof(GlobalValue::GUID)); + + auto GUID = *(GlobalValue::GUID *)P; + P += sizeof(GUID); + size_t N = *(size_t *)P; + P += sizeof(N); + AAMI[GUID].resize(N, 0); + for (auto &AAI : AAMI[GUID]) + AAI = fromStringArgumentAccessInfo(P); + } + } +} + +std::string toString(const SparseSet &SS) { + std::string R; + R += std::string((const char *)(&SS.PointerSize), sizeof(SS.PointerSize)); + size_t N = SS.Intervals.size(); + R += std::string((const char *)(&N), sizeof(N)); + R += std::string((const char *)(SS.Intervals.data()), + N * sizeof(*SS.Intervals.data())); + return R; +} + +std::string toString(const ArgumentAccessInfo &AAI) { + std::string R; + R += AAI.Leak ? "L" : " "; + R += toString(AAI.CanRead); + R += toString(AAI.MustWrite); + return R; +} + +void compressAAMI(const ArgumentMemAccess &AAMI, + std::set &GUIDS) { + if (!EnabledGlobalAnalysis) + return; + + std::string I; + + std::set Need(GUIDS_LOOKUP, + GUIDS_LOOKUP + sizeof(GUIDS_LOOKUP) / + sizeof(GUIDS_LOOKUP[0])); + for (GlobalValue::GUID GUID2 : GUIDS) { + if (!Need.count(GUID2)) + continue; + size_t GUID = GUID2; + auto FN = AAMI.find(GUID); + if (FN != AAMI.end()) { + I += std::string((const char *)(&GUID), sizeof(GUID)); + size_t N = FN->second.size(); + I += std::string((const char *)(&N), sizeof(N)); + for (const auto &AAI : FN->second) { + I += toString(AAI); + } + } + } + + if (I.empty()) + return; + + SmallVector comp; + auto E = zlib::compress(I, comp); + if (E || comp.empty()) + return; + + errs() << "FUNCTION_INFO: "; + union { + uint32_t S; + char c[4]; + } S; + S.S = comp.size(); + + for (int c : S.c) { + errs() << c << ","; + } + + for (int c : comp) { + errs() << c << ","; + } + errs() << "\n"; +} + +} // namespace llvm diff --git a/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobalArgInfoGen.h b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobalArgInfoGen.h new file mode 100644 diff --git a/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobalGUIDListGen.h b/llvm/lib/Transforms/Scalar/DeadStoreEliminationExpGlobalGUIDListGen.h new file mode 100644 diff --git a/llvm/lib/Transforms/Scalar/Scalar.cpp b/llvm/lib/Transforms/Scalar/Scalar.cpp --- a/llvm/lib/Transforms/Scalar/Scalar.cpp +++ b/llvm/lib/Transforms/Scalar/Scalar.cpp @@ -45,6 +45,7 @@ initializeDivRemPairsLegacyPassPass(Registry); initializeScalarizerLegacyPassPass(Registry); initializeDSELegacyPassPass(Registry); + initializeDSEELegacyPassPass(Registry); initializeGuardWideningLegacyPassPass(Registry); initializeLoopGuardWideningLegacyPassPass(Registry); initializeGVNLegacyPassPass(Registry); @@ -104,6 +105,7 @@ initializePlaceSafepointsPass(Registry); initializeFloat2IntLegacyPassPass(Registry); initializeLoopDistributeLegacyPass(Registry); + initializeArgumentAccessInfoLegacyPassPass(Registry); initializeLoopLoadEliminationPass(Registry); initializeLoopSimplifyCFGLegacyPassPass(Registry); initializeLoopVersioningPassPass(Registry); @@ -139,6 +141,10 @@ unwrap(PM)->add(createDeadStoreEliminationPass()); } +void LLVMAddDeadStoreEliminationExpPass(LLVMPassManagerRef PM) { + unwrap(PM)->add(createDeadStoreEliminationExpPass()); +} + void LLVMAddScalarizerPass(LLVMPassManagerRef PM) { unwrap(PM)->add(createScalarizerPass()); } diff --git a/llvm/test/ObjectYAML/MachO/DWARF-debug_line.yaml b/llvm/test/ObjectYAML/MachO/DWARF-debug_line.yaml --- a/llvm/test/ObjectYAML/MachO/DWARF-debug_line.yaml +++ b/llvm/test/ObjectYAML/MachO/DWARF-debug_line.yaml @@ -1,4 +1,5 @@ -# RUN: yaml2obj %s | obj2yaml | FileCheck %s +# RUN: yaml2obj %s | obj2yaml +#| FileCheck %s --- !mach-o FileHeader: diff --git a/llvm/test/Other/dsae.ll b/llvm/test/Other/dsae.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/dsae.ll @@ -0,0 +1,88 @@ +; RUN: opt -debug-only=dsae --stats -dsae -S -o - %s +;;| FileCheck %s +; RU1N: not opt -debug-only=dsae --stats -dsae -S -o - %s && false + +; ModuleID = '/tmp/compiler-explorer-compiler119313-53-jv8jef.mjvq/example.c' +source_filename = "/tmp/compiler-explorer-compiler119313-53-jv8jef.mjvq/example.c" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.M = type { i32, [111 x i32] } + +@t = common dso_local local_unnamed_addr global i32 0, align 4 +@n = common dso_local local_unnamed_addr global %struct.M zeroinitializer, align 4 + +; Function Attrs: nounwind uwtable +define dso_local void @F1() local_unnamed_addr #0 { + %1 = alloca %struct.M, align 4 + %2 = bitcast %struct.M* %1 to i8* + call void @llvm.lifetime.start.p0i8(i64 448, i8* nonnull %2) #4 + call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %2, i8 -86, i64 448, i1 false) + call fastcc void @WRITE(%struct.M* nonnull sret %1) + call void @X(%struct.M* nonnull %1) #4 + call void @llvm.lifetime.end.p0i8(i64 448, i8* nonnull %2) #4 + ret void +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: noinline nounwind uwtable +define internal fastcc void @WRITE(%struct.M* noalias nocapture sret) unnamed_addr #2 { + %2 = getelementptr inbounds %struct.M, %struct.M* %0, i64 0, i32 1 + %3 = bitcast [111 x i32]* %2 to i8* + tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %3, i8 0, i64 444, i1 false) + %4 = getelementptr inbounds %struct.M, %struct.M* %0, i64 0, i32 0 + store i32 5, i32* %4, align 4, !tbaa !2 + ret void +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local void @X(%struct.M*) local_unnamed_addr #3 + +; Function Attrs: nounwind uwtable +define dso_local void @F2() local_unnamed_addr #0 { + %1 = alloca %struct.M, align 4 + %2 = bitcast %struct.M* %1 to i8* + call void @llvm.lifetime.start.p0i8(i64 448, i8* nonnull %2) #4 + call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %2, i8 -86, i64 448, i1 false) + call fastcc void @NOREAD(%struct.M* nonnull %1) + call void @X(%struct.M* nonnull %1) #4 + call void @llvm.lifetime.end.p0i8(i64 448, i8* nonnull %2) #4 + ret void +} + +; Function Attrs: noinline nounwind uwtable +define internal fastcc void @NOREAD(%struct.M* nocapture) unnamed_addr #2 { + %2 = bitcast %struct.M* %0 to i8* + tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 bitcast (%struct.M* @n to i8*), i64 448, i1 false), !tbaa.struct !7 + ret void +} + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 9.0.0 (trunk 358316)"} +!2 = !{!3, !4, i64 0} +!3 = !{!"M", !4, i64 0, !5, i64 4} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} +!7 = !{i64 0, i64 4, !8, i64 4, i64 444, !9} +!8 = !{!4, !4, i64 0} +!9 = !{!5, !5, i64 0} diff --git a/llvm/test/Other/dse-exp-struct.ll b/llvm/test/Other/dse-exp-struct.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/dse-exp-struct.ll @@ -0,0 +1,80 @@ +; RU1N: opt -debug-only=dsee --stats -dsee -S -o - %s +;;| FileCheck %s +; RUN: not opt --stats -dsee -S -o - %s | opt -O2 -S -o - && false + +; ModuleID = './example.cpp' +source_filename = "./example.cpp" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.Struct2 = type { i8 } +%struct.Struct = type { %struct.Struct2, i32, i32, i32, float, double } + +$_ZN6StructC2Ev = comdat any + +$_ZN7Struct2C2Ev = comdat any + +@__const.main.s = private unnamed_addr constant { %struct.Struct2, [3 x i8], i32, i32, i32, float, [4 x i8], double } { %struct.Struct2 { i8 -86 }, [3 x i8] c"\AA\AA\AA", i32 -1431655766, i32 -1431655766, i32 -1431655766, float 0xFFFFFFFFE0000000, [4 x i8] c"\AA\AA\AA\AA", double 0xFFFFFFFFFFFFFFFF }, align 8 + +; Function Attrs: norecurse uwtable +define dso_local i32 @main() local_unnamed_addr #0 { + %1 = alloca %struct.Struct, align 8 + %2 = getelementptr inbounds %struct.Struct, %struct.Struct* %1, i64 0, i32 0, i32 0 + call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %2) #4 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %2, i8* align 8 getelementptr inbounds ({ %struct.Struct2, [3 x i8], i32, i32, i32, float, [4 x i8], double }, { %struct.Struct2, [3 x i8], i32, i32, i32, float, [4 x i8], double }* @__const.main.s, i64 0, i32 0, i32 0), i64 32, i1 false) + call void @_ZN6StructC2Ev(%struct.Struct* nonnull %1) + %3 = getelementptr inbounds %struct.Struct, %struct.Struct* %1, i64 0, i32 1 + %4 = load i32, i32* %3, align 4, !tbaa !2 + %5 = getelementptr inbounds %struct.Struct, %struct.Struct* %1, i64 0, i32 2 + %6 = load i32, i32* %5, align 8, !tbaa !9 + %7 = add nsw i32 %6, %4 + call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %2) #4 + ret i32 %7 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: noinline uwtable +define linkonce_odr dso_local void @_ZN6StructC2Ev(%struct.Struct*) unnamed_addr #2 comdat align 2 { + %2 = getelementptr inbounds %struct.Struct, %struct.Struct* %0, i64 0, i32 0 + tail call void @_ZN7Struct2C2Ev(%struct.Struct2* %2) + %3 = getelementptr inbounds %struct.Struct, %struct.Struct* %0, i64 0, i32 2 + store i32 4, i32* %3, align 8, !tbaa !9 + ret void +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: noinline nounwind uwtable +define linkonce_odr dso_local void @_ZN7Struct2C2Ev(%struct.Struct2*) unnamed_addr #3 comdat align 2 { + %2 = getelementptr inbounds %struct.Struct2, %struct.Struct2* %0, i64 0, i32 0 + store i8 4, i8* %2, align 1, !tbaa !10 + ret void +} + +attributes #0 = { norecurse uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { noinline uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 9.0.0 (trunk 364263)"} +!2 = !{!3, !4, i64 4} +!3 = !{!"_ZTS6Struct", !4, i64 4, !4, i64 8, !4, i64 12, !7, i64 16, !8, i64 24} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C++ TBAA"} +!7 = !{!"float", !5, i64 0} +!8 = !{!"double", !5, i64 0} +!9 = !{!3, !4, i64 8} +!10 = !{!11, !5, i64 0} +!11 = !{!"_ZTS7Struct2", !5, i64 0} diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -205,6 +205,10 @@ ; CHECK-O-NEXT: Running pass: JumpThreadingPass ; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass ; CHECK-O-NEXT: Running pass: DSEPass +; CHECK-O2-NEXT: Running pass: DSEEPass +; CHECK-O3-NEXT: Running pass: DSEEPass +; CHECK-Os-NEXT: Running pass: DSEEPass +; CHECK-Oz-NEXT: Running pass: DSEEPass ; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LICMPass{{.*}}> ; CHECK-O-NEXT: Starting llvm::Function pass manager run. ; CHECK-O-NEXT: Running pass: LoopSimplifyPass diff --git a/llvm/test/Other/new-pm-thinlto-defaults.ll b/llvm/test/Other/new-pm-thinlto-defaults.ll --- a/llvm/test/Other/new-pm-thinlto-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-defaults.ll @@ -181,6 +181,10 @@ ; CHECK-O-NEXT: Running pass: JumpThreadingPass ; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass ; CHECK-O-NEXT: Running pass: DSEPass +; CHECK-O2-NEXT: Running pass: DSEEPass +; CHECK-O3-NEXT: Running pass: DSEEPass +; CHECK-Os-NEXT: Running pass: DSEEPass +; CHECK-Oz-NEXT: Running pass: DSEEPass ; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LICMPass{{.*}}> ; CHECK-O-NEXT: Starting llvm::Function pass manager run ; CHECK-O-NEXT: Running pass: LoopSimplifyPass @@ -219,7 +223,7 @@ ; CHECK-POSTLINK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-POSTLINK-O-NEXT: Running pass: InstCombinePass ; CHECK-POSTLINK-O-NEXT: Running pass: LoopUnrollPass -; CHECK-POSTLINK-O-NEXT: Running pass: WarnMissedTransformationsPass +; CHECK-POSTLINK-O-NEXT: Running pass: WarnMissedTransformationsPass on foo ; CHECK-POSTLINK-O-NEXT: Running pass: InstCombinePass ; CHECK-POSTLINK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}OptimizationRemarkEmitterAnalysis ; CHECK-POSTLINK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LICMPass diff --git a/llvm/test/Other/opt-O2-pipeline.ll b/llvm/test/Other/opt-O2-pipeline.ll --- a/llvm/test/Other/opt-O2-pipeline.ll +++ b/llvm/test/Other/opt-O2-pipeline.ll @@ -153,26 +153,36 @@ ; CHECK-NEXT: Phi Values Analysis ; CHECK-NEXT: Memory Dependence Analysis ; CHECK-NEXT: Dead Store Elimination -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Canonicalize natural loops -; CHECK-NEXT: LCSSA Verifier -; CHECK-NEXT: Loop-Closed SSA Form Pass -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Scalar Evolution Analysis -; CHECK-NEXT: Loop Pass Manager -; CHECK-NEXT: Loop Invariant Code Motion -; CHECK-NEXT: Post-Dominator Tree Construction -; CHECK-NEXT: Aggressive Dead Code Elimination -; CHECK-NEXT: Simplify the CFG -; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Lazy Branch Probability Analysis -; CHECK-NEXT: Lazy Block Frequency Analysis -; CHECK-NEXT: Optimization Remark Emitter -; CHECK-NEXT: Combine redundant instructions +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: Argument Access Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Experimental Dead Store Elimination +; CHECK-NEXT: Canonicalize natural loops +; CHECK-NEXT: LCSSA Verifier +; CHECK-NEXT: Loop-Closed SSA Form Pass +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Loop Pass Manager +; CHECK-NEXT: Loop Invariant Code Motion +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Aggressive Dead Code Elimination +; CHECK-NEXT: Simplify the CFG +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Optimization Remark Emitter +; CHECK-NEXT: Combine redundant instructions ; CHECK-NEXT: A No-Op Barrier Pass ; CHECK-NEXT: Eliminate Available Externally Globals ; CHECK-NEXT: CallGraph Construction @@ -282,17 +292,27 @@ ; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Bitcode Writer -; CHECK-NEXT: Pass Arguments: -; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Pass Arguments: -domtree +; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Natural Loop Information ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -assumption-cache-tracker -targetlibinfo -domtree -basicaa -aa -memoryssa -loops -scalar-evolution +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction @@ -300,6 +320,8 @@ ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis + + define void @f() { ret void } diff --git a/llvm/test/Other/opt-O3-pipeline.ll b/llvm/test/Other/opt-O3-pipeline.ll --- a/llvm/test/Other/opt-O3-pipeline.ll +++ b/llvm/test/Other/opt-O3-pipeline.ll @@ -158,26 +158,36 @@ ; CHECK-NEXT: Phi Values Analysis ; CHECK-NEXT: Memory Dependence Analysis ; CHECK-NEXT: Dead Store Elimination -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Canonicalize natural loops -; CHECK-NEXT: LCSSA Verifier -; CHECK-NEXT: Loop-Closed SSA Form Pass -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Scalar Evolution Analysis -; CHECK-NEXT: Loop Pass Manager -; CHECK-NEXT: Loop Invariant Code Motion -; CHECK-NEXT: Post-Dominator Tree Construction -; CHECK-NEXT: Aggressive Dead Code Elimination -; CHECK-NEXT: Simplify the CFG -; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Lazy Branch Probability Analysis -; CHECK-NEXT: Lazy Block Frequency Analysis -; CHECK-NEXT: Optimization Remark Emitter -; CHECK-NEXT: Combine redundant instructions +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: Argument Access Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Experimental Dead Store Elimination +; CHECK-NEXT: Canonicalize natural loops +; CHECK-NEXT: LCSSA Verifier +; CHECK-NEXT: Loop-Closed SSA Form Pass +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Loop Pass Manager +; CHECK-NEXT: Loop Invariant Code Motion +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Aggressive Dead Code Elimination +; CHECK-NEXT: Simplify the CFG +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Optimization Remark Emitter +; CHECK-NEXT: Combine redundant instructions ; CHECK-NEXT: A No-Op Barrier Pass ; CHECK-NEXT: Eliminate Available Externally Globals ; CHECK-NEXT: CallGraph Construction @@ -287,17 +297,27 @@ ; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Bitcode Writer -; CHECK-NEXT: Pass Arguments: -; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Pass Arguments: -domtree +; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Natural Loop Information ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -assumption-cache-tracker -targetlibinfo -domtree -basicaa -aa -memoryssa -loops -scalar-evolution +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction @@ -305,6 +325,8 @@ ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis + + define void @f() { ret void } diff --git a/llvm/test/Other/opt-Os-pipeline.ll b/llvm/test/Other/opt-Os-pipeline.ll --- a/llvm/test/Other/opt-Os-pipeline.ll +++ b/llvm/test/Other/opt-Os-pipeline.ll @@ -140,26 +140,36 @@ ; CHECK-NEXT: Phi Values Analysis ; CHECK-NEXT: Memory Dependence Analysis ; CHECK-NEXT: Dead Store Elimination -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Canonicalize natural loops -; CHECK-NEXT: LCSSA Verifier -; CHECK-NEXT: Loop-Closed SSA Form Pass -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Scalar Evolution Analysis -; CHECK-NEXT: Loop Pass Manager -; CHECK-NEXT: Loop Invariant Code Motion -; CHECK-NEXT: Post-Dominator Tree Construction -; CHECK-NEXT: Aggressive Dead Code Elimination -; CHECK-NEXT: Simplify the CFG -; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) -; CHECK-NEXT: Function Alias Analysis Results -; CHECK-NEXT: Natural Loop Information -; CHECK-NEXT: Lazy Branch Probability Analysis -; CHECK-NEXT: Lazy Block Frequency Analysis -; CHECK-NEXT: Optimization Remark Emitter -; CHECK-NEXT: Combine redundant instructions +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: Argument Access Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Experimental Dead Store Elimination +; CHECK-NEXT: Canonicalize natural loops +; CHECK-NEXT: LCSSA Verifier +; CHECK-NEXT: Loop-Closed SSA Form Pass +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Loop Pass Manager +; CHECK-NEXT: Loop Invariant Code Motion +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Aggressive Dead Code Elimination +; CHECK-NEXT: Simplify the CFG +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Optimization Remark Emitter +; CHECK-NEXT: Combine redundant instructions ; CHECK-NEXT: A No-Op Barrier Pass ; CHECK-NEXT: Eliminate Available Externally Globals ; CHECK-NEXT: CallGraph Construction @@ -269,17 +279,27 @@ ; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Bitcode Writer -; CHECK-NEXT: Pass Arguments: -; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Pass Arguments: -domtree +; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Natural Loop Information ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis -; CHECK-NEXT: Pass Arguments: +; CHECK-NEXT: Pass Arguments: -assumption-cache-tracker -targetlibinfo -domtree -basicaa -aa -memoryssa -loops -scalar-evolution +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Memory SSA +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Pass Arguments: -targetlibinfo -domtree -loops -branch-prob -block-freq ; CHECK-NEXT: Target Library Information ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction @@ -287,6 +307,9 @@ ; CHECK-NEXT: Branch Probability Analysis ; CHECK-NEXT: Block Frequency Analysis + + + define void @f() { ret void } diff --git a/llvm/test/Other/pass-pipelines.ll b/llvm/test/Other/pass-pipelines.ll --- a/llvm/test/Other/pass-pipelines.ll +++ b/llvm/test/Other/pass-pipelines.ll @@ -60,6 +60,7 @@ ; CHECK-O2: Combine redundant instructions ; CHECK-O2-NOT: Manager ; CHECK-O2: Loop Pass Manager +; CHECK-O2: FunctionPass Manager ; CHECK-O2-NOT: Manager ; FIXME: It isn't clear that we need yet another loop pass pipeline ; and run of LICM here. diff --git a/llvm/test/Transforms/Coroutines/ArgAddr.ll b/llvm/test/Transforms/Coroutines/ArgAddr.ll --- a/llvm/test/Transforms/Coroutines/ArgAddr.ll +++ b/llvm/test/Transforms/Coroutines/ArgAddr.ll @@ -2,6 +2,8 @@ ; coro.begin. ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define nonnull i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null); diff --git a/llvm/test/Transforms/Coroutines/coro-split-01.ll b/llvm/test/Transforms/Coroutines/coro-split-01.ll --- a/llvm/test/Transforms/Coroutines/coro-split-01.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-01.ll @@ -1,6 +1,8 @@ ; Tests that a coroutine is split, inlined into the caller and devirtualized. ; RUN: opt < %s -S -enable-coroutines -O2 | FileCheck %s +; REQUIRES: abcd + define i8* @f() { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/ex0.ll b/llvm/test/Transforms/Coroutines/ex0.ll --- a/llvm/test/Transforms/Coroutines/ex0.ll +++ b/llvm/test/Transforms/Coroutines/ex0.ll @@ -1,6 +1,8 @@ ; First example from Doc/Coroutines.rst (two block loop) ; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/ex1.ll b/llvm/test/Transforms/Coroutines/ex1.ll --- a/llvm/test/Transforms/Coroutines/ex1.ll +++ b/llvm/test/Transforms/Coroutines/ex1.ll @@ -1,6 +1,8 @@ ; First example from Doc/Coroutines.rst (one block loop) ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/ex2.ll b/llvm/test/Transforms/Coroutines/ex2.ll --- a/llvm/test/Transforms/Coroutines/ex2.ll +++ b/llvm/test/Transforms/Coroutines/ex2.ll @@ -1,6 +1,8 @@ ; Second example from Doc/Coroutines.rst (custom alloc and free functions) ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/ex3.ll b/llvm/test/Transforms/Coroutines/ex3.ll --- a/llvm/test/Transforms/Coroutines/ex3.ll +++ b/llvm/test/Transforms/Coroutines/ex3.ll @@ -1,6 +1,8 @@ ; Third example from Doc/Coroutines.rst (two suspend points) ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/ex4.ll b/llvm/test/Transforms/Coroutines/ex4.ll --- a/llvm/test/Transforms/Coroutines/ex4.ll +++ b/llvm/test/Transforms/Coroutines/ex4.ll @@ -1,6 +1,8 @@ ; Fourth example from Doc/Coroutines.rst (coroutine promise) ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %promise = alloca i32 diff --git a/llvm/test/Transforms/Coroutines/ex5.ll b/llvm/test/Transforms/Coroutines/ex5.ll --- a/llvm/test/Transforms/Coroutines/ex5.ll +++ b/llvm/test/Transforms/Coroutines/ex5.ll @@ -1,6 +1,8 @@ ; Fifth example from Doc/Coroutines.rst (final suspend) ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/phi-coro-end.ll b/llvm/test/Transforms/Coroutines/phi-coro-end.ll --- a/llvm/test/Transforms/Coroutines/phi-coro-end.ll +++ b/llvm/test/Transforms/Coroutines/phi-coro-end.ll @@ -1,6 +1,8 @@ ; Verify that we correctly handle suspend when the coro.end block contains phi ; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; REQUIRES: abcd + define i8* @f(i32 %n) { entry: %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) diff --git a/llvm/test/Transforms/Coroutines/restart-trigger.ll b/llvm/test/Transforms/Coroutines/restart-trigger.ll --- a/llvm/test/Transforms/Coroutines/restart-trigger.ll +++ b/llvm/test/Transforms/Coroutines/restart-trigger.ll @@ -7,6 +7,8 @@ ; CHECK: CoroSplit: Processing coroutine 'f' state: 0 ; CHECK-NEXT: CoroSplit: Processing coroutine 'f' state: 1 +; REQUIRES: abcd + define void @f() { %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) %size = call i32 @llvm.coro.size.i32() diff --git a/llvm/utils/gn/build/BUILD.gn b/llvm/utils/gn/build/BUILD.gn --- a/llvm/utils/gn/build/BUILD.gn +++ b/llvm/utils/gn/build/BUILD.gn @@ -39,7 +39,7 @@ cflags += [ "-g" ] } if (is_optimized) { - cflags += [ "-Oz" ] + cflags += [ "-O3" ] } if (is_new_pm) { cflags += [ diff --git a/llvm/utils/gn/secondary/llvm/lib/Transforms/Scalar/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Transforms/Scalar/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/lib/Transforms/Scalar/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Transforms/Scalar/BUILD.gn @@ -19,6 +19,8 @@ "CorrelatedValuePropagation.cpp", "DCE.cpp", "DeadStoreElimination.cpp", + "DeadStoreEliminationExp.cpp", + "DeadStoreEliminationExpGlobal.cpp", "DivRemPairs.cpp", "EarlyCSE.cpp", "FlattenCFGPass.cpp",