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 getIRCount(); + /// 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 getIRCount(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 getIRCount(); + /// 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,12 @@ { TimeRegion PassTimer(getPassTimer(CGSP)); + + // Remember the number of instructions in the module before the pass. + int OriginalCount = M.getIRCount(); Changed = CGSP->runOnSCC(CurSCC); + // If the pass modified the size of the module, emit a remark. + emitIRSizeChangedRemark(P, M, OriginalCount, M.getIRCount()); } // After the CGSCCPass is done, when assertions are enabled, use @@ -147,13 +152,23 @@ "Invalid CGPassManager member"); FPPassManager *FPP = (FPPassManager*)P; + // Keep track of the original module size. + int ModuleCount = M.getIRCount(); + // 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()); { + int OrigFnCount = F->getIRCount(); TimeRegion PassTimer(getPassTimer(FPP)); Changed |= FPP->runOnFunction(*F); + if (Changed) { + // If there was a size change, then emit a remark. + int InstrDiff = F->getIRCount() - 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 @@ -194,6 +194,13 @@ return getType()->getContext(); } +int Function::getIRCount() { + 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" @@ -134,8 +135,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) @@ -1284,6 +1310,8 @@ return false; bool Changed = doInitialization(F); + Module &M = *F.getParent(); + int ModuleCount = M.getIRCount(); for (BasicBlock &BB : F) for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) { @@ -1300,7 +1328,17 @@ PassManagerPrettyStackEntry X(BP, BB); TimeRegion PassTimer(getPassTimer(BP)); + int OrigBBCount = BB.size(); LocalChanged |= BP->runOnBasicBlock(BB); + + // Run BP on BB and check if anything changed. + if (LocalChanged) { + // It changed something. Keep track of the local change, and emit a + // remark if it changed the size of the module. + int InstrDiff = BB.size() - OrigBBCount; + emitIRSizeChangedRemark(BP, M, ModuleCount, ModuleCount + InstrDiff); + ModuleCount += InstrDiff; + } } Changed |= LocalChanged; @@ -1500,6 +1538,8 @@ return false; bool Changed = false; + Module &M = *(F.getParent()); + int ModuleCount = M.getIRCount(); // Collect inherited analysis from Module level pass manager. populateInheritedAnalysis(TPM->activeStack); @@ -1517,7 +1557,15 @@ PassManagerPrettyStackEntry X(FP, F); TimeRegion PassTimer(getPassTimer(FP)); + int OrigFnCount = F.getIRCount(); LocalChanged |= FP->runOnFunction(F); + // Did FP modify F? + if (LocalChanged) { + // Emit a remark explaining why the size changed if it did. + int InstrDiff = F.getIRCount() - OrigFnCount; + emitIRSizeChangedRemark(FP, M, ModuleCount, ModuleCount + InstrDiff); + ModuleCount += InstrDiff; + } } Changed |= LocalChanged; @@ -1594,7 +1642,11 @@ PassManagerPrettyStackEntry X(MP, M); TimeRegion PassTimer(getPassTimer(MP)); + int OriginalCount = M.getIRCount(); LocalChanged |= MP->runOnModule(M); + + // If MP changed the size of the module, then emit a remark. + emitIRSizeChangedRemark(MP, M, OriginalCount, M.getIRCount()); } Changed |= LocalChanged; Index: lib/IR/Module.cpp =================================================================== --- lib/IR/Module.cpp +++ lib/IR/Module.cpp @@ -464,6 +464,13 @@ return cast(Val->getValue())->getZExtValue(); } +int Module::getIRCount() { + int IRCount = 0; + for (Function &F : FunctionList) + IRCount += F.getIRCount(); + return IRCount; +} + Comdat *Module::getOrInsertComdat(StringRef Name) { auto &Entry = *ComdatSymTab.insert(std::make_pair(Name, Comdat())).first; Entry.second.Name = &Entry; 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 +}