Index: include/llvm/Analysis/OptimizationDiagnosticInfo.h =================================================================== --- include/llvm/Analysis/OptimizationDiagnosticInfo.h +++ include/llvm/Analysis/OptimizationDiagnosticInfo.h @@ -17,8 +17,10 @@ #include "llvm/ADT/Optional.h" #include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" +#include "llvm/Support/ToolOutputFile.h" namespace llvm { class DebugLoc; @@ -62,6 +64,9 @@ return *this; } + /// The new interface to emit remarks. + void emit(DiagnosticInfoOptimizationBase &OptDiag); + /// Emit an optimization-applied message. /// /// \p PassName is the name of the pass emitting the message. If -Rpass= is @@ -199,6 +204,7 @@ std::unique_ptr OwnedBFI; Optional computeHotness(const Value *V); + void computeHotness(DiagnosticInfoOptimizationBase &OptDiag); /// \brief Only allow verbose messages if we know we're filtering by hotness /// (BFI is only set in this case). Index: include/llvm/IR/DiagnosticInfo.h =================================================================== --- include/llvm/IR/DiagnosticInfo.h +++ include/llvm/IR/DiagnosticInfo.h @@ -20,6 +20,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/IR/DebugLoc.h" #include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm-c/Types.h" #include #include @@ -375,6 +376,47 @@ /// Common features for diagnostics dealing with optimization remarks. class DiagnosticInfoOptimizationBase : public DiagnosticInfoWithDebugLocBase { public: + struct Argument { + enum KindType { StringKind, ValueKind }; + + KindType Kind; + std::string Str; + Value *V; + + Argument(Value *V = nullptr) : Kind(ValueKind), V(V) {} + Argument(StringRef S) : Kind(StringKind), Str(S) {} + + std::string getString(bool UseTag = false) const { + std::string Buffer; + raw_string_ostream OS(Buffer); + + switch (Kind) { + case StringKind: + if (UseTag) + OS << "string: "; + OS << Str; + break; + case ValueKind: + if (UseTag) + OS << "value: "; + OS << V->getName(); + break; + } + + return OS.str(); + } + }; + + DiagnosticInfoOptimizationBase(enum DiagnosticKind Kind, + enum DiagnosticSeverity Severity, + const char *PassName, + const Function &Fn, + const DebugLoc &DLoc, + Value *CodeRegion = nullptr) + : DiagnosticInfoWithDebugLocBase(Kind, Severity, Fn, DLoc), + PassName(PassName), CodeRegion(CodeRegion) {} + + /// Legacy interface. /// \p PassName is the name of the pass emitting this diagnostic. /// \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 @@ -388,7 +430,12 @@ const DebugLoc &DLoc, const Twine &Msg, Optional Hotness = None) : DiagnosticInfoWithDebugLocBase(Kind, Severity, Fn, DLoc), - PassName(PassName), Msg(Msg), Hotness(Hotness) {} + PassName(PassName), Hotness(Hotness) { + Args.emplace_back(Msg.str()); + } + + DiagnosticInfoOptimizationBase &operator<<(StringRef S); + DiagnosticInfoOptimizationBase &operator<<(Value *V); /// \see DiagnosticInfo::print. void print(DiagnosticPrinter &DP) const override; @@ -400,9 +447,12 @@ /// in BackendConsumer::OptimizationRemarkHandler). virtual bool isEnabled() const = 0; - const char *getPassName() const { return PassName; } - const Twine &getMsg() const { return Msg; } + const char *getPassName() const { return PassName.c_str(); } + std::string getMsg() const; Optional getHotness() const { return Hotness; } + void setHotness(Optional H) { Hotness = H; } + + Value *getCodeRegion() const { return CodeRegion; } static bool classof(const DiagnosticInfo *DI) { return DI->getKind() >= DK_FirstRemark && @@ -413,14 +463,17 @@ /// 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; - - /// Message to report. - const Twine &Msg; + std::string PassName; /// If profile information is available, this is the number of times the /// corresponding code was executed in a profile instrumentation run. Optional Hotness; + + Value *CodeRegion; + + SmallVector Args; + + friend struct yaml::MappingTraits; }; /// Diagnostic information for applied optimization remarks. @@ -467,6 +520,9 @@ : DiagnosticInfoOptimizationBase(DK_OptimizationRemarkMissed, DS_Remark, PassName, Fn, DLoc, Msg, Hotness) {} + DiagnosticInfoOptimizationRemarkMissed(const char *PassName, + Instruction *Inst); + static bool classof(const DiagnosticInfo *DI) { return DI->getKind() == DK_OptimizationRemarkMissed; } Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -34,6 +34,9 @@ class Function; class DebugLoc; class OptBisect; +namespace yaml { +class Output; +} /// 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 @@ -181,6 +184,9 @@ /// diagnostics. void setDiagnosticHotnessRequested(bool Requested); + yaml::Output *getDiagnosticsOutputFile(); + void setDiagnosticsOutputFile(yaml::Output *F); + /// \brief Get the prefix that should be printed in front of a diagnostic of /// the given \p Severity static const char *getDiagnosticMessagePrefix(DiagnosticSeverity Severity); Index: lib/Analysis/OptimizationDiagnosticInfo.cpp =================================================================== --- lib/Analysis/OptimizationDiagnosticInfo.cpp +++ lib/Analysis/OptimizationDiagnosticInfo.cpp @@ -16,6 +16,7 @@ #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/LazyBlockFrequencyInfo.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/LLVMContext.h" @@ -51,6 +52,85 @@ return BFI->getBlockProfileCount(cast(V)); } +template<> +struct yaml::MappingTraits { + static void mapping(IO &io, DiagnosticInfoOptimizationBase *&OptDiag) { + assert(io.outputting() && "input not yet implemented"); + + if (io.mapTag("!Missed", + OptDiag->getKind() == DK_OptimizationRemarkMissed)) + ; + else + llvm_unreachable("todo"); + + // These are read-only for now. + DebugLoc DL = OptDiag->getDebugLoc(); + std::string FN = OptDiag->getFunction().getName(); + + io.mapRequired("Pass", OptDiag->PassName); + if (!io.outputting() || DL) + io.mapOptional("DebugLoc", DL); + io.mapRequired("Function", FN); + io.mapOptional("Hotness", OptDiag->Hotness); + io.mapOptional("Args", OptDiag->Args); + } +}; + +template<> +struct yaml::MappingTraits { + static void mapping(IO &io, DebugLoc &DL) { + assert(io.outputting() && "input not yet implemented"); + + auto *Scope = cast(DL.getScope()); + std::string File = Scope->getFilename(); + unsigned Line = DL.getLine(); + unsigned Col = DL.getCol(); + + io.mapRequired("File", File); + io.mapRequired("Line", Line); + io.mapRequired("Column", Col); + } + + static const bool flow = true; +}; + +template<> +struct yaml::ScalarTraits { + static void output(const DiagnosticInfoOptimizationBase::Argument &Arg, + void *, llvm::raw_ostream &out) { + out << Arg.getString(/*UseTag=*/true); + } + + static StringRef input(StringRef scalar, void *, + DiagnosticInfoOptimizationBase::Argument &Arg) { + llvm_unreachable("input not yet implemented"); + } + + static bool mustQuote(StringRef) { return false; } +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(DiagnosticInfoOptimizationBase::Argument) + +void OptimizationRemarkEmitter::computeHotness( + DiagnosticInfoOptimizationBase &OptDiag) { + Value *V = OptDiag.getCodeRegion(); + if (V) + OptDiag.setHotness(computeHotness(V)); +} + +void OptimizationRemarkEmitter::emit( + DiagnosticInfoOptimizationBase &OptDiag) { + computeHotness(OptDiag); + + yaml::Output *Out = F->getContext().getDiagnosticsOutputFile(); + if (Out) { + auto *P = &const_cast(OptDiag); + *Out << P; + } + else + F->getContext().diagnose(OptDiag); +} + void OptimizationRemarkEmitter::emitOptimizationRemark(const char *PassName, const DebugLoc &DLoc, const Value *V, Index: lib/IR/DiagnosticInfo.cpp =================================================================== --- lib/IR/DiagnosticInfo.cpp +++ lib/IR/DiagnosticInfo.cpp @@ -181,6 +181,13 @@ PassRemarksOptLoc.Pattern->match(getPassName()); } +DiagnosticInfoOptimizationRemarkMissed:: +DiagnosticInfoOptimizationRemarkMissed(const char *PassName, + Instruction *Inst) : + DiagnosticInfoOptimizationBase(DK_OptimizationRemarkMissed, DS_Remark, + PassName, *Inst->getParent()->getParent(), + Inst->getDebugLoc(), Inst->getParent()) {} + bool DiagnosticInfoOptimizationRemarkMissed::isEnabled() const { return PassRemarksMissedOptLoc.Pattern && PassRemarksMissedOptLoc.Pattern->match(getPassName()); @@ -266,3 +273,24 @@ void DiagnosticInfoISelFallback::print(DiagnosticPrinter &DP) const { DP << "Instruction selection used fallback path for " << getFunction(); } + +DiagnosticInfoOptimizationBase & +DiagnosticInfoOptimizationBase::operator<<(StringRef S) { + Args.emplace_back(S); + return *this; +} + +DiagnosticInfoOptimizationBase & +DiagnosticInfoOptimizationBase::operator<<(Value *V) { + Args.emplace_back(V); + return *this; +} + +std::string +DiagnosticInfoOptimizationBase::getMsg() const { + std::string Str; + raw_string_ostream OS(Str); + for (const DiagnosticInfoOptimizationBase::Argument &Arg: Args) + OS << Arg.getString(); + return OS.str(); +} Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -203,6 +203,14 @@ return pImpl->DiagnosticHotnessRequested; } +yaml::Output *LLVMContext::getDiagnosticsOutputFile() { + return pImpl->DiagnosticsOutputFile.get(); +} + +void LLVMContext::setDiagnosticsOutputFile(yaml::Output *F) { + pImpl->DiagnosticsOutputFile.reset(F); +} + LLVMContext::DiagnosticHandlerTy LLVMContext::getDiagnosticHandler() const { return pImpl->DiagnosticHandler; } Index: lib/IR/LLVMContextImpl.h =================================================================== --- lib/IR/LLVMContextImpl.h +++ lib/IR/LLVMContextImpl.h @@ -33,6 +33,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Support/Dwarf.h" +#include "llvm/Support/YAMLTraits.h" #include namespace llvm { @@ -1043,6 +1044,7 @@ void *DiagnosticContext; bool RespectDiagnosticFilters; bool DiagnosticHotnessRequested; + std::unique_ptr DiagnosticsOutputFile; LLVMContext::YieldCallbackTy YieldCallback; void *YieldOpaqueHandle; Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -469,10 +469,11 @@ // direct call, so we keep it. if (Function *Callee = CS.getCalledFunction()) if (Callee->isDeclaration()) { - ORE.emitOptimizationRemarkMissedAndAnalysis( + ORE.emit(DiagnosticInfoOptimizationRemarkMissed(DEBUG_TYPE, &I) + << Callee << " will not be inlined into " + << CS.getCaller()); + ORE.emitOptimizationRemarkAnalysis( DEBUG_TYPE, &I, - Twine(Callee->getName()) + " will not be inlined into " + - CS.getCaller()->getName(), Twine("definition of ") + Callee->getName() + " is not available", /*Verbose=*/true); Index: test/Transforms/Inline/optimization-remarks-yaml.ll =================================================================== --- /dev/null +++ test/Transforms/Inline/optimization-remarks-yaml.ll @@ -0,0 +1,64 @@ +; RUN: opt < %s -inline -pass-remarks-missed=inline -pass-remarks-with-hotness \ +; RUN: -pass-remarks-output=%t | FileCheck %s +; RUN: cat %t | FileCheck -check-prefix=YAML %s + +; CHECK-NOT: remark: + +; YAML: --- !Missed +; YAML-NEXT: Pass: inline +; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 5, Column: 10 } +; YAML-NEXT: Function: baz +; YAML-NEXT: Hotness: 30 +; YAML-NEXT: Args: +; YAML-NEXT: - value: foo +; YAML-NEXT: - string: will not be inlined into +; YAML-NEXT: - value: baz +; YAML-NEXT: ... +; YAML-NEXT: --- !Missed +; YAML-NEXT: Pass: inline +; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 5, Column: 18 } +; YAML-NEXT: Function: baz +; YAML-NEXT: Hotness: 30 +; YAML-NEXT: Args: +; YAML-NEXT: - value: bar +; YAML-NEXT: - string: will not be inlined into +; YAML-NEXT: - value: baz +; YAML-NEXT: ... + +; ModuleID = '/tmp/s.c' +source_filename = "/tmp/s.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +; Function Attrs: nounwind ssp uwtable +define i32 @baz() !dbg !7 !prof !14 { +entry: + %call = call i32 (...) @foo(), !dbg !9 + %call1 = call i32 (...) @bar(), !dbg !10 + %add = add nsw i32 %call, %call1, !dbg !12 + ret i32 %add, !dbg !13 +} + +declare i32 @foo(...) + +declare i32 @bar(...) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0 (trunk 281293) (llvm/trunk 281290)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) +!1 = !DIFile(filename: "/tmp/s.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"PIC Level", i32 2} +!6 = !{!"clang version 4.0.0 (trunk 281293) (llvm/trunk 281290)"} +!7 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !8, isLocal: false, isDefinition: true, scopeLine: 4, isOptimized: true, unit: !0, variables: !2) +!8 = !DISubroutineType(types: !2) +!9 = !DILocation(line: 5, column: 10, scope: !7) +!10 = !DILocation(line: 5, column: 18, scope: !11) +!11 = !DILexicalBlockFile(scope: !7, file: !1, discriminator: 1) +!12 = !DILocation(line: 5, column: 16, scope: !7) +!13 = !DILocation(line: 5, column: 3, scope: !7) +!14 = !{!"function_entry_count", i64 30} Index: tools/opt/opt.cpp =================================================================== --- tools/opt/opt.cpp +++ tools/opt/opt.cpp @@ -49,6 +49,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Coroutines.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" @@ -231,6 +232,11 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); +static cl::opt +RemarksFilename("pass-remarks-output", + cl::desc("Output filename for pass remarks"), + cl::value_desc("filename")); + static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); @@ -408,6 +414,20 @@ if (PassRemarksWithHotness) Context.setDiagnosticHotnessRequested(true); + std::unique_ptr YamlFile; + if (RemarksFilename != "") { + std::error_code EC; + YamlFile = + llvm::make_unique(RemarksFilename, EC, sys::fs::F_None); + // FIXME: + YamlFile->keep(); + if (EC) { + errs() << EC.message() << '\n'; + return 1; + } + Context.setDiagnosticsOutputFile(new yaml::Output(YamlFile->os())); + } + // Load the input module... std::unique_ptr M = parseIRFile(InputFilename, Err, Context);