victorkingi updated this revision to Diff 549887.
victorkingi added a comment.
added comment
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,52 @@
+! 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=CHECK-REMARKS
+! RUN: %flang_fc1 %s -O1 -Rpass -Rno-pass -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-REMARKS
+
+! Check "unknown remark option" warning
+! RUN: %flang %s -O1 -R 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS-WARN
+
+! Check "unknown remark option" warning with suggestion
+! RUN: %flang %s -O1 -Rpas 2>&1 | FileCheck %s --check-prefix=CHECK-WARN-SUGGEST
+
+! 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=CHECK-NO-REMARKS
+! RUN: %flang %s -O1 -Rno-pass-missed 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-NO-REMARKS
+! RUN: %flang %s -O1 -Rno-pass-analysis 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-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=CHECK-REMARKS-MISSED
+
+! Check full -Rpass-analysis message is emitted
+! RUN: %flang %s -O1 -Rpass-analysis 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS-ANALYSIS
+
+! CHECK: optimization-remark.f90:49:5: remark: Loop deleted because it is invariant [-Rpass=loop-delete]
+! CHECK-REMARKS-MISSED: optimization-remark.f90:44:5: remark: loop not vectorized [-Rpass-missed=loop-vectorize]
+! CHECK-REMARKS-ANALYSIS: optimization-remark.f90:44:5: remark: loop not vectorized: instruction cannot be vectorized [-Rpass-analysis=loop-vectorize]
+! CHECK-REMARKS: remark:
+! CHECK-NO-REMARKS-NOT: remark:
+
+! CHECK-REMARKS-WARN: warning: unknown remark option '-R' [-Wunknown-warning-option]
+! CHECK-WARN-SUGGEST: warning: unknown remark option '-Rpas'; did you mean '-Rpass'? [-Wunknown-warning-option]
+
+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
\ No newline at end of file
Index: flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -27,6 +27,7 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/BuryPointer.h"
#include "llvm/Support/CommandLine.h"
+#include <clang/Basic/DiagnosticFrontend.h>
namespace Fortran::frontend {
@@ -100,6 +101,61 @@
llvm_unreachable("Invalid program action!");
}
+// Emit a warning and typo hint for unknown warning opts
+static void emitUnknownDiagWarning(clang::DiagnosticsEngine &diags,
+ clang::diag::Flavor flavor,
+ llvm::StringRef prefix,
+ llvm::StringRef opt) {
+ llvm::StringRef suggestion =
+ clang::DiagnosticIDs::getNearestOption(flavor, opt);
+ diags.Report(clang::diag::warn_unknown_diag_option)
+ << (flavor == clang::diag::Flavor::WarningOrError ? 0 : 1)
+ << (prefix.str() += std::string(opt)) << !suggestion.empty()
+ << (prefix.str() += std::string(suggestion));
+}
+
+void processWarningOptions(clang::DiagnosticsEngine &diags,
+ const clang::DiagnosticOptions &opts,
+ bool reportDiags = true) {
+
+ llvm::SmallVector<clang::diag::kind, 10> _diags;
+ const llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagIDs =
+ diags.getDiagnosticIDs();
+ // We parse the warning options twice. The first pass sets diagnostic state,
+ // while the second pass reports warnings/errors. This has the effect that
+ // we follow the more canonical "last option wins" paradigm when there are
+ // conflicting options.
+ for (unsigned report = 0, reportEnd = 2; report != reportEnd; ++report) {
+ bool setDiagnostic = (report == 0);
+
+ // If we've set the diagnostic state and are not reporting diagnostics then
+ // we're done.
+ if (!setDiagnostic && !reportDiags)
+ break;
+
+ for (unsigned i = 0, e = opts.Remarks.size(); i != e; ++i) {
+ llvm::StringRef opt = 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 = !opt.startswith("no-");
+ if (!isPositive)
+ opt = opt.substr(3);
+
+ if (report) {
+ if (diagIDs->getDiagnosticsInGroup(flavor, opt, _diags))
+ emitUnknownDiagWarning(diags, flavor, isPositive ? "-R" : "-Rno-",
+ opt);
+ } else {
+ diags.setSeverityForGroup(flavor, opt,
+ isPositive ? clang::diag::Severity::Remark
+ : clang::diag::Severity::Ignored);
+ }
+ }
+ }
+}
+
bool executeCompilerInvocation(CompilerInstance *flang) {
// Honor -help.
if (flang->getFrontendOpts().showHelp) {
@@ -166,6 +222,8 @@
// Honor color diagnostics.
flang->getDiagnosticOpts().ShowColors = flang->getFrontendOpts().showColors;
+ processWarningOptions(flang->getDiagnostics(), flang->getDiagnosticOpts());
+
// Create and execute the frontend action.
std::unique_ptr<FrontendAction> act(createFrontendAction(*flang));
if (!act)
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,23 @@
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) {
+ llvm::StringRef opt =
+ clang::DiagnosticIDs::getWarningOptionForDiag(info.getID());
+ if (!opt.empty()) {
+ os << " [" << (level == clang::DiagnosticsEngine::Remark ? "-R" : "-W")
+ << opt;
+ llvm::StringRef optValue = info.getDiags()->getFlagValue();
+ if (!optValue.empty())
+ os << "=" << optValue;
+ os << ']';
+ }
+}
+
void TextDiagnosticPrinter::HandleDiagnostic(
clang::DiagnosticsEngine::Level level, const clang::Diagnostic &info) {
// Default implementation (Warnings/errors count).
@@ -40,6 +61,7 @@
info.FormatDiagnostic(outStr);
llvm::raw_svector_ostream diagMessageStream(outStr);
+ printDiagnosticOptions(diagMessageStream, level, info, *diagOpts);
if (!prefix.empty())
os << prefix << ": ";
@@ -48,12 +70,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 BackendRemarkConsumer
+ std::string diagMsg = std::string(diagMessageStream.str());
+ std::string delimiter = ";;";
+
+ size_t pos = 0;
+ std::vector<std::string> tokens;
+ while ((pos = diagMsg.find(delimiter)) != std::string::npos) {
+ tokens.push_back(diagMsg.substr(0, pos));
+ diagMsg.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, diagMsg,
+ 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"
@@ -919,6 +920,115 @@
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 &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);
+
+ return;
+ }
+
+ 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);
+
+ return;
+ }
+
+ assert(d.isAnalysis() && "Unknown remark type");
+
+ auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(&d);
+ if (!ora)
+ return;
+
+ bool 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();
@@ -1029,6 +1139,11 @@
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(
Index: flang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- flang/lib/Frontend/CompilerInvocation.cpp
+++ flang/lib/Frontend/CompilerInvocation.cpp
@@ -153,6 +153,36 @@
return true;
}
+static CodeGenOptions::OptRemark
+parseOptimizationRemark(clang::DiagnosticsEngine &diags,
+ llvm::opt::ArgList &args, llvm::opt::OptSpecifier optEq,
+ llvm::StringRef name) {
+ 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 == name)
+ result.Kind = CodeGenOptions::RemarkKind::RK_Enabled;
+ else if (value.split('-') == std::make_pair(llvm::StringRef("no"), name))
+ result.Kind = CodeGenOptions::RemarkKind::RK_Disabled;
+ else
+ continue;
+
+ if (result.Kind == CodeGenOptions::RemarkKind::RK_Disabled) {
+ result.Pattern = "";
+ result.Regex = nullptr;
+ } else {
+ result.Pattern = ".*";
+ result.Regex = std::make_shared<llvm::Regex>(result.Pattern);
+ }
+ }
+ }
+
+ return result;
+}
+
static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
@@ -194,13 +224,45 @@
args.getLastArg(clang::driver::options::OPT_opt_record_file))
opts.OptRecordFile = a->getValue();
+ // Specifies what passes to include. 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();
+
+ // 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();
- if (const llvm::opt::Arg *a =
- args.getLastArg(clang::driver::options::OPT_opt_record_passes))
- opts.OptRecordPasses = a->getValue();
+ // Specify which successful passes should be reported using a regex.
+ opts.OptimizationRemark = parseOptimizationRemark(
+ diags, args, clang::driver::options::OPT_Rpass_EQ, "pass");
+
+ // Specify which missed passes should be reported using a regex.
+ opts.OptimizationRemarkMissed = parseOptimizationRemark(
+ diags, args, clang::driver::options::OPT_Rpass_missed_EQ, "pass-missed");
+
+ // Specify which passes, with additional information,
+ // should be reported using a regex.
+ opts.OptimizationRemarkAnalysis = parseOptimizationRemark(
+ diags, args, clang::driver::options::OPT_Rpass_analysis_EQ,
+ "pass-analysis");
+
+ if (opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo) {
+ // If the user requested a flag that requires source locations available in
+ // the backend, make sure that the backend tracks source location
+ // information.
+ bool needLocTracking = !opts.OptRecordFile.empty() ||
+ !opts.OptRecordPasses.empty() ||
+ !opts.OptRecordFormat.empty() ||
+ opts.OptimizationRemark.hasValidPattern() ||
+ opts.OptimizationRemarkMissed.hasValidPattern() ||
+ opts.OptimizationRemarkAnalysis.hasValidPattern();
+
+ if (needLocTracking)
+ opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly);
+ }
if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ))
opts.SaveTempsDir = a->getValue();
@@ -959,6 +1021,13 @@
res.loweringOpts.setNoPPCNativeVecElemOrder(true);
}
+ // add the remark option requested i.e. pass, pass-missed or pass-analysis.
+ // This will be used later during processing warnings and remarks to give
+ // messages specific to a remark argument. That happens in
+ // processWarningOptions 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);
Index: flang/include/flang/Frontend/CodeGenOptions.h
===================================================================
--- flang/include/flang/Frontend/CodeGenOptions.h
+++ flang/include/flang/Frontend/CodeGenOptions.h
@@ -69,6 +69,58 @@
/// 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'.
+ 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);
+ }
+ };
+
+ // 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) \
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits