[Flang] Add support for fsave-optimization-record

Add support for generating and saving the optimization record.
Optimization record lists the optimizations performed by LLVM.

This patch enables the flag in Flang. Clang handles this functionality
using the BackendConsumer which Flang doesn't have, hence, was
implemented in CodeGenAction::executeAction

FlangOption added to all variants of fsave-optimization-record in
clang/include/clang/Driver/Options.td . Clang handles it the
same way.

opt_record_file, opt_record_passes and opt_record_format flags
in Options.td were moved out of the group [CC1Option, NoDriverOption]
to allow flang -fc1 support.

The renderRemarksOptions and willEmitRemarks functions in
clang/lib/Driver/ToolChains/Flang.cpp follow same syntax as clang.
In flang/lib/Frontend/CompilerInvocation.cpp we update the field
OptRecordFile with the provided optimization file value. Clang
doesn't do this as it processes the Options.td, mapping the
OptRecordFile earlier on.

Reviewed By: awarzynski, tblah

Differential Revision: https://reviews.llvm.org/D155452
This commit is contained in:
Victor Kingi 2023-07-27 21:57:54 +00:00 committed by Kiran Chandramohan
parent 1478d4dc8d
commit f04ccadf35
8 changed files with 225 additions and 25 deletions

View File

@ -3084,18 +3084,18 @@ def foperator_arrow_depth_EQ : Joined<["-"], "foperator-arrow-depth=">, Group<f_
HelpText<"Maximum number of 'operator->'s to call for a member access">,
MarshallingInfoInt<LangOpts<"ArrowDepth">, "256">;
def fsave_optimization_record : Flag<["-"], "fsave-optimization-record">,
def fsave_optimization_record : Flag<["-"], "fsave-optimization-record">, Flags<[FlangOption]>,
Group<f_Group>, HelpText<"Generate a YAML optimization record file">;
def fsave_optimization_record_EQ : Joined<["-"], "fsave-optimization-record=">,
def fsave_optimization_record_EQ : Joined<["-"], "fsave-optimization-record=">, Flags<[FlangOption]>,
Group<f_Group>, HelpText<"Generate an optimization record file in a specific format">,
MetaVarName<"<format>">;
def fno_save_optimization_record : Flag<["-"], "fno-save-optimization-record">,
Group<f_Group>, Flags<[NoArgumentUnused]>;
def foptimization_record_file_EQ : Joined<["-"], "foptimization-record-file=">,
Group<f_Group>, Flags<[FlangOption, NoArgumentUnused]>;
def foptimization_record_file_EQ : Joined<["-"], "foptimization-record-file=">, Flags<[FlangOption]>,
Group<f_Group>,
HelpText<"Specify the output name of the file containing the optimization remarks. Implies -fsave-optimization-record. On Darwin platforms, this cannot be used with multiple -arch <arch> options.">,
MetaVarName<"<file>">;
def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=">,
def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=">, Flags<[FlangOption]>,
Group<f_Group>,
HelpText<"Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)">,
MetaVarName<"<regex>">;
@ -6390,14 +6390,6 @@ def arcmt_action_EQ : Joined<["-"], "arcmt-action=">, Flags<[CC1Option, NoDriver
NormalizedValues<["ARCMT_Check", "ARCMT_Modify", "ARCMT_Migrate"]>,
MarshallingInfoEnum<FrontendOpts<"ARCMTAction">, "ARCMT_None">;
def opt_record_file : Separate<["-"], "opt-record-file">,
HelpText<"File name to use for YAML optimization record output">,
MarshallingInfoString<CodeGenOpts<"OptRecordFile">>;
def opt_record_passes : Separate<["-"], "opt-record-passes">,
HelpText<"Only record remark information for passes whose names match the given regular expression">;
def opt_record_format : Separate<["-"], "opt-record-format">,
HelpText<"The format used for serializing remarks (default: YAML)">;
def print_stats : Flag<["-"], "print-stats">,
HelpText<"Print performance metrics and statistics">,
MarshallingInfoFlag<FrontendOpts<"ShowStats">>;
@ -6838,6 +6830,14 @@ defm debug_pass_manager : BoolOption<"f", "debug-pass-manager",
PosFlag<SetTrue, [], "Prints debug information for the new pass manager">,
NegFlag<SetFalse, [], "Disables debug printing for the new pass manager">>;
def opt_record_file : Separate<["-"], "opt-record-file">,
HelpText<"File name to use for YAML optimization record output">,
MarshallingInfoString<CodeGenOpts<"OptRecordFile">>;
def opt_record_passes : Separate<["-"], "opt-record-passes">,
HelpText<"Only record remark information for passes whose names match the given regular expression">;
def opt_record_format : Separate<["-"], "opt-record-format">,
HelpText<"The format used for serializing remarks (default: YAML)">;
} // let Flags = [CC1Option, FC1Option, NoDriverOption]
//===----------------------------------------------------------------------===//

View File

@ -12,6 +12,8 @@
#include "clang/Driver/Options.h"
#include "llvm/Frontend/Debug/Options.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include <cassert>
@ -387,6 +389,50 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
CmdArgs.push_back("-freciprocal-math");
}
static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs,
const InputInfo &Input) {
StringRef Format = "yaml";
if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ))
Format = A->getValue();
CmdArgs.push_back("-opt-record-file");
const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ);
if (A) {
CmdArgs.push_back(A->getValue());
} else {
SmallString<128> F;
if (Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)) {
if (Arg *FinalOutput = Args.getLastArg(options::OPT_o))
F = FinalOutput->getValue();
}
if (F.empty()) {
// Use the input filename.
F = llvm::sys::path::stem(Input.getBaseInput());
}
SmallString<32> Extension;
Extension += "opt.";
Extension += Format;
llvm::sys::path::replace_extension(F, Extension);
CmdArgs.push_back(Args.MakeArgString(F));
}
if (const Arg *A =
Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) {
CmdArgs.push_back("-opt-record-passes");
CmdArgs.push_back(A->getValue());
}
if (!Format.empty()) {
CmdArgs.push_back("-opt-record-format");
CmdArgs.push_back(Format.data());
}
}
void Flang::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output, const InputInfoList &Inputs,
const ArgList &Args, const char *LinkingOutput) const {
@ -471,6 +517,10 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
// Add Codegen options
addCodegenOptions(Args, CmdArgs);
// Remarks can be enabled with any of the `-f.*optimization-record.*` flags.
if (willEmitRemarks(Args))
renderRemarksOptions(Args, CmdArgs, Input);
// Add other compile options
addOtherOptions(Args, CmdArgs);

View File

@ -58,6 +58,17 @@ public:
/// The directory where temp files are stored if specified by -save-temps
std::optional<std::string> SaveTempsDir;
/// The name of the file to which the backend should save YAML optimization
/// records.
std::string OptRecordFile;
/// The regex that filters the passes that should be saved to the optimization
/// records.
std::string OptRecordPasses;
/// The format used for serializing remarks (default: YAML)
std::string OptRecordFormat;
// Define accessors/mutators for code generation options of enumeration type.
#define CODEGENOPT(Name, Bits, Default)
#define ENUM_CODEGENOPT(Name, Type, Bits, Default) \

View File

@ -163,13 +163,12 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
opts.DebugPassManager = 1;
if (args.hasFlag(clang::driver::options::OPT_fstack_arrays,
clang::driver::options::OPT_fno_stack_arrays, false)) {
clang::driver::options::OPT_fno_stack_arrays, false))
opts.StackArrays = 1;
}
if (args.hasFlag(clang::driver::options::OPT_floop_versioning,
clang::driver::options::OPT_fno_loop_versioning, false)) {
clang::driver::options::OPT_fno_loop_versioning, false))
opts.LoopVersioning = 1;
}
for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ))
opts.LLVMPassPlugins.push_back(a->getValue());
@ -190,6 +189,19 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
opts.PrepareForThinLTO = true;
}
// -f[no-]save-optimization-record[=<format>]
if (const llvm::opt::Arg *a =
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();
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_opt_record_passes))
opts.OptRecordPasses = a->getValue();
if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ))
opts.SaveTempsDir = a->getValue();

View File

@ -42,11 +42,13 @@
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
@ -55,6 +57,7 @@
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
@ -939,9 +942,32 @@ void CodeGenAction::embedOffloadObjects() {
}
}
static void reportOptRecordError(llvm::Error e, clang::DiagnosticsEngine &diags,
const CodeGenOptions &codeGenOpts) {
handleAllErrors(
std::move(e),
[&](const llvm::LLVMRemarkSetupFileError &e) {
diags.Report(clang::diag::err_cannot_open_file)
<< codeGenOpts.OptRecordFile << e.message();
},
[&](const llvm::LLVMRemarkSetupPatternError &e) {
diags.Report(clang::diag::err_drv_optimization_remark_pattern)
<< e.message() << codeGenOpts.OptRecordPasses;
},
[&](const llvm::LLVMRemarkSetupFormatError &e) {
diags.Report(clang::diag::err_drv_optimization_remark_format)
<< codeGenOpts.OptRecordFormat;
});
}
void CodeGenAction::executeAction() {
CompilerInstance &ci = this->getInstance();
clang::DiagnosticsEngine &diags = ci.getDiagnostics();
const CodeGenOptions &codeGenOpts = ci.getInvocation().getCodeGenOpts();
Fortran::lower::LoweringOptions &loweringOpts =
ci.getInvocation().getLoweringOpts();
// If the output stream is a file, generate it and define the corresponding
// output stream. If a pre-defined output stream is available, we will use
// that instead.
@ -959,15 +985,15 @@ void CodeGenAction::executeAction() {
os = getOutputStream(ci, getCurrentFileOrBufferName(), action);
if (!os) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.getDiagnostics().Report(diagID);
diags.Report(diagID);
return;
}
}
if (action == BackendActionTy::Backend_EmitFIR) {
if (ci.getInvocation().getLoweringOpts().getLowerToHighLevelFIR()) {
if (loweringOpts.getLowerToHighLevelFIR()) {
lowerHLFIRToFIR();
}
mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
@ -975,7 +1001,7 @@ void CodeGenAction::executeAction() {
}
if (action == BackendActionTy::Backend_EmitHLFIR) {
assert(ci.getInvocation().getLoweringOpts().getLowerToHighLevelFIR() &&
assert(loweringOpts.getLowerToHighLevelFIR() &&
"Lowering must have been configured to emit HLFIR");
mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
return;
@ -994,8 +1020,7 @@ void CodeGenAction::executeAction() {
const std::string &theTriple = tm->getTargetTriple().str();
if (llvmModule->getTargetTriple() != theTriple) {
ci.getDiagnostics().Report(clang::diag::warn_fe_override_module)
<< theTriple;
diags.Report(clang::diag::warn_fe_override_module) << theTriple;
}
// Always set the triple and data layout, to make sure they match and are set.
@ -1005,9 +1030,30 @@ void CodeGenAction::executeAction() {
llvmModule->setDataLayout(tm->createDataLayout());
// Embed offload objects specified with -fembed-offload-object
if (!ci.getInvocation().getCodeGenOpts().OffloadObjects.empty())
if (!codeGenOpts.OffloadObjects.empty())
embedOffloadObjects();
// write optimization-record
llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
setupLLVMOptimizationRemarks(
llvmModule->getContext(), codeGenOpts.OptRecordFile,
codeGenOpts.OptRecordPasses, codeGenOpts.OptRecordFormat,
/*DiagnosticsWithHotness=*/false,
/*DiagnosticsHotnessThreshold=*/0);
if (llvm::Error e = optRecordFileOrErr.takeError()) {
reportOptRecordError(std::move(e), diags, codeGenOpts);
return;
}
std::unique_ptr<llvm::ToolOutputFile> optRecordFile =
std::move(*optRecordFileOrErr);
if (optRecordFile) {
optRecordFile->keep();
optRecordFile->os().flush();
}
// Run LLVM's middle-end (i.e. the optimizer).
runOptimizationPipeline(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
@ -1026,7 +1072,7 @@ void CodeGenAction::executeAction() {
if (action == BackendActionTy::Backend_EmitAssembly ||
action == BackendActionTy::Backend_EmitObj) {
generateMachineCodeOrAssemblyImpl(
ci.getDiagnostics(), *tm, action, *llvmModule,
diags, *tm, action, *llvmModule,
ci.isOutputStreamNull() ? *os : ci.getOutputStream());
return;
}

View File

@ -58,8 +58,16 @@
! CHECK-NEXT: -fopenmp-version=<value>
! CHECK-NEXT: Set OpenMP version (e.g. 45 for OpenMP 4.5, 50 for OpenMP 5.0). Default value is 50 for Clang and 11 for Flang
! CHECK-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code.
! CHECK-NEXT: -foptimization-record-file=<file>
! CHECK-NEXT: Specify the output name of the file containing the optimization remarks. Implies -fsave-optimization-record. On Darwin platforms, this cannot be used with multiple -arch <arch> options.
! CHECK-NEXT: -foptimization-record-passes=<regex>
! CHECK-NEXT: Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)
! CHECK-NEXT: -fpass-plugin=<dsopath> Load pass plugin from a dynamic shared object file (only with new pass manager).
! CHECK-NEXT: -freciprocal-math Allow division operations to be reassociated
! CHECK-NEXT: -fsave-optimization-record=<format>
! CHECK-NEXT: Generate an optimization record file in a specific format
! CHECK-NEXT: -fsave-optimization-record
! CHECK-NEXT: Generate a YAML optimization record file
! CHECK-NEXT: -fstack-arrays Attempt to allocate array temporaries on the stack, no matter their size
! CHECK-NEXT: -fsyntax-only Run the preprocessor, parser and semantic analysis stages
! CHECK-NEXT: -funderscoring Appends one trailing underscore to external names

View File

@ -54,8 +54,16 @@
! HELP-NEXT: -fopenmp-version=<value>
! HELP-NEXT: Set OpenMP version (e.g. 45 for OpenMP 4.5, 50 for OpenMP 5.0). Default value is 50 for Clang and 11 for Flang
! HELP-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code.
! HELP-NEXT: -foptimization-record-file=<file>
! HELP-NEXT: Specify the output name of the file containing the optimization remarks. Implies -fsave-optimization-record. On Darwin platforms, this cannot be used with multiple -arch <arch> options.
! HELP-NEXT: -foptimization-record-passes=<regex>
! HELP-NEXT: Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)
! HELP-NEXT: -fpass-plugin=<dsopath> Load pass plugin from a dynamic shared object file (only with new pass manager).
! HELP-NEXT: -freciprocal-math Allow division operations to be reassociated
! HELP-NEXT: -fsave-optimization-record=<format>
! HELP-NEXT: Generate an optimization record file in a specific format
! HELP-NEXT: -fsave-optimization-record
! HELP-NEXT: Generate a YAML optimization record file
! HELP-NEXT: -fstack-arrays Attempt to allocate array temporaries on the stack, no matter their size
! HELP-NEXT: -fsyntax-only Run the preprocessor, parser and semantic analysis stages
! HELP-NEXT: -funderscoring Appends one trailing underscore to external names
@ -186,6 +194,12 @@
! HELP-FC1-NEXT: -mrelocation-model <value>
! HELP-FC1-NEXT: The relocation model to use
! HELP-FC1-NEXT: -nocpp Disable predefined and command line preprocessor macros
! HELP-FC1-NEXT: -opt-record-file <value>
! HELP-FC1-NEXT: File name to use for YAML optimization record output
! HELP-FC1-NEXT: -opt-record-format <value>
! HELP-FC1-NEXT: The format used for serializing remarks (default: YAML)
! HELP-FC1-NEXT: -opt-record-passes <value>
! HELP-FC1-NEXT: Only record remark information for passes whose names match the given regular expression
! HELP-FC1-NEXT: -o <file> Write output to <file>
! HELP-FC1-NEXT: -pedantic Warn on language extensions
! HELP-FC1-NEXT: -pic-is-pie File is for a position independent executable

View File

@ -0,0 +1,59 @@
! Tests for the '-f[no-]save-optimization-record[=<format>]' flag.
! Test opt_record flags get generated for fc1
! RUN: %flang -### %s 2>&1 \
! RUN: -foptimization-record-file=%t.opt.yaml \
! RUN: | FileCheck --check-prefix=YAML %s
! RUN: %flang -### %s 2>&1 \
! RUN: -fsave-optimization-record \
! RUN: | FileCheck --check-prefix=YAML %s
! Test -foptimization-record-file produces YAML file with given content
! RUN: rm -f %t.opt.yaml
! RUN: %flang -foptimization-record-file=%t.opt.yaml -c %s
! RUN: cat %t.opt.yaml | FileCheck %s
! Test -fsave-optimization-record produces YAML file with given content
! RUN: rm -f %t.opt.yaml
! RUN: %flang -fsave-optimization-record -c -o %t.o %s
! RUN: cat %t.opt.yaml | FileCheck %s
! RUN: rm -f %t.opt.yaml
! RUN: %flang -fsave-optimization-record -S -o %t.s %s
! RUN: cat %t.opt.yaml | FileCheck %s
! Produces an empty file
! RUN: rm -f %t.opt.yaml
! RUN: %flang -fsave-optimization-record -S -emit-llvm -o %t.ll %s
! RUN: cat %t.opt.yaml
!Test unknown format produces error
! RUN: not %flang -fsave-optimization-record=hello %s 2>&1 \
! RUN: | FileCheck --check-prefix=CHECK-FORMAT-ERROR %s
! YAML: "-opt-record-file" "{{.+}}.opt.yaml"
! YAML: "-opt-record-format" "yaml"
! CHECK: --- !Analysis
! CHECK: Pass: prologepilog
! CHECK: Name: StackSize
! CHECK: Function: _QQmain
! CHECK: Pass: asm-printer
! CHECK: Name: InstructionMix
! CHECK: Name: InstructionCount
! CHECK-FORMAT-ERROR: error: unknown remark serializer format: 'hello'
program forttest
implicit none
integer :: n
n = 1 * 1
end program forttest