victorkingi updated this revision to Diff 548664.
victorkingi edited the summary of this revision.
victorkingi added a comment.

Added remark error test and color printing for remark errors


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156320/new/

https://reviews.llvm.org/D156320

Files:
  flang/include/flang/Frontend/CodeGenOptions.h
  flang/lib/Frontend/CompilerInvocation.cpp
  flang/lib/Frontend/FrontendActions.cpp
  flang/lib/Frontend/TextDiagnosticPrinter.cpp
  flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
  flang/test/Driver/optimization-remark.f90

Index: flang/test/Driver/optimization-remark.f90
===================================================================
--- /dev/null
+++ flang/test/Driver/optimization-remark.f90
@@ -0,0 +1,55 @@
+! This file tests the -Rpass family of flags (-Rpass, -Rpass-missed
+! and -Rpass-analysis)
+! loop-delete isn't enabled at O0 so we use at least O1
+
+! Check that we can override -Rpass= with -Rno-pass.
+! RUN: %flang_fc1 %s -O1 -Rpass=loop-delete -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
+! RUN: %flang_fc1 %s -O1 -Rpass=loop-delete -Rno-pass -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-REMARKS
+! RUN: %flang_fc1 %s -O1 -Rpass=loop-delete -Rno-everything -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-REMARKS
+! RUN: %flang_fc1 %s -O1 -Rpass=loop-delete -Rno-everything -Reverything -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
+
+! -Reverything implies -Rpass=.*.
+! RUN: %flang_fc1 %s -O1 -Reverything -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
+
+! -Rpass implies -Rpass=.*
+! RUN: %flang_fc1 %s -O1 -Rpass -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
+
+! Check error on bad regex
+! RUN: not %flang %s -O1 -Rpass=[ 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS-ERROR
+
+! Check full -Rpass message is emitted
+! RUN: %flang %s -O1 -Rpass=loop-delete 2>&1 | FileCheck %s
+
+! Check full -Rpass-missed message is emitted
+! RUN: %flang %s -O1 -Rpass-missed=loop-vectorize 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS-MISSED
+
+! Check full -Rpass-analysis message is emitted
+! RUN: %flang %s -O1 -Rpass-analysis=loop-vectorize 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS-ANALYSIS
+
+
+! CHECK: remark: Loop deleted because it is invariant
+
+! CHECK-REMARKS-MISSED: remark: loop not vectorized
+! CHECK-REMARKS-ANALYSIS: remark: loop not vectorized: call instruction cannot be vectorized
+! CHECK-REMARKS: remark:
+
+! CHECK-NO-REMARKS-NOT: remark:
+
+! CHECK-REMARKS-ERROR: error: in pattern '-Rpass=[': brackets ([ ]) not balanced
+
+
+program forttest
+    implicit none
+    real, dimension(1:50) :: aR1
+    integer :: n
+
+    do n = 1,50
+        aR1(n) = n * 1.34
+        print *, "hello"
+    end do
+
+    do n = 1,50
+        aR1(n) = n * 1.34
+    end do
+
+end program forttest
Index: flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -165,6 +165,10 @@
 
   // Honor color diagnostics.
   flang->getDiagnosticOpts().ShowColors = flang->getFrontendOpts().showColors;
+  flang->getDiagnosticOpts().ShowOptionNames = 1;
+
+  clang::ProcessWarningOptions(flang->getDiagnostics(),
+                               flang->getDiagnosticOpts(), false);
 
   // Create and execute the frontend action.
   std::unique_ptr<FrontendAction> act(createFrontendAction(*flang));
Index: flang/lib/Frontend/TextDiagnosticPrinter.cpp
===================================================================
--- flang/lib/Frontend/TextDiagnosticPrinter.cpp
+++ flang/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -20,6 +20,10 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <filesystem>
+#include <string>
+#include <vector>
 
 using namespace Fortran::frontend;
 
@@ -29,6 +33,28 @@
 
 TextDiagnosticPrinter::~TextDiagnosticPrinter() {}
 
+// Print any diagnostic option information to a raw_ostream.
+static void printDiagnosticOptions(llvm::raw_ostream &os,
+                                   clang::DiagnosticsEngine::Level level,
+                                   const clang::Diagnostic &info,
+                                   const clang::DiagnosticOptions &diagOpts) {
+  bool started = false;
+  if (diagOpts.ShowOptionNames) {
+    llvm::StringRef opt =
+        clang::DiagnosticIDs::getWarningOptionForDiag(info.getID());
+    if (!opt.empty()) {
+      os << (started ? "," : " [")
+         << (level == clang::DiagnosticsEngine::Remark ? "-R" : "-W") << opt;
+      llvm::StringRef optValue = info.getDiags()->getFlagValue();
+      if (!optValue.empty())
+        os << "=" << optValue;
+      started = true;
+    }
+  }
+  if (started)
+    os << ']';
+}
+
 void TextDiagnosticPrinter::HandleDiagnostic(
     clang::DiagnosticsEngine::Level level, const clang::Diagnostic &info) {
   // Default implementation (Warnings/errors count).
@@ -40,6 +66,7 @@
   info.FormatDiagnostic(outStr);
 
   llvm::raw_svector_ostream diagMessageStream(outStr);
+  printDiagnosticOptions(diagMessageStream, level, info, *diagOpts);
 
   if (!prefix.empty())
     os << prefix << ": ";
@@ -48,12 +75,46 @@
   assert(!info.getLocation().isValid() &&
          "Diagnostics with valid source location are not supported");
 
+  // split incoming string to get the absolute path and filename in the
+  // case we are receiving optimization remarks from StandaloneBackendConsumer
+  std::string s = std::string(diagMessageStream.str());
+  std::string delimiter = ";;";
+
+  size_t pos = 0;
+  std::vector<std::string> tokens;
+  while ((pos = s.find(delimiter)) != std::string::npos) {
+    tokens.push_back(s.substr(0, pos));
+    s.erase(0, pos + delimiter.length());
+  }
+
+  // tokens will always be of size 2 in the case of optimization
+  // remark message received, in this format;
+  // [file location with line and column];;[path to file];;[the remark message]
+  if (tokens.size() == 2) {
+    // extract absolute path from the provided relative path
+    std::filesystem::path absPath(tokens[1]);
+    std::filesystem::path canonicalPath =
+        std::filesystem::weakly_canonical(absPath);
+
+    // we don't need the filename since we will append tokens[0]
+    // which is the filename, line and column number
+    canonicalPath.remove_filename();
+    absPath = canonicalPath.make_preferred().string();
+
+    // used for changing only the bold attribute
+    if (diagOpts->ShowColors)
+      os.changeColor(llvm::raw_ostream::SAVEDCOLOR, true);
+
+    // print absolute path, file name, line and column
+    os << absPath << tokens[0] << ": ";
+  }
+
   Fortran::frontend::TextDiagnostic::printDiagnosticLevel(os, level,
                                                           diagOpts->ShowColors);
   Fortran::frontend::TextDiagnostic::printDiagnosticMessage(
       os,
-      /*IsSupplemental=*/level == clang::DiagnosticsEngine::Note,
-      diagMessageStream.str(), diagOpts->ShowColors);
+      /*IsSupplemental=*/level == clang::DiagnosticsEngine::Note, s,
+      diagOpts->ShowColors);
 
   os.flush();
 }
Index: flang/lib/Frontend/FrontendActions.cpp
===================================================================
--- flang/lib/Frontend/FrontendActions.cpp
+++ flang/lib/Frontend/FrontendActions.cpp
@@ -48,6 +48,7 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Verifier.h"
@@ -923,6 +924,107 @@
   mpm.run(*llvmModule, mam);
 }
 
+class StandaloneBackendConsumer : public llvm::DiagnosticHandler {
+
+  const CodeGenOptions &codeGenOpts;
+  clang::DiagnosticsEngine &diags;
+
+public:
+  StandaloneBackendConsumer(clang::DiagnosticsEngine &diags,
+                            const CodeGenOptions &codeGenOpts)
+      : codeGenOpts(codeGenOpts), diags(diags) {}
+
+  bool isAnalysisRemarkEnabled(llvm::StringRef passName) const override {
+    return codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName);
+  }
+  bool isMissedOptRemarkEnabled(llvm::StringRef passName) const override {
+    return codeGenOpts.OptimizationRemarkMissed.patternMatches(passName);
+  }
+  bool isPassedOptRemarkEnabled(llvm::StringRef passName) const override {
+    return codeGenOpts.OptimizationRemark.patternMatches(passName);
+  }
+
+  bool isAnyRemarkEnabled() const override {
+    return codeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() ||
+           codeGenOpts.OptimizationRemarkMissed.hasValidPattern() ||
+           codeGenOpts.OptimizationRemark.hasValidPattern();
+  }
+
+  void emitOptimizationMessage(const llvm::DiagnosticInfoOptimizationBase &d,
+                               unsigned diagID) {
+    // We only support warnings and remarks.
+    assert(d.getSeverity() == llvm::DS_Remark ||
+           d.getSeverity() == llvm::DS_Warning);
+
+    std::string msg;
+    llvm::raw_string_ostream msgStream(msg);
+
+    if (d.isLocationAvailable()) {
+      // Since sourceMgr isn't available, send file name and absolute path
+      // through msgStream, to use for printing
+      msgStream << d.getLocationStr() << ";;" << d.getAbsolutePath() << ";;";
+      msgStream << d.getMsg();
+    } else {
+      msgStream << d.getMsg();
+      llvm::DiagnosticPrinterRawOStream dp(msgStream);
+      d.print(dp);
+    }
+
+    // Emit message.
+    diags.Report(diagID) << clang::AddFlagValue(d.getPassName())
+                         << msgStream.str();
+  }
+
+  void
+  optimizationRemarkHandler(const llvm::DiagnosticInfoOptimizationBase &d) {
+    if (d.isPassed()) {
+      // Optimization remarks are active only if the -Rpass flag has a regular
+      // expression that matches the name of the pass name in \p D.
+      if (codeGenOpts.OptimizationRemark.patternMatches(d.getPassName()))
+        emitOptimizationMessage(
+            d, clang::diag::remark_fe_backend_optimization_remark);
+
+    } else if (d.isMissed()) {
+      // Missed optimization remarks are active only if the -Rpass-missed
+      // flag has a regular expression that matches the name of the pass
+      // name in \p D.
+      if (codeGenOpts.OptimizationRemarkMissed.patternMatches(d.getPassName()))
+        emitOptimizationMessage(
+            d, clang::diag::remark_fe_backend_optimization_remark_missed);
+    } else {
+      assert(d.isAnalysis() && "Unknown remark type");
+
+      bool shouldAlwaysPrint = false;
+      if (auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(&d))
+        shouldAlwaysPrint = ora->shouldAlwaysPrint();
+
+      if (shouldAlwaysPrint ||
+          codeGenOpts.OptimizationRemarkAnalysis.patternMatches(
+              d.getPassName()))
+        emitOptimizationMessage(
+            d, clang::diag::remark_fe_backend_optimization_remark_analysis);
+    }
+  }
+
+  bool handleDiagnostics(const llvm::DiagnosticInfo &di) override {
+    switch (di.getKind()) {
+    case llvm::DK_OptimizationRemark:
+      optimizationRemarkHandler(llvm::cast<llvm::OptimizationRemark>(di));
+      break;
+    case llvm::DK_OptimizationRemarkMissed:
+      optimizationRemarkHandler(llvm::cast<llvm::OptimizationRemarkMissed>(di));
+      break;
+    case llvm::DK_OptimizationRemarkAnalysis:
+      optimizationRemarkHandler(
+          llvm::cast<llvm::OptimizationRemarkAnalysis>(di));
+      break;
+    default:
+      break;
+    }
+    return true;
+  }
+};
+
 void CodeGenAction::embedOffloadObjects() {
   CompilerInstance &ci = this->getInstance();
   const auto &cgOpts = ci.getInvocation().getCodeGenOpts();
@@ -1033,6 +1135,11 @@
   if (!codeGenOpts.OffloadObjects.empty())
     embedOffloadObjects();
 
+  StandaloneBackendConsumer m(diags, codeGenOpts);
+
+  llvmModule->getContext().setDiagnosticHandler(
+      std::make_unique<StandaloneBackendConsumer>(m));
+
   // write optimization-record
   llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
       setupLLVMOptimizationRemarks(
Index: flang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- flang/lib/Frontend/CompilerInvocation.cpp
+++ flang/lib/Frontend/CompilerInvocation.cpp
@@ -153,6 +153,63 @@
   return true;
 }
 
+/// Parse a remark command line argument. It may be missing, disabled/enabled by
+/// '-R[no-]group' or specified with a regular expression by '-Rgroup=regexp'.
+/// On top of that, it can be disabled/enabled globally by '-R[no-]everything'.
+static CodeGenOptions::OptRemark
+parseOptimizationRemark(clang::DiagnosticsEngine &diags,
+                        llvm::opt::ArgList &args, llvm::opt::OptSpecifier optEq,
+                        llvm::StringRef name) {
+  CodeGenOptions::OptRemark result;
+
+  auto initializeResultPattern = [&diags, &args,
+                                  &result](const llvm::opt::Arg *a,
+                                           llvm::StringRef pattern) {
+    result.Pattern = pattern.str();
+
+    std::string regexError;
+    result.Regex = std::make_shared<llvm::Regex>(result.Pattern);
+    if (!result.Regex->isValid(regexError)) {
+      diags.Report(clang::diag::err_drv_optimization_remark_pattern)
+          << regexError << a->getAsString(args);
+      return false;
+    }
+
+    return true;
+  };
+
+  for (llvm::opt::Arg *a : args) {
+    if (a->getOption().matches(clang::driver::options::OPT_R_Joined)) {
+      llvm::StringRef value = a->getValue();
+
+      if (value == name)
+        result.Kind = CodeGenOptions::RemarkKind::RK_Enabled;
+      else if (value == "everything")
+        result.Kind = CodeGenOptions::RemarkKind::RK_EnabledEverything;
+      else if (value.split('-') == std::make_pair(llvm::StringRef("no"), name))
+        result.Kind = CodeGenOptions::RemarkKind::RK_Disabled;
+      else if (value == "no-everything")
+        result.Kind = CodeGenOptions::RemarkKind::RK_DisabledEverything;
+      else
+        continue;
+
+      if (result.Kind == CodeGenOptions::RemarkKind::RK_Disabled ||
+          result.Kind == CodeGenOptions::RemarkKind::RK_DisabledEverything) {
+        result.Pattern = "";
+        result.Regex = nullptr;
+      } else {
+        initializeResultPattern(a, ".*");
+      }
+    } else if (a->getOption().matches(optEq)) {
+      result.Kind = CodeGenOptions::RemarkKind::RK_WithPattern;
+      if (!initializeResultPattern(a, a->getValue()))
+        return CodeGenOptions::OptRemark();
+    }
+  }
+
+  return result;
+}
+
 static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
                              llvm::opt::ArgList &args,
                              clang::DiagnosticsEngine &diags) {
@@ -194,13 +251,42 @@
           args.getLastArg(clang::driver::options::OPT_opt_record_file))
     opts.OptRecordFile = a->getValue();
 
-  if (const llvm::opt::Arg *a =
-          args.getLastArg(clang::driver::options::OPT_opt_record_format))
-    opts.OptRecordFormat = a->getValue();
+  bool needLocTracking = false;
+
+  if (!opts.OptRecordFile.empty())
+    needLocTracking = true;
 
   if (const llvm::opt::Arg *a =
-          args.getLastArg(clang::driver::options::OPT_opt_record_passes))
+          args.getLastArg(clang::driver::options::OPT_opt_record_passes)) {
     opts.OptRecordPasses = a->getValue();
+    needLocTracking = true;
+  }
+
+  if (const llvm::opt::Arg *a =
+          args.getLastArg(clang::driver::options::OPT_opt_record_format)) {
+    opts.OptRecordFormat = a->getValue();
+    needLocTracking = true;
+  }
+
+  opts.OptimizationRemark = parseOptimizationRemark(
+      diags, args, clang::driver::options::OPT_Rpass_EQ, "pass");
+
+  opts.OptimizationRemarkMissed = parseOptimizationRemark(
+      diags, args, clang::driver::options::OPT_Rpass_missed_EQ, "pass-missed");
+
+  opts.OptimizationRemarkAnalysis = parseOptimizationRemark(
+      diags, args, clang::driver::options::OPT_Rpass_analysis_EQ,
+      "pass-analysis");
+
+  needLocTracking |= opts.OptimizationRemark.hasValidPattern() ||
+                     opts.OptimizationRemarkMissed.hasValidPattern() ||
+                     opts.OptimizationRemarkAnalysis.hasValidPattern();
+
+  // If the user requested a flag that requires source locations available in
+  // the backend, make sure that the backend tracks source location information.
+  if (needLocTracking &&
+      opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo)
+    opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly);
 
   if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ))
     opts.SaveTempsDir = a->getValue();
@@ -697,6 +783,7 @@
   // Default to off for `flang-new -fc1`.
   res.getFrontendOpts().showColors =
       parseShowColorsArgs(args, /*defaultDiagColor=*/false);
+  res.getDiagnosticOpts().ShowColors = res.getFrontendOpts().showColors;
 
   return diags.getNumErrors() == numErrorsBefore;
 }
@@ -903,6 +990,28 @@
   return true;
 }
 
+static void addDiagnosticArgs(const llvm::opt::ArgList &args,
+                              llvm::opt::OptSpecifier group,
+                              llvm::opt::OptSpecifier groupWithValue,
+                              std::vector<std::string> &diagnostics) {
+  for (auto *a : args.filtered(group)) {
+    if (a->getOption().getKind() == llvm::opt::Option::FlagClass) {
+      // The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add
+      // its name (minus the "W" or "R" at the beginning) to the diagnostics.
+      diagnostics.push_back(
+          std::string(a->getOption().getName().drop_front(1)));
+    } else if (a->getOption().matches(groupWithValue)) {
+      // This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic
+      // group. Add only the group name to the diagnostics.
+      diagnostics.push_back(
+          std::string(a->getOption().getName().drop_front(1).rtrim("=-")));
+    } else {
+      // Otherwise, add its value (for OPT_W_Joined and similar).
+      diagnostics.push_back(a->getValue());
+    }
+  }
+}
+
 bool CompilerInvocation::createFromArgs(
     CompilerInvocation &res, llvm::ArrayRef<const char *> commandLineArgs,
     clang::DiagnosticsEngine &diags, const char *argv0) {
@@ -959,6 +1068,10 @@
     res.loweringOpts.setNoPPCNativeVecElemOrder(true);
   }
 
+  addDiagnosticArgs(args, clang::driver::options::OPT_R_Group,
+                    clang::driver::options::OPT_R_value_Group,
+                    res.getDiagnosticOpts().Remarks);
+
   success &= parseFrontendArgs(res.getFrontendOpts(), args, diags);
   parseTargetArgs(res.getTargetOpts(), args);
   parsePreprocessorArgs(res.getPreprocessorOpts(), args);
Index: flang/include/flang/Frontend/CodeGenOptions.h
===================================================================
--- flang/include/flang/Frontend/CodeGenOptions.h
+++ flang/include/flang/Frontend/CodeGenOptions.h
@@ -69,6 +69,53 @@
   /// The format used for serializing remarks (default: YAML)
   std::string OptRecordFormat;
 
+  enum class RemarkKind {
+    RK_Missing,            // Remark argument not present on the command line.
+    RK_Enabled,            // Remark enabled via '-Rgroup'.
+    RK_EnabledEverything,  // Remark enabled via '-Reverything'.
+    RK_Disabled,           // Remark disabled via '-Rno-group'.
+    RK_DisabledEverything, // Remark disabled via '-Rno-everything'.
+    RK_WithPattern,        // Remark pattern specified via '-Rgroup=regexp'.
+  };
+
+  /// Optimization remark with an optional regular expression pattern.
+  struct OptRemark {
+    RemarkKind Kind = RemarkKind::RK_Missing;
+    std::string Pattern;
+    std::shared_ptr<llvm::Regex> Regex;
+
+    /// By default, optimization remark is missing.
+    OptRemark() = default;
+
+    /// Returns true iff the optimization remark holds a valid regular
+    /// expression.
+    bool hasValidPattern() const { return Regex != nullptr; }
+
+    /// Matches the given string against the regex, if there is some.
+    bool patternMatches(llvm::StringRef string) const {
+      return hasValidPattern() && Regex->match(string);
+    }
+  };
+
+  /// Selected optimizations for which we should enable optimization remarks.
+  /// Transformation passes whose name matches the contained (optional) regular
+  /// expression (and support this feature), will emit a diagnostic whenever
+  /// they perform a transformation.
+  OptRemark OptimizationRemark;
+
+  /// Selected optimizations for which we should enable missed optimization
+  /// remarks. Transformation passes whose name matches the contained (optional)
+  /// regular expression (and support this feature), will emit a diagnostic
+  /// whenever they tried but failed to perform a transformation.
+  OptRemark OptimizationRemarkMissed;
+
+  /// Selected optimizations for which we should enable optimization analyses.
+  /// Transformation passes whose name matches the contained (optional) regular
+  /// expression (and support this feature), will emit a diagnostic whenever
+  /// they want to explain why they decided to apply or not apply a given
+  /// transformation.
+  OptRemark OptimizationRemarkAnalysis;
+
   // Define accessors/mutators for code generation options of enumeration type.
 #define CODEGENOPT(Name, Bits, Default)
 #define ENUM_CODEGENOPT(Name, Type, Bits, Default)                             \
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to