Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -141,6 +141,9 @@ // Provide fast operand accessors. DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); + /// Returns the number of IR instructions contained in the \p Function. + int getFunctionInstrCount(); + /// Returns the FunctionType for me. FunctionType *getFunctionType() const { return cast(getValueType()); Index: include/llvm/IR/LegacyPassManagers.h =================================================================== --- include/llvm/IR/LegacyPassManagers.h +++ include/llvm/IR/LegacyPassManagers.h @@ -403,6 +403,14 @@ InheritedAnalysis[Index++] = (*I)->getAvailableAnalysis(); } + /// Return the total number of IR instructions in \p M. + int getModuleInstrCount(Module &M); + + /// \brief Emit a remark signifying that the number of IR instructions in the + /// module changed. + void emitIRSizeChangedRemark(Pass *P, Module &M, const int &OriginalCount, + const int &FinalCount); + protected: // Top level manager. PMTopLevelManager *TPM; Index: include/llvm/IR/Module.h =================================================================== --- include/llvm/IR/Module.h +++ include/llvm/IR/Module.h @@ -207,6 +207,9 @@ /// @returns the module identifier as a string const std::string &getModuleIdentifier() const { return ModuleID; } + /// Return the number of IR instructions contained in the module. + int getModuleInstrCount(); + /// Get the module's original source file name. When compiling from /// bitcode, this is taken from a bitcode record where it was recorded. /// For other compiles it is the same as the ModuleID, which would Index: lib/Analysis/CallGraphSCCPass.cpp =================================================================== --- lib/Analysis/CallGraphSCCPass.cpp +++ lib/Analysis/CallGraphSCCPass.cpp @@ -120,7 +120,7 @@ bool &DevirtualizedCall) { bool Changed = false; PMDataManager *PM = P->getAsPMDataManager(); - + Module &M = CG.getModule(); if (!PM) { CallGraphSCCPass *CGSP = (CallGraphSCCPass*)P; if (!CallGraphUpToDate) { @@ -130,7 +130,13 @@ { TimeRegion PassTimer(getPassTimer(CGSP)); + + // Remember the number of instructions in the module before the pass. + int OriginalCount = M.getModuleInstrCount(); Changed = CGSP->runOnSCC(CurSCC); + // If the pass modified the size of the module, emit a remark. + emitIRSizeChangedRemark(P, M, OriginalCount, M.getModuleInstrCount()); + } // After the CGSCCPass is done, when assertions are enabled, use @@ -147,13 +153,26 @@ "Invalid CGPassManager member"); FPPassManager *FPP = (FPPassManager*)P; + // Keep track of the original module size. + int ModuleCount = M.getModuleInstrCount(); + // Run pass P on all functions in the current SCC. for (CallGraphNode *CGN : CurSCC) { if (Function *F = CGN->getFunction()) { dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName()); { TimeRegion PassTimer(getPassTimer(FPP)); - Changed |= FPP->runOnFunction(*F); + int OrigFnCount = F->getFunctionInstrCount(); + + // Run FPP on F and check if it changed anything. + if (FPP->runOnFunction(*F)) { + // It changed something. Keep track of it and also emit a remark if + // it changed the size of the module. + Changed = true; + int InstrDiff = F->getFunctionInstrCount() - OrigFnCount; + emitIRSizeChangedRemark(P, M, ModuleCount, ModuleCount + InstrDiff); + ModuleCount += InstrDiff; + } } F->getContext().yield(); } Index: lib/IR/Function.cpp =================================================================== --- lib/IR/Function.cpp +++ lib/IR/Function.cpp @@ -195,6 +195,13 @@ return getType()->getContext(); } +int Function::getFunctionInstrCount() { + int IRCount = 0; + for (BasicBlock &BB : BasicBlocks) + IRCount += BB.size(); + return IRCount; +} + void Function::removeFromParent() { getParent()->getFunctionList().remove(getIterator()); } Index: lib/IR/LegacyPassManager.cpp =================================================================== --- lib/IR/LegacyPassManager.cpp +++ lib/IR/LegacyPassManager.cpp @@ -13,6 +13,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/ADT/Statistic.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManagers.h" @@ -128,8 +129,33 @@ return PassDebugging >= Executions; } - - +void PMDataManager::emitIRSizeChangedRemark(Pass *P, Module &M, + const int &OriginalCount, + const int &FinalCount) { + // If there was no change, don't emit a remark. + if (OriginalCount == FinalCount) + return; + + // If it's a pass manager, don't emit a remark. (This hinges on the assumption + // that the only passes that return non-null with getAsPMDataManager are + // pass managers.) + if (P->getAsPMDataManager()) + return; + + Function &F = *M.begin(); + BasicBlock &BB = *F.begin(); + OptimizationRemarkAnalysis R("size-info", "IRSizeChange", + DiagnosticLocation(), &BB); + R << DiagnosticInfoOptimizationBase::Argument("Pass", P->getPassName()) + << ": IR instruction count changed from " + << DiagnosticInfoOptimizationBase::Argument("IRInstrsBefore", OriginalCount) + << " to " + << DiagnosticInfoOptimizationBase::Argument("IRInstrsAfter", FinalCount) + << "; Delta: " + << DiagnosticInfoOptimizationBase::Argument("DeltaInstrCount", + FinalCount - OriginalCount); + F.getContext().diagnose(R); +} void PassManagerPrettyStackEntry::print(raw_ostream &OS) const { if (!V && !M) @@ -173,7 +199,7 @@ public: static char ID; - explicit BBPassManager() + explicit BBPassManager() : PMDataManager(), FunctionPass(ID) {} /// Execute all of the passes scheduled for execution. Keep track of @@ -322,7 +348,7 @@ using llvm::Pass::doInitialization; using llvm::Pass::doFinalization; - + /// Pass Manager itself does not invalidate any analysis info. void getAnalysisUsage(AnalysisUsage &Info) const override { Info.setPreservesAll(); @@ -350,7 +376,7 @@ ModulePass *MP = getContainedPass(Index); MP->dumpPassStructure(Offset + 1); std::map::const_iterator I = - OnTheFlyManagers.find(MP); + OnTheFlyManagers.find(MP); if (I != OnTheFlyManagers.end()) I->second->dumpPassStructure(Offset + 2); dumpLastUses(MP, Offset+1); @@ -366,7 +392,7 @@ return PMT_ModulePassManager; } - private: +private: /// Collection of on the fly FPPassManagers. These managers manage /// function passes that are required by module passes. std::map OnTheFlyManagers; @@ -394,8 +420,8 @@ PMTopLevelManager(new MPPassManager()) {} /// \copydoc PassManager::add() - void add(Pass *P) { - schedulePass(P); + void add(Pass *P) { + schedulePass(P); } /// createPrinterPass - Get a module printer pass. @@ -410,7 +436,7 @@ using llvm::Pass::doInitialization; using llvm::Pass::doFinalization; - + /// Pass Manager itself does not invalidate any analysis info. void getAnalysisUsage(AnalysisUsage &Info) const override { Info.setPreservesAll(); @@ -467,8 +493,8 @@ static void createTheTimeInfo(); // print - Prints out timing information and then resets the timers. - void print() { - TG.print(*CreateInfoOutputFile()); + void print() { + TG.print(*CreateInfoOutputFile()); } /// getPassTimer - Return the timer for the specified pass if it exists. @@ -554,7 +580,7 @@ void PMTopLevelManager::collectLastUses(SmallVectorImpl &LastUses, Pass *P) { DenseMap >::iterator DMI = - InversedLastUser.find(P); + InversedLastUser.find(P); if (DMI == InversedLastUser.end()) return; @@ -579,7 +605,7 @@ // of dependencies. AnalysisUsage AU; P->getAnalysisUsage(AU); - + AUFoldingSetNode* Node = nullptr; FoldingSetNodeID ID; AUFoldingSetNode::Profile(ID, AU); @@ -873,7 +899,7 @@ const AnalysisUsage::VectorType &PreservedSet = AnUsage->getPreservedSet(); for (DenseMap::iterator I = AvailableAnalysis.begin(), - E = AvailableAnalysis.end(); I != E; ) { + E = AvailableAnalysis.end(); I != E; ) { DenseMap::iterator Info = I++; if (Info->second->getAsImmutablePass() == nullptr && !is_contained(PreservedSet, Info->first)) { @@ -895,8 +921,8 @@ continue; for (DenseMap::iterator - I = InheritedAnalysis[Index]->begin(), - E = InheritedAnalysis[Index]->end(); I != E; ) { + I = InheritedAnalysis[Index]->begin(), + E = InheritedAnalysis[Index]->end(); I != E; ) { DenseMap::iterator Info = I++; if (Info->second->getAsImmutablePass() == nullptr && !is_contained(PreservedSet, Info->first)) { @@ -956,7 +982,7 @@ const std::vector &II = PInf->getInterfacesImplemented(); for (unsigned i = 0, e = II.size(); i != e; ++i) { DenseMap::iterator Pos = - AvailableAnalysis.find(II[i]->getTypeInfo()); + AvailableAnalysis.find(II[i]->getTypeInfo()); if (Pos != AvailableAnalysis.end() && Pos->second == P) AvailableAnalysis.erase(Pos); } @@ -1120,16 +1146,16 @@ for (Pass *P : PassVector) { if (PMDataManager *PMD = P->getAsPMDataManager()) PMD->dumpPassArguments(); - else - if (const PassInfo *PI = + else + if (const PassInfo *PI = TPM->findAnalysisPassInfo(P->getPassID())) - if (!PI->isAnalysisGroup()) - dbgs() << " -" << PI->getPassArgument(); + if (!PI->isAnalysisGroup()) + dbgs() << " -" << PI->getPassArgument(); } } void PMDataManager::dumpPassInfo(Pass *P, enum PassDebuggingString S1, - enum PassDebuggingString S2, + enum PassDebuggingString S2, StringRef Msg) { if (PassDebugging < Executions) return; @@ -1278,6 +1304,8 @@ return false; bool Changed = doInitialization(F); + Module &M = *F.getParent(); + int ModuleCount = M.getModuleInstrCount(); for (BasicBlock &BB : F) for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) { @@ -1294,7 +1322,17 @@ PassManagerPrettyStackEntry X(BP, BB); TimeRegion PassTimer(getPassTimer(BP)); - LocalChanged |= BP->runOnBasicBlock(BB); + int OrigBBCount = BB.size(); + + // Run BP on BB and check if anything changed. + if (BP->runOnBasicBlock(BB)) { + // It changed something. Keep track of the local change, and emit a + // remark if it changed the size of the module. + LocalChanged = true; + int InstrDiff = BB.size() - OrigBBCount; + emitIRSizeChangedRemark(BP, M, ModuleCount, ModuleCount + InstrDiff); + ModuleCount += InstrDiff; + } } Changed |= LocalChanged; @@ -1368,12 +1406,12 @@ FPM->setResolver(AR); } -FunctionPassManager::~FunctionPassManager() { - delete FPM; +FunctionPassManager::~FunctionPassManager() { + delete FPM; } -void FunctionPassManager::add(Pass *P) { - FPM->add(P); +void FunctionPassManager::add(Pass *P) { + FPM->add(P); } /// run - Execute all of the passes scheduled for execution. Keep @@ -1396,8 +1434,8 @@ /// doFinalization - Run all of the finalizers for the function passes. /// -bool FunctionPassManager::doFinalization() { - return FPM->doFinalization(*M); +bool FunctionPassManager::doFinalization() { + return FPM->doFinalization(*M); } //===----------------------------------------------------------------------===// @@ -1432,12 +1470,12 @@ /// cleanup - After running all passes, clean up pass manager cache. void FPPassManager::cleanup() { - for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) { + for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) { FunctionPass *FP = getContainedPass(Index); AnalysisResolver *AR = FP->getResolver(); assert(AR && "Analysis Resolver is not set"); AR->clearAnalysisImpls(); - } + } } void FunctionPassManagerImpl::releaseMemoryOnTheFly() { @@ -1494,6 +1532,8 @@ return false; bool Changed = false; + Module &M = *(F.getParent()); + int ModuleCount = M.getModuleInstrCount(); // Collect inherited analysis from Module level pass manager. populateInheritedAnalysis(TPM->activeStack); @@ -1511,7 +1551,17 @@ PassManagerPrettyStackEntry X(FP, F); TimeRegion PassTimer(getPassTimer(FP)); - LocalChanged |= FP->runOnFunction(F); + int OrigFnCount = F.getFunctionInstrCount(); + + // Did FP modify F? + if (FP->runOnFunction(F)) { + // It changed something. Keep track of the local change, and emit a + // remark if it changed the size of the module. + LocalChanged = true; + int InstrDiff = F.getFunctionInstrCount() - OrigFnCount; + emitIRSizeChangedRemark(FP, M, ModuleCount, ModuleCount + InstrDiff); + ModuleCount += InstrDiff; + } } Changed |= LocalChanged; @@ -1588,11 +1638,15 @@ PassManagerPrettyStackEntry X(MP, M); TimeRegion PassTimer(getPassTimer(MP)); + int OriginalCount = M.getModuleInstrCount(); LocalChanged |= MP->runOnModule(M); + + // If MP changed the size of the module, then emit a remark. + emitIRSizeChangedRemark(MP, M, OriginalCount, M.getModuleInstrCount()); } Changed |= LocalChanged; - if (LocalChanged) + if (LocalChanged) dumpPassInfo(MP, MODIFICATION_MSG, ON_MODULE_MSG, M.getModuleIdentifier()); dumpPreservedSet(MP); @@ -1646,7 +1700,7 @@ Pass *FoundPass = nullptr; if (RequiredPassPI && RequiredPassPI->isAnalysis()) { FoundPass = - ((PMTopLevelManager*)FPP)->findAnalysisPass(RequiredPass->getPassID()); + ((PMTopLevelManager*)FPP)->findAnalysisPass(RequiredPass->getPassID()); } if (!FoundPass) { FoundPass = RequiredPass; @@ -1902,4 +1956,4 @@ BBP->add(this); } -PassManagerBase::~PassManagerBase() {} +PassManagerBase::~PassManagerBase() {} \ No newline at end of file Index: lib/IR/Module.cpp =================================================================== --- lib/IR/Module.cpp +++ lib/IR/Module.cpp @@ -323,6 +323,13 @@ return getOrInsertNamedMetadata("llvm.module.flags"); } +int Module::getModuleInstrCount() { + int IRCount = 0; + for (Function &F : FunctionList) + IRCount += F.getFunctionInstrCount(); + return IRCount; +} + /// addModuleFlag - Add a module-level flag to the module-level flags /// metadata. It will create the module-level flags named metadata if it doesn't /// already exist. Index: test/Other/size-remarks.ll =================================================================== --- /dev/null +++ test/Other/size-remarks.ll @@ -0,0 +1,159 @@ +; Ensure that IR count remarks in the pass manager work for every type of +; pass that we handle. + +; First, make sure remarks work for CGSCC passes. +; RUN: opt < %s -inline -pass-remarks-analysis='size-info' -S 2>&1 \ +; RUN: | FileCheck %s -check-prefix=CGSCC +; CGSCC: remark: :0:0: Function Integration/Inlining: +; CGSCC-SAME: IR instruction count changed from 17 to 31; Delta: 14 +; RUN: opt < %s -inline -pass-remarks-analysis='size-info' -S \ +; RUN: -pass-remarks-output=%t.cgscc.yaml -o /dev/null +; RUN: cat %t.cgscc.yaml | FileCheck %s -check-prefix=CGSCC-YAML +; CGSCC-YAML: --- !Analysis +; CGSCC-YAML-NEXT: Pass: size-info +; CGSCC-YAML-NEXT: Name: IRSizeChange +; CGSCC-YAML-NEXT: Function: baz +; CGSCC-YAML-NEXT: Args: +; CGSCC-YAML-NEXT: - Pass: Function Integration/Inlining +; CGSCC-YAML-NEXT: - String: ': IR instruction count changed from ' +; CGSCC-YAML-NEXT: - IRInstrsBefore: '17' +; CGSCC-YAML-NEXT: - String: ' to ' +; CGSCC-YAML-NEXT: - IRInstrsAfter: '31' +; CGSCC-YAML-NEXT: - String: '; Delta: ' +; CGSCC-YAML-NEXT: - DeltaInstrCount: '14' + +; Next, make sure it works for function passes. +; RUN: opt < %s -instcombine -pass-remarks-analysis='size-info' -S 2>&1 \ +; RUN: | FileCheck %s -check-prefix=FUNC +; FUNC: remark: :0:0: Combine redundant instructions: +; FUNC-SAME: IR instruction count changed from 17 to 16; Delta: -1 +; FUNC: remark: :0:0: Combine redundant instructions: +; FUNC-SAME: IR instruction count changed from 16 to 10; Delta: -6 +; FUNC: remark: :0:0: Combine redundant instructions: +; FUNC-SAME: IR instruction count changed from 10 to 6; Delta: -4 +; RUN: opt < %s -instcombine -pass-remarks-analysis='size-info' -S \ +; RUN: -pass-remarks-output=%t.func.yaml -o /dev/null +; RUN: cat %t.func.yaml | FileCheck %s -check-prefix=FUNC-YAML + +; FUNC-YAML: --- !Analysis +; FUNC-YAML-NEXT: Pass: size-info +; FUNC-YAML-NEXT: Name: IRSizeChange +; FUNC-YAML-NEXT: Function: baz +; FUNC-YAML-NEXT: Args: +; FUNC-YAML-NEXT: - Pass: Combine redundant instructions +; FUNC-YAML-NEXT: - String: ': IR instruction count changed from ' +; FUNC-YAML-NEXT: - IRInstrsBefore: '17' +; FUNC-YAML-NEXT: - String: ' to ' +; FUNC-YAML-NEXT: - IRInstrsAfter: '16' +; FUNC-YAML-NEXT: - String: '; Delta: ' +; FUNC-YAML-NEXT: - DeltaInstrCount: '-1' + +; FUNC-YAML: --- !Analysis +; FUNC-YAML-NEXT: Pass: size-info +; FUNC-YAML-NEXT: Name: IRSizeChange +; FUNC-YAML-NEXT: Function: baz +; FUNC-YAML-NEXT: Args: +; FUNC-YAML-NEXT: - Pass: Combine redundant instructions +; FUNC-YAML-NEXT: - String: ': IR instruction count changed from ' +; FUNC-YAML-NEXT: - IRInstrsBefore: '16' +; FUNC-YAML-NEXT: - String: ' to ' +; FUNC-YAML-NEXT: - IRInstrsAfter: '10' +; FUNC-YAML-NEXT: - String: '; Delta: ' +; FUNC-YAML-NEXT: - DeltaInstrCount: '-6' + +; FUNC-YAML: --- !Analysis +; FUNC-YAML-NEXT: Pass: size-info +; FUNC-YAML-NEXT: Name: IRSizeChange +; FUNC-YAML-NEXT: Function: baz +; FUNC-YAML-NEXT: Args: +; FUNC-YAML-NEXT: - Pass: Combine redundant instructions +; FUNC-YAML-NEXT: - String: ': IR instruction count changed from ' +; FUNC-YAML-NEXT: - IRInstrsBefore: '10' +; FUNC-YAML-NEXT: - String: ' to ' +; FUNC-YAML-NEXT: - IRInstrsAfter: '6' +; FUNC-YAML-NEXT: - String: '; Delta: ' +; FUNC-YAML-NEXT: - DeltaInstrCount: '-4' + +; Make sure it works for module passes. +; RUN: opt < %s -gvn -pass-remarks-analysis='size-info' -S 2>&1 \ +; RUN: | FileCheck %s -check-prefix=MODULE +; MODULE: remark: :0:0: Global Value Numbering: +; MODULE-SAME: IR instruction count changed from 17 to 15; Delta: -2 +; MODULE: remark: :0:0: Global Value Numbering: +; MODULE-SAME: IR instruction count changed from 15 to 13; Delta: -2 +; RUN: opt < %s -gvn -pass-remarks-analysis='size-info' -S \ +; RUN: -pass-remarks-output=%t.module.yaml -o /dev/null +; RUN: cat %t.module.yaml | FileCheck %s -check-prefix=MODULE-YAML +; MODULE-YAML: --- !Analysis +; MODULE-YAML-NEXT: Pass: size-info +; MODULE-YAML-NEXT: Name: IRSizeChange +; MODULE-YAML-NEXT: Function: baz +; MODULE-YAML-NEXT: Args: +; MODULE-YAML-NEXT: - Pass: Global Value Numbering +; MODULE-YAML-NEXT: - String: ': IR instruction count changed from ' +; MODULE-YAML-NEXT: - IRInstrsBefore: '17' +; MODULE-YAML-NEXT: - String: ' to ' +; MODULE-YAML-NEXT: - IRInstrsAfter: '15' +; MODULE-YAML-NEXT: - String: '; Delta: ' +; MODULE-YAML-NEXT: - DeltaInstrCount: '-2' +; MODULE-YAML: --- !Analysis +; MODULE-YAML-NEXT: Pass: size-info +; MODULE-YAML-NEXT: Name: IRSizeChange +; MODULE-YAML-NEXT: Function: baz +; MODULE-YAML-NEXT: Args: +; MODULE-YAML-NEXT: - Pass: Global Value Numbering +; MODULE-YAML-NEXT: - String: ': IR instruction count changed from ' +; MODULE-YAML-NEXT: - IRInstrsBefore: '15' +; MODULE-YAML-NEXT: - String: ' to ' +; MODULE-YAML-NEXT: - IRInstrsAfter: '13' +; MODULE-YAML-NEXT: - String: '; Delta: ' +; MODULE-YAML-NEXT: - DeltaInstrCount: '-2' + +; Make sure it works for basic block passes. +; RUN: opt < %s -dce -pass-remarks-analysis='size-info' -S 2>&1 \ +; RUN: | FileCheck %s -check-prefix=BB +; BB: remark: :0:0: Dead Code Elimination: +; BB-SAME: IR instruction count changed from 17 to 16; Delta: -1 +; RUN: opt < %s -dce -pass-remarks-analysis='size-info' -S \ +; RUN: -pass-remarks-output=%t.bb.yaml -o /dev/null +; RUN: cat %t.bb.yaml | FileCheck %s -check-prefix=BB-YAML +; BB-YAML: --- !Analysis +; BB-YAML-NEXT: Pass: size-info +; BB-YAML-NEXT: Name: IRSizeChange +; BB-YAML-NEXT: Function: baz +; BB-YAML-NEXT: Args: +; BB-YAML-NEXT: - Pass: Dead Code Elimination +; BB-YAML-NEXT: - String: ': IR instruction count changed from ' +; BB-YAML-NEXT: - IRInstrsBefore: '17' +; BB-YAML-NEXT: - String: ' to ' +; BB-YAML-NEXT: - IRInstrsAfter: '16' +; BB-YAML-NEXT: - String: '; Delta: ' +; BB-YAML-NEXT: - DeltaInstrCount: '-1' + +define void @baz() #0 { + %x.addr = alloca i32, align 4 + ret void +} + +define i32 @foo(i32 %x, i32 %y) #0 { +entry: + %x.addr = alloca i32, align 4 + %y.addr = alloca i32, align 4 + store i32 %x, i32* %x.addr, align 4 + store i32 %y, i32* %y.addr, align 4 + %0 = load i32, i32* %x.addr, align 4 + %1 = load i32, i32* %y.addr, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add +} + +define i32 @bar(i32 %j) #0 { +entry: + %j.addr = alloca i32, align 4 + store i32 %j, i32* %j.addr, align 4 + %0 = load i32, i32* %j.addr, align 4 + %1 = load i32, i32* %j.addr, align 4 + %sub = sub nsw i32 %1, 2 + %call = call i32 @foo(i32 %0, i32 %sub) + ret i32 %call +} \ No newline at end of file