From d31ddfcf61863ed0c147cc0c027189ff1eb65c57 Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Wed, 16 Apr 2014 16:54:24 +0000 Subject: [PATCH] Add support for optimization reports. Summary: This patch adds a new flag -Rpass=. The flag indicates the name of the optimization pass that should emit remarks stating when it made a transformation to the code. This implements the design I proposed in: https://docs.google.com/document/d/1FYUatSjZZO-zmFBxjOiuOzAy9mhHA8hqdvklZv68WuQ/edit?usp=sharing Other changes: - Add DiagnosticIDs::isRemark(). Use it in printDiagnosticOptions to print "-R" instead of "-W" in the diagnostic message. - In BackendConsumer::OptimizationRemarkHandler, get a SourceLocation object out of the file name, line and column number. Use that location in the call to Diags.Report(). - When -Rpass is used without debug info a note is emitted alerting the user that they need to use -gline-tables-only -gcolumn-info to get this information. CC: llvm-commits Differential Revision: http://llvm-reviews.chandlerc.com/D3226 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@206401 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Diagnostic.td | 1 + include/clang/Basic/DiagnosticDriverKinds.td | 2 + .../clang/Basic/DiagnosticFrontendKinds.td | 6 +++ include/clang/Basic/DiagnosticGroups.td | 1 + include/clang/Basic/DiagnosticIDs.h | 3 ++ include/clang/Driver/Options.td | 5 +- include/clang/Frontend/CodeGenOptions.h | 8 +++ lib/Basic/DiagnosticIDs.cpp | 5 ++ lib/CodeGen/CodeGenAction.cpp | 49 +++++++++++++++++++ lib/Driver/Tools.cpp | 3 ++ lib/Frontend/CompilerInvocation.cpp | 11 +++++ lib/Frontend/TextDiagnosticPrinter.cpp | 3 +- test/Frontend/optimization-remark.c | 19 +++++++ 13 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 test/Frontend/optimization-remark.c diff --git a/include/clang/Basic/Diagnostic.td b/include/clang/Basic/Diagnostic.td index 61a4508615..5bcc2d5c16 100644 --- a/include/clang/Basic/Diagnostic.td +++ b/include/clang/Basic/Diagnostic.td @@ -102,6 +102,7 @@ class DefaultWarnNoWerror { class DefaultWarnShowInSystemHeader { bit WarningShowInSystemHeader = 1; } +class DefaultRemark { DiagMapping DefaultMapping = MAP_REMARK; } // Definitions for Diagnostics. include "DiagnosticASTKinds.td" diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index a64b1ae63c..f65fecdcf6 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -114,6 +114,8 @@ def err_drv_unknown_objc_runtime : Error< "unknown or ill-formed Objective-C runtime '%0'">; def err_drv_emit_llvm_link : Error< "-emit-llvm cannot be used when linking">; +def err_drv_optimization_remark_pattern : Error< + "%0 in '%1'">; def warn_O4_is_O3 : Warning<"-O4 is equivalent to -O3">, InGroup; def warn_drv_optimization_value : Warning<"optimization level '%0' is unsupported; using '%1%2' instead">, diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 32c824a930..7b500eed5c 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -32,6 +32,12 @@ def err_fe_backend_plugin: Error<"%0">, CatBackend; def remark_fe_backend_plugin: Remark<"%0">, CatBackend, InGroup; def note_fe_backend_plugin: Note<"%0">, CatBackend; +def remark_fe_backend_optimization_remark : Remark<"%0">, CatBackend, + InGroup, DefaultRemark; +def note_fe_backend_optimization_remark_missing_loc : Note<"use " + "-gline-tables-only -gcolumn-info to track source location information " + "for this optimization remark">; + def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; def err_fe_stdout_binary : Error<"unable to change standard output to binary">, diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index f707c989fc..b47b871dbe 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -665,6 +665,7 @@ def BackendInlineAsm : DiagGroup<"inline-asm">; def BackendFrameLargerThan : DiagGroup<"frame-larger-than">; def BackendPlugin : DiagGroup<"backend-plugin">; def RemarkBackendPlugin : DiagGroup<"remark-backend-plugin">; +def BackendOptimizationRemark : DiagGroup<"pass">; // Instrumentation based profiling warnings. def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">; diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index f9b845606b..c952e49793 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -154,6 +154,9 @@ public: /// default. static bool isDefaultMappingAsError(unsigned DiagID); + /// \brief Return true if the specified diagnostic is a Remark. + static bool isRemark(unsigned DiagID); + /// \brief Determine whether the given built-in diagnostic ID is a Note. static bool isBuiltinNote(unsigned DiagID); diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 1c84d74b8c..b734280a89 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -60,6 +60,7 @@ def I_Group : OptionGroup<"">, Group; def M_Group : OptionGroup<"">, Group; def T_Group : OptionGroup<"">; def O_Group : OptionGroup<"">, Group; +def R_Group : OptionGroup<"">, Group; def W_Group : OptionGroup<"">, Group; def d_Group : OptionGroup<"">; def f_Group : OptionGroup<"">, Group; @@ -253,7 +254,9 @@ def Qn : Flag<["-"], "Qn">; def Qunused_arguments : Flag<["-"], "Qunused-arguments">, Flags<[DriverOption, CoreOption]>, HelpText<"Don't emit warning for unused driver arguments">; def Q : Flag<["-"], "Q">; -def R : Flag<["-"], "R">; +def Rpass_EQ : Joined<["-"], "Rpass=">, Group, Flags<[CC1Option]>, + HelpText<"Report transformations performed by optimization passes whose " + "name matches the given POSIX regular expression">; def S : Flag<["-"], "S">, Flags<[DriverOption,CC1Option]>, Group, HelpText<"Only run preprocess and compilation steps">; def Tbss : JoinedOrSeparate<["-"], "Tbss">, Group; diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index da97e5c956..9c8de1b5ce 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -16,6 +16,7 @@ #include #include +#include "llvm/Support/Regex.h" namespace clang { @@ -145,6 +146,13 @@ public: /// Name of the profile file to use as input for -fprofile-instr-use std::string InstrProfileInput; + /// Regular expression to select optimizations for which we should enable + /// optimization remarks. Transformation passes whose name matches this + /// expression (and support this feature), will emit a diagnostic + /// whenever they perform a transformation. This is enabled by the + /// -Rpass=regexp flag. + std::shared_ptr OptimizationRemarkPattern; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index cf6933aeb1..5334ae88d1 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/lib/Basic/DiagnosticIDs.cpp @@ -361,6 +361,11 @@ bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) { return GetDefaultDiagMappingInfo(DiagID).getMapping() == diag::MAP_ERROR; } +bool DiagnosticIDs::isRemark(unsigned DiagID) { + return DiagID < diag::DIAG_UPPER_LIMIT && + getBuiltinDiagClass(DiagID) == CLASS_REMARK; +} + /// getDescription - Given a diagnostic ID, return a description of the /// issue. StringRef DiagnosticIDs::getDescription(unsigned DiagID) const { diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index 573f8e98e7..67859bad2b 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -20,6 +20,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" @@ -220,6 +221,11 @@ namespace clang { /// \return True if the diagnostic has been successfully reported, false /// otherwise. bool StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D); + /// \brief Specialized handler for the optimization diagnostic. + /// Note that this handler only accepts remarks and it always handles + /// them. + void + OptimizationRemarkHandler(const llvm::DiagnosticInfoOptimizationRemark &D); }; void BackendConsumer::anchor() {} @@ -374,6 +380,44 @@ BackendConsumer::StackSizeDiagHandler(const llvm::DiagnosticInfoStackSize &D) { return true; } +void BackendConsumer::OptimizationRemarkHandler( + const llvm::DiagnosticInfoOptimizationRemark &D) { + // We only support remarks. + assert (D.getSeverity() == llvm::DS_Remark); + + // Optimization remarks are active only if -Rpass=regexp is given and the + // regular expression pattern in 'regexp' matches the name of the pass + // name in \p D. + if (CodeGenOpts.OptimizationRemarkPattern && + CodeGenOpts.OptimizationRemarkPattern->match(D.getPassName())) { + SourceManager &SourceMgr = Context->getSourceManager(); + FileManager &FileMgr = SourceMgr.getFileManager(); + StringRef Filename; + unsigned Line, Column; + D.getLocation(&Filename, &Line, &Column); + SourceLocation Loc; + if (Line > 0) { + // If -gcolumn-info was not used, Column will be 0. This upsets the + // source manager, so if Column is not set, set it to 1. + if (Column == 0) + Column = 1; + Loc = SourceMgr.translateFileLineCol(FileMgr.getFile(Filename), Line, + Column); + } + Diags.Report(Loc, diag::remark_fe_backend_optimization_remark) + << D.getMsg().str(); + + if (Line == 0) + // If we could not extract a source location for the diagnostic, + // inform the user how they can get source locations back. + // + // FIXME: We should really be generating !srcloc annotations when + // -Rpass is used. !srcloc annotations need to be emitted in + // approximately the same spots as !dbg nodes. + Diags.Report(diag::note_fe_backend_optimization_remark_missing_loc); + } +} + /// \brief This function is invoked when the backend needs /// to report something to the user. void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { @@ -391,6 +435,11 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { return; ComputeDiagID(Severity, backend_frame_larger_than, DiagID); break; + case llvm::DK_OptimizationRemark: + // Optimization remarks are always handled completely by this + // handler. There is no generic way of emitting them. + OptimizationRemarkHandler(cast(DI)); + return; default: // Plugin IDs are not bound to any value as they are set dynamically. ComputeDiagRemarkID(Severity, backend_plugin, DiagID); diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 018f9ba574..9f64c3fa46 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3417,6 +3417,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->render(Args, CmdArgs); } + if (Arg *A = Args.getLastArg(options::OPT_Rpass_EQ)) + A->render(Args, CmdArgs); + if (Args.hasArg(options::OPT_mkernel)) { if (!Args.hasArg(options::OPT_fapple_kext) && types::isCXX(InputType)) CmdArgs.push_back("-fapple-kext"); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 6177a96fa7..e51d2cf852 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -520,6 +520,17 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DependentLibraries = Args.getAllArgValues(OPT_dependent_lib); + if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) { + StringRef Val = A->getValue(); + std::string RegexError; + Opts.OptimizationRemarkPattern = std::make_shared(Val); + if (!Opts.OptimizationRemarkPattern->isValid(RegexError)) { + Diags.Report(diag::err_drv_optimization_remark_pattern) + << RegexError << A->getAsString(Args); + Opts.OptimizationRemarkPattern.reset(); + } + } + return Success; } diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 994a8f74ed..3b5eca6dd9 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -81,7 +81,8 @@ static void printDiagnosticOptions(raw_ostream &OS, StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { - OS << (Started ? "," : " [") << "-W" << Opt; + OS << (Started ? "," : " [") + << (DiagnosticIDs::isRemark(Info.getID()) ? "-R" : "-W") << Opt; Started = true; } } diff --git a/test/Frontend/optimization-remark.c b/test/Frontend/optimization-remark.c new file mode 100644 index 0000000000..d5345b546a --- /dev/null +++ b/test/Frontend/optimization-remark.c @@ -0,0 +1,19 @@ +// This file tests the -Rpass= flag with the inliner. The test is +// designed to always trigger the inliner, so it should be independent +// of the optimization level. + +// RUN: %clang -c %s -Rpass=inline -O0 -gline-tables-only -o /dev/null 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=INLINE + +// RUN: %clang -c %s -Rpass=inline -O0 -o /dev/null 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=INLINE-NO-LOC + +int foo(int x, int y) __attribute__((always_inline)); + +int foo(int x, int y) { return x + y; } +int bar(int j) { return foo(j, j - 2); } + +// INLINE: remark: foo inlined into bar [-Rpass] + +// INLINE-NO-LOC: {{^remark: foo inlined into bar}} +// INLINE-NO-LOC: note: use -gline-tables-only -gcolumn-info to track -- 2.40.0