Index: include/llvm/IR/DiagnosticInfo.h =================================================================== --- include/llvm/IR/DiagnosticInfo.h +++ include/llvm/IR/DiagnosticInfo.h @@ -26,6 +26,7 @@ class Instruction; class Twine; class Value; +class DebugLoc; /// \brief Defines the different supported severity of a diagnostic. enum DiagnosticSeverity { @@ -44,6 +45,7 @@ DK_StackSize, DK_DebugMetadataVersion, DK_SampleProfile, + DK_OptimizationRemark, DK_FirstPluginKind }; @@ -235,6 +237,63 @@ const Twine &Msg; }; +/// Diagnostic information for optimization remarks. +class DiagnosticInfoOptimizationRemark : public DiagnosticInfo { +public: + /// \p PassName is the name of the pass emitting this diagnostic. If + /// this name matches the regular expression given in -Rpass=, then the + /// diagnostic will be emitted. \p Fn is the function where the diagnostic + /// is being emitted. \p DLoc is the location information to use in the + /// diagnostic. If line table information is available, the diagnostic + /// will include the source code location. \p Msg is the message to show. + /// Note that this class does not copy this message, so this reference + /// must be valid for the whole life time of the diagnostic. + DiagnosticInfoOptimizationRemark(const char *PassName, const Function &Fn, + const DebugLoc &DLoc, const Twine &Msg) + : DiagnosticInfo(DK_OptimizationRemark, DS_Remark), PassName(PassName), + Fn(Fn), DLoc(DLoc), Msg(Msg) {} + + /// \see DiagnosticInfo::print. + void print(DiagnosticPrinter &DP) const override; + + /// Hand rolled RTTI. + static bool classof(const DiagnosticInfo *DI) { + return DI->getKind() == DK_OptimizationRemark; + } + + /// Return true if location information is available for this diagnostic. + bool isLocationAvailable() const; + + /// Return a string with the location information for this diagnostic + /// in the format "file:line:col". If location information is not available, + /// it returns ":0:0". + const StringRef getLocationStr() const; + + /// Return location information for this diagnostic in three parts: + /// the source file name, line number and column. + void getLocation(StringRef *Filename, unsigned *Line, unsigned *Column) const; + + const char *getPassName() const { return PassName; } + const Function &getFunction() const { return Fn; } + const DebugLoc &getDebugLoc() const { return DLoc; } + const Twine &getMsg() const { return Msg; } + +private: + /// Name of the pass that triggers this report. If this matches the + /// regular expression given in -Rpass=regexp, then the remark will + /// be emitted. + const char *PassName; + + /// Function where this diagnostic is triggered. + const Function &Fn; + + /// Debug location where this diagnostic is triggered. + const DebugLoc &DLoc; + + /// Message to report. + const Twine &Msg; +}; + } // End namespace llvm #endif Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -29,6 +29,8 @@ class SMDiagnostic; class DiagnosticInfo; template class SmallVectorImpl; +class Function; +class DebugLoc; /// This is an important class for using LLVM in a threaded context. It /// (opaquely) owns and manages the core "global" data of LLVM's core @@ -125,6 +127,15 @@ void emitError(const Instruction *I, const Twine &ErrorStr); void emitError(const Twine &ErrorStr); + /// emitOptimizationRemark - Emit an optimization remark message. \p PassName + /// is the name of the pass emitting the message. If -Rpass= is given + /// and \p PassName matches the regular expression in -Rpass, then the + /// remark will be emitted. \p Fn is the function triggering the remark, + /// \p DLoc is the debug location where the diagnostic is generated. + /// \p Msg is the message string to use. + void emitOptimizationRemark(const char *PassName, const Function &Fn, + const DebugLoc &DLoc, const Twine &Msg); + private: LLVMContext(LLVMContext&) LLVM_DELETED_FUNCTION; void operator=(LLVMContext&) LLVM_DELETED_FUNCTION; Index: lib/IR/DiagnosticInfo.cpp =================================================================== --- lib/IR/DiagnosticInfo.cpp +++ lib/IR/DiagnosticInfo.cpp @@ -14,11 +14,13 @@ #include "llvm/ADT/Twine.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" #include "llvm/Support/Atomic.h" #include @@ -64,3 +66,29 @@ DP << getFileName() << ": "; DP << getMsg(); } + +bool DiagnosticInfoOptimizationRemark::isLocationAvailable() const { + return getFunction().getParent()->getNamedMetadata("llvm.dbg.cu") != 0; +} + +void DiagnosticInfoOptimizationRemark::getLocation(StringRef *Filename, + unsigned *Line, + unsigned *Column) const { + DILocation DIL(getDebugLoc().getAsMDNode(getFunction().getContext())); + *Filename = DIL.getFilename(); + *Line = DIL.getLineNumber(); + *Column = DIL.getColumnNumber(); +} + +const StringRef DiagnosticInfoOptimizationRemark::getLocationStr() const { + StringRef Filename(""); + unsigned Line = 0; + unsigned Column = 0; + if (isLocationAvailable()) + getLocation(&Filename, &Line, &Column); + return Twine(Filename + ":" + Twine(Line) + ":" + Twine(Column)).str(); +} + +void DiagnosticInfoOptimizationRemark::print(DiagnosticPrinter &DP) const { + DP << getLocationStr() << ": " << getMsg(); +} Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -15,6 +15,7 @@ #include "llvm/IR/LLVMContext.h" #include "LLVMContextImpl.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugLoc.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Instruction.h" @@ -155,6 +156,14 @@ diagnose(DiagnosticInfoInlineAsm(LocCookie, ErrorStr)); } +void LLVMContext::emitOptimizationRemark(const char *PassName, + const Function &Fn, + const DebugLoc &DLoc, + const Twine &Msg) { + if (pImpl->optimizationRemarksEnabledFor(PassName)) + diagnose(DiagnosticInfoOptimizationRemark(PassName, Fn, DLoc, Msg)); +} + //===----------------------------------------------------------------------===// // Metadata Kind Uniquing //===----------------------------------------------------------------------===// Index: lib/IR/LLVMContextImpl.h =================================================================== --- lib/IR/LLVMContextImpl.h +++ lib/IR/LLVMContextImpl.h @@ -368,6 +368,10 @@ typedef DenseMap PrefixDataMapTy; PrefixDataMapTy PrefixDataMap; + /// \brief Return true if the given pass name should emit optimization + /// remarks. + bool optimizationRemarksEnabledFor(const char *PassName) const; + int getOrAddScopeRecordIdxEntry(MDNode *N, int ExistingIdx); int getOrAddScopeInlinedAtIdxEntry(MDNode *Scope, MDNode *IA,int ExistingIdx); Index: lib/IR/LLVMContextImpl.cpp =================================================================== --- lib/IR/LLVMContextImpl.cpp +++ lib/IR/LLVMContextImpl.cpp @@ -15,6 +15,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Regex.h" #include using namespace llvm; @@ -43,6 +45,59 @@ } namespace { + +/// \brief Regular expression corresponding to the value given in the +/// command line flag -pass-remarks. Passes whose name matches this +/// regexp will emit a diagnostic when calling +/// LLVMContext::emitOptimizationRemark. +static Regex *OptimizationRemarkPattern = 0; + +/// \brief String to hold all the values passed via -pass-remarks. Every +/// instance of -pass-remarks on the command line will be concatenated +/// to this string. Values are stored inside braces and concatenated with +/// the '|' operator. This implements the expected semantics that multiple +/// -pass-remarks are additive. +static std::string OptimizationRemarkExpr; + +struct PassRemarksOpt { + void operator=(const std::string &Val) const { + // Create a regexp object to match pass names for emitOptimizationRemark. + if (!Val.empty()) { + if (!OptimizationRemarkExpr.empty()) + OptimizationRemarkExpr += "|"; + OptimizationRemarkExpr += "(" + Val + ")"; + delete OptimizationRemarkPattern; + OptimizationRemarkPattern = new Regex(OptimizationRemarkExpr); + std::string RegexError; + if (!OptimizationRemarkPattern->isValid(RegexError)) + report_fatal_error("Invalid regular expression '" + Val + + "' in -pass-remarks: " + RegexError, + false); + } + }; +}; + +static PassRemarksOpt PassRemarksOptLoc; + +// -pass-remarks +// Command line flag to enable LLVMContext::emitOptimizationRemark() +// and LLVMContext::emitOptimizationNote() calls. +static cl::opt> +PassRemarks("pass-remarks", cl::value_desc("pattern"), + cl::desc("Enable optimization remarks from passes whose name match " + "the given regular expression"), + cl::Hidden, cl::location(PassRemarksOptLoc), cl::ValueRequired, + cl::ZeroOrMore); +} + +bool +LLVMContextImpl::optimizationRemarksEnabledFor(const char *PassName) const { + return OptimizationRemarkPattern && + OptimizationRemarkPattern->match(PassName); +} + + +namespace { struct DropReferences { // Takes the value_type of a ConstantUniqueMap's internal map, whose 'second' // is a Constant*. Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -21,6 +21,7 @@ #include "llvm/Analysis/InlineCost.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" @@ -522,7 +523,12 @@ InlineHistoryID, InsertLifetime, DL)) continue; ++NumInlined; - + + // Report the inline decision. + Caller->getContext().emitOptimizationRemark( + DEBUG_TYPE, *Caller, CS.getInstruction()->getDebugLoc(), + Twine(Callee->getName() + " inlined into " + Caller->getName())); + // If inlining this function gave us any new call sites, throw them // onto our worklist to process. They are useful inline candidates. if (!InlineInfo.InlinedCalls.empty()) { Index: test/Other/optimization-remarks-inline.ll =================================================================== --- /dev/null +++ test/Other/optimization-remarks-inline.ll @@ -0,0 +1,33 @@ +; RUN: opt < %s -inline -pass-remarks='inline' -S 2>&1 | FileCheck %s +; RUN: opt < %s -inline -pass-remarks='inl.*' -S 2>&1 | FileCheck %s +; RUN: opt < %s -inline -pass-remarks='vector' -pass-remarks='inl' -S 2>&1 | FileCheck %s +; RUN: opt < %s -inline -pass-remarks='vector' -S 2>&1 | FileCheck --check-prefix=REMARKS %s +; RUN: opt < %s -inline -S 2>&1 | FileCheck --check-prefix=REMARKS %s +; RUN: not opt < %s -pass-remarks='(' 2>&1 | FileCheck --check-prefix=BAD-REGEXP %s + +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* %x.addr, align 4 + %1 = load 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* %j.addr, align 4 + %1 = load i32* %j.addr, align 4 + %sub = sub nsw i32 %1, 2 + %call = call i32 @foo(i32 %0, i32 %sub) +; CHECK: foo inlined into bar +; REMARKS-NOT: foo inlined into bar + ret i32 %call +} + +; BAD-REGEXP: Invalid regular expression '(' in -pass-remarks: