diff --git a/flang/include/flang/Frontend/CodeGenOptions.h b/flang/include/flang/Frontend/CodeGenOptions.h --- a/flang/include/flang/Frontend/CodeGenOptions.h +++ b/flang/include/flang/Frontend/CodeGenOptions.h @@ -69,6 +69,57 @@ /// 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 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 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -153,6 +153,38 @@ 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(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 @@ 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 @@ 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 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -919,6 +919,108 @@ 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(&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(di)); + break; + case llvm::DK_OptimizationRemarkMissed: + optimizationRemarkHandler(llvm::cast(di)); + break; + case llvm::DK_OptimizationRemarkAnalysis: + optimizationRemarkHandler( + llvm::cast(di)); + break; + default: + break; + } + return true; + } +}; + void CodeGenAction::embedOffloadObjects() { CompilerInstance &ci = this->getInstance(); const auto &cgOpts = ci.getInvocation().getCodeGenOpts(); @@ -1029,6 +1131,11 @@ if (!codeGenOpts.OffloadObjects.empty()) embedOffloadObjects(); + BackendRemarkConsumer remarkConsumer(diags, codeGenOpts); + + llvmModule->getContext().setDiagnosticHandler( + std::make_unique(remarkConsumer)); + // write optimization-record llvm::Expected> optRecordFileOrErr = setupLLVMOptimizationRemarks( diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- 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 @@ 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 diags; + const llvm::IntrusiveRefCntPtr 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 @@ // Honor color diagnostics. flang->getDiagnosticOpts().ShowColors = flang->getFrontendOpts().showColors; + updateDiagEngineForOptRemarks(flang->getDiagnostics(), + flang->getDiagnosticOpts()); + // Create and execute the frontend action. std::unique_ptr 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 --- /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