diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3065,18 +3065,18 @@ HelpText<"Maximum number of 'operator->'s to call for a member access">, MarshallingInfoInt, "256">; -def fsave_optimization_record : Flag<["-"], "fsave-optimization-record">, +def fsave_optimization_record : Flag<["-"], "fsave-optimization-record">, Flags<[FlangOption]>, 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, HelpText<"Generate an optimization record file in a specific format">, MetaVarName<"">; def fno_save_optimization_record : Flag<["-"], "fno-save-optimization-record">, - Group, Flags<[NoArgumentUnused]>; -def foptimization_record_file_EQ : Joined<["-"], "foptimization-record-file=">, + Group, Flags<[FlangOption, NoArgumentUnused]>; +def foptimization_record_file_EQ : Joined<["-"], "foptimization-record-file=">, Flags<[FlangOption]>, 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 options.">, MetaVarName<"">; -def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=">, +def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=">, Flags<[FlangOption]>, Group, HelpText<"Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)">, MetaVarName<"">; @@ -6364,14 +6364,6 @@ NormalizedValues<["ARCMT_Check", "ARCMT_Modify", "ARCMT_Migrate"]>, MarshallingInfoEnum, "ARCMT_None">; -def opt_record_file : Separate<["-"], "opt-record-file">, - HelpText<"File name to use for YAML optimization record output">, - MarshallingInfoString>; -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>; @@ -6433,6 +6425,14 @@ } // let Flags = [CC1Option, NoDriverOption] +def opt_record_file : Separate<["-"], "opt-record-file">, Flags<[FC1Option, CC1Option]>, + HelpText<"File name to use for YAML optimization record output">, + MarshallingInfoString>; +def opt_record_passes : Separate<["-"], "opt-record-passes">, Flags<[FC1Option, CC1Option]>, + HelpText<"Only record remark information for passes whose names match the given regular expression">; +def opt_record_format : Separate<["-"], "opt-record-format">, Flags<[FC1Option, CC1Option]>, + HelpText<"The format used for serializing remarks (default: YAML)">; + //===----------------------------------------------------------------------===// // Language Options //===----------------------------------------------------------------------===// diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -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 @@ -387,6 +389,52 @@ CmdArgs.push_back("-freciprocal-math"); } +static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, + const llvm::Triple &Triple, + const InputInfo &Input, + const InputInfo &Output, const JobAction &JA) { + 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 +519,11 @@ // Add Codegen options addCodegenOptions(Args, CmdArgs); + // Remarks can be enabled with any of the `-f.*optimization-record.*` flags. + if (willEmitRemarks(Args)) { + renderRemarksOptions(Args, CmdArgs, Triple, Input, Output, JA); + } + // Add other compile options addOtherOptions(Args, CmdArgs); 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 @@ -58,6 +58,17 @@ /// The directory where temp files are stored if specified by -save-temps std::optional 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) \ 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 @@ -190,6 +190,22 @@ opts.PrepareForThinLTO = true; } + // -f[no-]save-optimization-record[=] + 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(); 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 @@ -47,6 +47,7 @@ #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 +56,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" @@ -1002,6 +1004,28 @@ if (!ci.getInvocation().getCodeGenOpts().OffloadObjects.empty()) embedOffloadObjects(); + // write optimization-record + const CodeGenOptions &CodeGenOpts = ci.getInvocation().getCodeGenOpts(); + + llvm::Expected> RemarksFileOrErr = + setupLLVMOptimizationRemarks( + llvmModule->getContext(), CodeGenOpts.OptRecordFile, + CodeGenOpts.OptRecordPasses, CodeGenOpts.OptRecordFormat, + /*DiagnosticsWithHotness=*/false, + /*DiagnosticsHotnessThreshold=*/0); + + if (llvm::Error E = RemarksFileOrErr.takeError()) { + llvm::errs() << toString(std::move(E)) << '\n'; + return; + } + std::unique_ptr ToolRemarksFile = + std::move(*RemarksFileOrErr); + + if (ToolRemarksFile) { + ToolRemarksFile->keep(); + ToolRemarksFile->os().flush(); + } + // Run LLVM's middle-end (i.e. the optimizer). runOptimizationPipeline(ci.isOutputStreamNull() ? *os : ci.getOutputStream()); diff --git a/flang/test/Driver/driver-help-hidden.f90 b/flang/test/Driver/driver-help-hidden.f90 --- a/flang/test/Driver/driver-help-hidden.f90 +++ b/flang/test/Driver/driver-help-hidden.f90 @@ -58,8 +58,16 @@ ! CHECK-NEXT: -fopenmp-version= ! 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= +! 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 options. +! CHECK-NEXT: -foptimization-record-passes= +! 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= 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= +! 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 diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -54,8 +54,16 @@ ! HELP-NEXT: -fopenmp-version= ! 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= +! 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 options. +! HELP-NEXT: -foptimization-record-passes= +! 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= 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= +! 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 ! 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 +! HELP-FC1-NEXT: File name to use for YAML optimization record output +! HELP-FC1-NEXT: -opt-record-format +! HELP-FC1-NEXT: The format used for serializing remarks (default: YAML) +! HELP-FC1-NEXT: -opt-record-passes +! HELP-FC1-NEXT: Only record remark information for passes whose names match the given regular expression ! HELP-FC1-NEXT: -o Write output to ! HELP-FC1-NEXT: -pedantic Warn on language extensions ! HELP-FC1-NEXT: -pic-is-pie File is for a position independent executable diff --git a/flang/test/Driver/frontend-forwarding.f90 b/flang/test/Driver/frontend-forwarding.f90 --- a/flang/test/Driver/frontend-forwarding.f90 +++ b/flang/test/Driver/frontend-forwarding.f90 @@ -15,6 +15,7 @@ ! RUN: -fassociative-math \ ! RUN: -freciprocal-math \ ! RUN: -fpass-plugin=Bye%pluginext \ +! RUN: -fsave-optimization-record \ ! RUN: -fversion-loops-for-stride \ ! RUN: -flang-experimental-polymorphism \ ! RUN: -mllvm -print-before-all \ @@ -38,5 +39,7 @@ ! CHECK: "-fpass-plugin=Bye ! CHECK: "-flang-experimental-polymorphism" ! CHECK: "-fversion-loops-for-stride" +! CHECK: "-opt-record-file" +! CHECK: "-opt-record-format" ! CHECK: "-mllvm" "-print-before-all" ! CHECK: "-save-temps=obj" diff --git a/flang/test/Driver/fsave-optimization-record.f90 b/flang/test/Driver/fsave-optimization-record.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/fsave-optimization-record.f90 @@ -0,0 +1,21 @@ +! Tests for the '-f[no-]save-optimization-record[=]' flag. + +! Test that the optimization file was generated with the needed name +! RUN: %flang -foptimization-record-file=%t.opt.yaml %s +! RUN: FileCheck %s < %t.opt.yaml + +! Test that no error or warning should be produced +! RUN: %flang -fsave-optimization-record=yaml %s +! RUN: %flang -fsave-optimization-record -S %s +! RUN: %flang -fsave-optimization-record -S -emit-llvm %s +! RUN: %flang -fsave-optimization-record -c %s + +! CHECK: --- !Analysis + +program forttest + implicit none + integer :: n + + n = 1 * 1 + +end program forttest