Index: include/clang/Basic/Diagnostic.td =================================================================== --- include/clang/Basic/Diagnostic.td +++ include/clang/Basic/Diagnostic.td @@ -102,6 +102,7 @@ class DefaultWarnShowInSystemHeader { bit WarningShowInSystemHeader = 1; } +class DefaultRemark { DiagMapping DefaultMapping = MAP_REMARK; } // Definitions for Diagnostics. include "DiagnosticASTKinds.td" Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -114,6 +114,8 @@ "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">, Index: include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- include/clang/Basic/DiagnosticFrontendKinds.td +++ include/clang/Basic/DiagnosticFrontendKinds.td @@ -32,6 +32,12 @@ 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">, Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -665,6 +665,7 @@ 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">; Index: include/clang/Basic/DiagnosticIDs.h =================================================================== --- include/clang/Basic/DiagnosticIDs.h +++ include/clang/Basic/DiagnosticIDs.h @@ -154,6 +154,9 @@ /// 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); Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -60,6 +60,7 @@ 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 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; Index: include/clang/Frontend/CodeGenOptions.h =================================================================== --- include/clang/Frontend/CodeGenOptions.h +++ include/clang/Frontend/CodeGenOptions.h @@ -16,6 +16,7 @@ #include #include +#include "llvm/Support/Regex.h" namespace clang { @@ -145,6 +146,13 @@ /// 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) Index: lib/Basic/DiagnosticIDs.cpp =================================================================== --- lib/Basic/DiagnosticIDs.cpp +++ lib/Basic/DiagnosticIDs.cpp @@ -361,6 +361,11 @@ 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 { Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ 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 @@ /// \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 @@ 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 @@ 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); Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -3415,6 +3415,9 @@ 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"); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -520,6 +520,17 @@ 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; } Index: lib/Frontend/TextDiagnosticPrinter.cpp =================================================================== --- lib/Frontend/TextDiagnosticPrinter.cpp +++ lib/Frontend/TextDiagnosticPrinter.cpp @@ -81,7 +81,8 @@ StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { - OS << (Started ? "," : " [") << "-W" << Opt; + OS << (Started ? "," : " [") + << (DiagnosticIDs::isRemark(Info.getID()) ? "-R" : "-W") << Opt; Started = true; } } Index: test/Frontend/optimization-remark.c =================================================================== --- /dev/null +++ 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