Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -141,6 +141,11 @@ // Provide fast operand accessors. DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); + /// Returns the number of IR instructions in this function. + /// This is equivalent to the sum of the sizes of each basic block contained + /// within this function. + int getInstructionCount(); + /// 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,12 @@ InheritedAnalysis[Index++] = (*I)->getAvailableAnalysis(); } + /// Emit a remark signifying that the number of IR instructions in the module + /// changed. + /// This updates the InstrCount parameter if the instruction count in the + /// module changed. + void emitIRSizeChangedRemark(Pass *P, Module &M, int &InstrCount); + 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,11 @@ /// @returns the module identifier as a string const std::string &getModuleIdentifier() const { return ModuleID; } + /// Returns the number of IR instructions in the module. + /// This is equivalent to the sum of the IR instruction counts of each + /// function contained in the module. + int getInstructionCount(); + /// 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,6 +120,8 @@ bool &DevirtualizedCall) { bool Changed = false; PMDataManager *PM = P->getAsPMDataManager(); + Module &M = CG.getModule(); + int InstrCount = M.getInstructionCount(); if (!PM) { CallGraphSCCPass *CGSP = (CallGraphSCCPass*)P; @@ -131,6 +133,11 @@ { TimeRegion PassTimer(getPassTimer(CGSP)); Changed = CGSP->runOnSCC(CurSCC); + + // If the pass modified the module, it may have modified the instruction + // count of the module. Try emitting a remark. + if (Changed) + emitIRSizeChangedRemark(P, M, InstrCount); } // After the CGSCCPass is done, when assertions are enabled, use @@ -154,6 +161,8 @@ { TimeRegion PassTimer(getPassTimer(FPP)); Changed |= FPP->runOnFunction(*F); + if (Changed) + emitIRSizeChangedRemark(P, M, InstrCount); } 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::getInstructionCount() { + 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,42 @@ return PassDebugging >= Executions; } +void PMDataManager::emitIRSizeChangedRemark(Pass *P, Module &M, + int &InstrCount) { + Function &F = *M.begin(); + // If the user doesn't want this remark, don't do any work. + if (!F.getContext().getDiagHandlerPtr()->isAnalysisRemarkEnabled("size-info")) + return; + + int FinalCount = M.getInstructionCount(); + // If there was no change, don't emit a remark. + if (InstrCount == 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; + BasicBlock &BB = *F.begin(); + OptimizationRemarkAnalysis R("size-info", "IRSizeChange", + DiagnosticLocation(), &BB); + // FIXME: Move ore namespace to DiagnosticInfo so that we can use it. This + // would let us use NV instead of DiagnosticInfoOptimizationBase::Argument. + R << DiagnosticInfoOptimizationBase::Argument("Pass", P->getPassName()) + << ": IR instruction count changed from " + << DiagnosticInfoOptimizationBase::Argument("IRInstrsBefore", InstrCount) + << " to " + << DiagnosticInfoOptimizationBase::Argument("IRInstrsAfter", FinalCount) + << "; Delta: " + << DiagnosticInfoOptimizationBase::Argument("DeltaInstrCount", + FinalCount - InstrCount); + F.getContext().diagnose(R); // Not using ORE for layering reasons. + + InstrCount = FinalCount; +} void PassManagerPrettyStackEntry::print(raw_ostream &OS) const { if (!V && !M) @@ -1285,6 +1320,9 @@ bool Changed = doInitialization(F); + Module &M = *F.getParent(); + int InstrCount = M.getInstructionCount(); + for (BasicBlock &BB : F) for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) { BasicBlockPass *BP = getContainedPass(Index); @@ -1301,6 +1339,8 @@ TimeRegion PassTimer(getPassTimer(BP)); LocalChanged |= BP->runOnBasicBlock(BB); + if (LocalChanged) + emitIRSizeChangedRemark(BP, M, InstrCount); } Changed |= LocalChanged; @@ -1500,6 +1540,8 @@ return false; bool Changed = false; + Module &M = *(F.getParent()); + int InstrCount = M.getInstructionCount(); // Collect inherited analysis from Module level pass manager. populateInheritedAnalysis(TPM->activeStack); @@ -1518,6 +1560,8 @@ TimeRegion PassTimer(getPassTimer(FP)); LocalChanged |= FP->runOnFunction(F); + if (LocalChanged) + emitIRSizeChangedRemark(FP, M, InstrCount); } Changed |= LocalChanged; @@ -1594,7 +1638,10 @@ PassManagerPrettyStackEntry X(MP, M); TimeRegion PassTimer(getPassTimer(MP)); + int InstrCount = M.getInstructionCount(); LocalChanged |= MP->runOnModule(M); + if (LocalChanged) + emitIRSizeChangedRemark(MP, M, InstrCount); } 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::getInstructionCount() { + int IRCount = 0; + for (Function &F : FunctionList) + IRCount += F.getInstructionCount(); + 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 +}