[flang-commits] [flang] 91989c6 - [Flang][Driver] Implement OPT_R_Joined options

Victor Kingi via flang-commits flang-commits at lists.llvm.org
Wed Aug 23 03:20:19 PDT 2023


Author: Victor Kingi
Date: 2023-08-23T10:19:56Z
New Revision: 91989c67483c37bbcee4404eea789e39dd3470d9

URL: https://github.com/llvm/llvm-project/commit/91989c67483c37bbcee4404eea789e39dd3470d9
DIFF: https://github.com/llvm/llvm-project/commit/91989c67483c37bbcee4404eea789e39dd3470d9.diff

LOG: [Flang][Driver] Implement OPT_R_Joined options

Add a BackendRemarkConsumer class, responsible for handling diagnostics
received from LLVM. The diagnostics being information on middle and
backend passes used or not used.

Clang by default has all remarks ignored but manually sets the severity of
`R_Group` to visible(`clang::diag::clang::Severity::Remark`). This patch does
the same for Flang.

Depends on D157410. That patch adds the R family of options to
`FlangOption` and `FC1Option` in
`clang/include/clang/Driver/Options.td`

Reviewed By: awarzynski

Differential Revision: https://reviews.llvm.org/D158174

Added: 
    flang/test/Driver/optimization-remark.f90

Modified: 
    flang/include/flang/Frontend/CodeGenOptions.h
    flang/lib/Frontend/CompilerInvocation.cpp
    flang/lib/Frontend/FrontendActions.cpp
    flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Frontend/CodeGenOptions.h b/flang/include/flang/Frontend/CodeGenOptions.h
index c56886890f2857..0bc7758a678016 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.h
+++ b/flang/include/flang/Frontend/CodeGenOptions.h
@@ -69,6 +69,57 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// The format used for serializing remarks (default: YAML)
   std::string OptRecordFormat;
 
+  // The RemarkKind enum class and OptRemark struct are identical to what Clang
+  // has
+  // TODO: Share with clang instead of re-implementing here
+  enum class RemarkKind {
+    RK_Missing,  // Remark argument not present on the command line.
+    RK_Enabled,  // Remark enabled via '-Rgroup', i.e. -Rpass, -Rpass-missed,
+                 // -Rpass-analysis
+    RK_Disabled, // Remark disabled via '-Rno-group', i.e. -Rno-pass,
+                 // -Rno-pass-missed, -Rno-pass-analysis.
+  };
+
+  /// 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);
+    }
+  };
+
+  // The OptRemark fields provided here are identical to Clang.
+
+  /// 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)                             \

diff  --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index ecafa4e6454147..f2a7ffdd135edb 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -153,6 +153,38 @@ static bool parseDebugArgs(Fortran::frontend::CodeGenOptions &opts,
   return true;
 }
 
+// Generate an OptRemark object containing info on if the -Rgroup
+// specified is enabled or not.
+static CodeGenOptions::OptRemark
+parseOptimizationRemark(llvm::opt::ArgList &args,
+                        llvm::StringRef remarkOptName) {
+  assert((remarkOptName == "pass" || remarkOptName == "pass-missed" ||
+          remarkOptName == "pass-analysis") &&
+         "Unknown group name provided.");
+  CodeGenOptions::OptRemark result;
+
+  for (llvm::opt::Arg *a : args) {
+    if (a->getOption().matches(clang::driver::options::OPT_R_Joined)) {
+      llvm::StringRef value = a->getValue();
+
+      if (value == remarkOptName) {
+        result.Kind = CodeGenOptions::RemarkKind::RK_Enabled;
+        // Enable everything
+        result.Pattern = ".*";
+        result.Regex = std::make_shared<llvm::Regex>(result.Pattern);
+
+      } else if (value.split('-') ==
+                 std::make_pair(llvm::StringRef("no"), remarkOptName)) {
+        result.Kind = CodeGenOptions::RemarkKind::RK_Disabled;
+        // Disable everything
+        result.Pattern = "";
+        result.Regex = nullptr;
+      }
+    }
+  }
+  return result;
+}
+
 static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
                              llvm::opt::ArgList &args,
                              clang::DiagnosticsEngine &diags) {
@@ -194,14 +226,30 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
           args.getLastArg(clang::driver::options::OPT_opt_record_file))
     opts.OptRecordFile = a->getValue();
 
+  // Optimization file format. Defaults to yaml
   if (const llvm::opt::Arg *a =
           args.getLastArg(clang::driver::options::OPT_opt_record_format))
     opts.OptRecordFormat = a->getValue();
 
+  // Specifies, using a regex, which successful optimization passes(middle and
+  // backend), to include in the final optimization record file generated. If
+  // not provided -fsave-optimization-record will include all passes.
   if (const llvm::opt::Arg *a =
           args.getLastArg(clang::driver::options::OPT_opt_record_passes))
     opts.OptRecordPasses = a->getValue();
 
+  // Create OptRemark that allows printing of all successful optimization
+  // passes applied.
+  opts.OptimizationRemark = parseOptimizationRemark(args, "pass");
+
+  // Create OptRemark that allows all missed optimization passes to be printed.
+  opts.OptimizationRemarkMissed = parseOptimizationRemark(args, "pass-missed");
+
+  // Create OptRemark that allows all optimization decisions made by LLVM
+  // to be printed.
+  opts.OptimizationRemarkAnalysis =
+      parseOptimizationRemark(args, "pass-analysis");
+
   if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ))
     opts.SaveTempsDir = a->getValue();
 
@@ -959,6 +1007,12 @@ bool CompilerInvocation::createFromArgs(
     res.loweringOpts.setNoPPCNativeVecElemOrder(true);
   }
 
+  // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
+  // -Rpass-analysis. This will be used later when processing and outputting the
+  // remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
+  for (auto *a : args.filtered(clang::driver::options::OPT_R_Group))
+    res.getDiagnosticOpts().Remarks.push_back(a->getValue());
+
   success &= parseFrontendArgs(res.getFrontendOpts(), args, diags);
   parseTargetArgs(res.getTargetOpts(), args);
   parsePreprocessorArgs(res.getPreprocessorOpts(), args);

diff  --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index d2c6e518a0500d..9e5efb8d6ed688 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -919,6 +919,108 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
   mpm.run(*llvmModule, mam);
 }
 
+// This class handles optimization remark messages requested if
+// any of -Rpass, -Rpass-analysis or -Rpass-missed flags were provided
+class BackendRemarkConsumer : public llvm::DiagnosticHandler {
+
+  const CodeGenOptions &codeGenOpts;
+  clang::DiagnosticsEngine &diags;
+
+public:
+  BackendRemarkConsumer(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 &diagInfo,
+                          unsigned diagID) {
+    // We only support warnings and remarks.
+    assert(diagInfo.getSeverity() == llvm::DS_Remark ||
+           diagInfo.getSeverity() == llvm::DS_Warning);
+
+    std::string msg;
+    llvm::raw_string_ostream msgStream(msg);
+    msgStream << diagInfo.getMsg();
+
+    // Emit message.
+    diags.Report(diagID) << clang::AddFlagValue(diagInfo.getPassName())
+                         << msgStream.str();
+  }
+
+  void optimizationRemarkHandler(
+      const llvm::DiagnosticInfoOptimizationBase &diagInfo) {
+    auto passName = diagInfo.getPassName();
+    if (diagInfo.isPassed()) {
+      if (codeGenOpts.OptimizationRemark.patternMatches(passName))
+        // Optimization remarks are active only if the -Rpass flag has a regular
+        // expression that matches the name of the pass name in \p d.
+        emitOptimizationMessage(
+            diagInfo, clang::diag::remark_fe_backend_optimization_remark);
+
+      return;
+    }
+
+    if (diagInfo.isMissed()) {
+      if (codeGenOpts.OptimizationRemarkMissed.patternMatches(passName))
+        // 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.
+        emitOptimizationMessage(
+            diagInfo,
+            clang::diag::remark_fe_backend_optimization_remark_missed);
+
+      return;
+    }
+
+    assert(diagInfo.isAnalysis() && "Unknown remark type");
+
+    bool shouldAlwaysPrint = false;
+    auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(&diagInfo);
+    if (ora)
+      shouldAlwaysPrint = ora->shouldAlwaysPrint();
+
+    if (shouldAlwaysPrint ||
+        codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName))
+      emitOptimizationMessage(
+          diagInfo,
+          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();
@@ -1029,6 +1131,11 @@ void CodeGenAction::executeAction() {
   if (!codeGenOpts.OffloadObjects.empty())
     embedOffloadObjects();
 
+  BackendRemarkConsumer remarkConsumer(diags, codeGenOpts);
+
+  llvmModule->getContext().setDiagnosticHandler(
+      std::make_unique<BackendRemarkConsumer>(remarkConsumer));
+
   // write optimization-record
   llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
       setupLLVMOptimizationRemarks(

diff  --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 883f109bcda282..e1de344825f88a 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -22,6 +22,7 @@
 #include "mlir/IR/AsmState.h"
 #include "mlir/IR/MLIRContext.h"
 #include "mlir/Pass/PassManager.h"
+#include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Driver/Options.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Option/Option.h"
@@ -100,6 +101,34 @@ createFrontendAction(CompilerInstance &ci) {
   llvm_unreachable("Invalid program action!");
 }
 
+// Remarks are ignored by default in Diagnostic.td, hence, we have to
+// enable them here before execution. Clang follows same idea using
+// ProcessWarningOptions in Warnings.cpp
+// This function is also responsible for emitting early warnings for
+// invalid -R options.
+static void
+updateDiagEngineForOptRemarks(clang::DiagnosticsEngine &diagsEng,
+                              const clang::DiagnosticOptions &opts) {
+  llvm::SmallVector<clang::diag::kind, 10> diags;
+  const llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagIDs =
+      diagsEng.getDiagnosticIDs();
+
+  for (unsigned i = 0; i < opts.Remarks.size(); i++) {
+    llvm::StringRef remarkOpt = opts.Remarks[i];
+    const auto flavor = clang::diag::Flavor::Remark;
+
+    // Check to see if this opt starts with "no-", if so, this is a
+    // negative form of the option.
+    bool isPositive = !remarkOpt.startswith("no-");
+    if (!isPositive)
+      remarkOpt = remarkOpt.substr(3);
+
+    diagsEng.setSeverityForGroup(flavor, remarkOpt,
+                                 isPositive ? clang::diag::Severity::Remark
+                                            : clang::diag::Severity::Ignored);
+  }
+}
+
 bool executeCompilerInvocation(CompilerInstance *flang) {
   // Honor -help.
   if (flang->getFrontendOpts().showHelp) {
@@ -165,6 +194,9 @@ bool executeCompilerInvocation(CompilerInstance *flang) {
   // Honor color diagnostics.
   flang->getDiagnosticOpts().ShowColors = flang->getFrontendOpts().showColors;
 
+  updateDiagEngineForOptRemarks(flang->getDiagnostics(),
+                                flang->getDiagnosticOpts());
+
   // Create and execute the frontend action.
   std::unique_ptr<FrontendAction> act(createFrontendAction(*flang));
   if (!act)

diff  --git a/flang/test/Driver/optimization-remark.f90 b/flang/test/Driver/optimization-remark.f90
new file mode 100644
index 00000000000000..3e121489bf754a
--- /dev/null
+++ b/flang/test/Driver/optimization-remark.f90
@@ -0,0 +1,46 @@
+! 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 -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=REMARKS
+! RUN: %flang_fc1 %s -O1 -Rpass -Rno-pass -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=NO-REMARKS
+
+! Check -Rno-pass, -Rno-pass-analysis, -Rno-pass-missed nothing emitted
+! RUN: %flang %s -O1 -Rno-pass 2>&1 | FileCheck %s --allow-empty --check-prefix=NO-REMARKS
+! RUN: %flang %s -O1 -Rno-pass-missed 2>&1 | FileCheck %s --allow-empty --check-prefix=NO-REMARKS
+! RUN: %flang %s -O1 -Rno-pass-analysis 2>&1 | FileCheck %s --allow-empty --check-prefix=NO-REMARKS
+
+! Check full -Rpass message is emitted
+! RUN: %flang %s -O1 -Rpass 2>&1 | FileCheck %s
+
+! Check full -Rpass-missed message is emitted
+! RUN: %flang %s -O1 -Rpass-missed 2>&1 | FileCheck %s --check-prefix=REMARKS-MISSED
+
+! Check full -Rpass-analysis message is emitted
+! RUN: %flang %s -O1 -Rpass-analysis 2>&1 | FileCheck %s --check-prefix=REMARKS-ANALYSIS
+
+! CHECK: remark: Loop deleted because it is invariant
+! REMARKS-MISSED: remark: loop not vectorized
+! REMARKS-MISSED-NOT: instruction cannot be vectorized
+! REMARKS-ANALYSIS: remark: loop not vectorized: instruction cannot be vectorized
+! REMARKS-ANALYSIS-NOT: will not be inlined into {{.*}} because its definition is unavailable
+
+! REMARKS: remark:
+! NO-REMARKS-NOT: remark:
+
+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


        


More information about the flang-commits mailing list