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 @@ -3074,18 +3074,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<"">; @@ -6380,14 +6380,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>; @@ -6449,6 +6441,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,50 @@ 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 @@ // 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); 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 @@ -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 @@ } } +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 @@ 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 @@ } 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 @@ 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 @@ 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> 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 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 @@ 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; } 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 \ @@ -22,6 +23,10 @@ ! RUN: -P \ ! RUN: | FileCheck %s +! RUN: %flang -### %s 2>&1 \ +! RUN: -foptimization-record-file=%t.opt.yaml \ +! RUN: | FileCheck --check-prefix=YAML %s + ! CHECK: "-P" ! CHECK: "-finput-charset=utf-8" ! CHECK: "-fdefault-double-8" @@ -38,5 +43,10 @@ ! CHECK: "-fpass-plugin=Bye ! CHECK: "-flang-experimental-polymorphism" ! CHECK: "-fversion-loops-for-stride" +! CHECK: "-opt-record-file" "{{.+}}.opt.yaml" +! CHECK: "-opt-record-format" "yaml" ! CHECK: "-mllvm" "-print-before-all" ! CHECK: "-save-temps=obj" + +! YAML: "-opt-record-file" "{{.+}}.opt.yaml" +! YAML: "-opt-record-format" "yaml" 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,42 @@ +! Tests for the '-f[no-]save-optimization-record[=]' flag. + +! 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 + +! 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