awarzynski created this revision. awarzynski added reviewers: rovka, kiranchandramohan, schweitz, peixin. Herald added subscribers: bzcheeseman, rriddle, mgorny. Herald added a reviewer: sscalpone. Herald added projects: Flang, All. awarzynski requested review of this revision. Herald added subscribers: cfe-commits, stephenneuendorffer, jdoerfert, MaskRay. Herald added a project: clang.
This patch adds support for most common optimisation compiler flags: `-O{0|1|2|3}`. This is implemented in both the compiler and fronted drivers. At this point, these options are only used to configure the LLVM optimisation pipelines (aka middle-end). LLVM backend or MLIR/FIR optimisations are not supported yet. Previously, the middle-end pass manager was only required when generating LLVM bitcode (i.e. for `flang-new -c -emit-llvm <file>` or `flang-new -fc1 -emit-llvm-bc <file>`). With this change, it becomes required for all frontend actions that are represented as `CodeGenAction` and `CodeGenAction::executeAction` is refactored accordingly (in the spirit of better code re-use). Additionally, the `-fdebug-pass-manager` option is enabled to facilitate testing. This flag can be used to configure the pass manager to print the middle-end passes that are being run. Similar option exists in Clang and the semantics in Flang are identical. This option translates to extra configuration when setting up the pass manager. This is implemented in `CodeGenAction::runOptimizationPipeline`. This patch also adds some bolier plate code to manage code-gen options ("code-gen" refers to generating machine code in LLVM in this context). This was extracted from Clang. In Clang, it simplifies defining code-gen options and enables option marshalling. In Flang, option marshalling is not yet supported (we might do at some point), but being able to auto-generate some code with macros is beneficial. This will become particularly apparent when we start adding more options (at least in Clang, the list of code-gen options is rather long). Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D128043 Files: clang/include/clang/Driver/Options.td clang/lib/Driver/ToolChains/Flang.cpp flang/include/flang/Frontend/CodeGenOptions.def flang/include/flang/Frontend/CodeGenOptions.h flang/include/flang/Frontend/CompilerInvocation.h flang/include/flang/Frontend/FrontendActions.h flang/lib/Frontend/CMakeLists.txt flang/lib/Frontend/CodeGenOptions.cpp flang/lib/Frontend/CompilerInvocation.cpp flang/lib/Frontend/FrontendActions.cpp flang/test/Driver/default-optimization-pipelines.f90 flang/test/Driver/driver-help.f90
Index: flang/test/Driver/driver-help.f90 =================================================================== --- flang/test/Driver/driver-help.f90 +++ flang/test/Driver/driver-help.f90 @@ -95,6 +95,7 @@ ! HELP-FC1-NEXT: -fdebug-measure-parse-tree ! HELP-FC1-NEXT: Measure the parse tree ! HELP-FC1-NEXT: -fdebug-module-writer Enable debug messages while writing module files +! HELP-FC1-NEXT: -fdebug-pass-manager Prints debug information for the new pass manage ! HELP-FC1-NEXT: -fdebug-pre-fir-tree Dump the pre-FIR tree ! HELP-FC1-NEXT: -fdebug-unparse-no-sema Unparse and stop (skips the semantic checks) ! HELP-FC1-NEXT: -fdebug-unparse-with-symbols @@ -119,6 +120,7 @@ ! HELP-FC1-NEXT: -fno-analyzed-objects-for-unparse ! HELP-FC1-NEXT: Do not use the analyzed objects when unparsing ! HELP-FC1-NEXT: -fno-automatic Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE +! HELP-FC1-NEXT: -fno-debug-pass-manager Disables debug printing for the new pass manager ! HELP-FC1-NEXT: -fno-reformat Dump the cooked character stream in -E mode ! HELP-FC1-NEXT: -fopenacc Enable OpenACC ! HELP-FC1-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code. Index: flang/test/Driver/default-optimization-pipelines.f90 =================================================================== --- /dev/null +++ flang/test/Driver/default-optimization-pipelines.f90 @@ -0,0 +1,24 @@ +! Verify that`-O{n}` is indeed taken into account when definining the LLVM optimization/middle-end pass pass pipeline. + +!----------- +! RUN LINES +!----------- +! RUN: %flang_fc1 -S -O0 %s -fdebug-pass-manager -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-O0 +! RUN: %flang_fc1 -S -O2 %s -fdebug-pass-manager -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-O2 + +!----------------------- +! EXPECTED OUTPUT +!----------------------- +! CHECK-O0-NOT: Running pass: SimplifyCFGPass on simple_loop_ +! CHECK-O0: Running analysis: TargetLibraryAnalysis on simple_loop_ + +! CHECK-O2: Running pass: SimplifyCFGPass on simple_loop_ + +!------- +! INPUT +!------- +subroutine simple_loop + integer :: i + do i=1,5 + end do +end subroutine Index: flang/lib/Frontend/FrontendActions.cpp =================================================================== --- flang/lib/Frontend/FrontendActions.cpp +++ flang/lib/Frontend/FrontendActions.cpp @@ -46,6 +46,7 @@ #include "llvm/IRReader/IRReader.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Target/TargetMachine.h" @@ -538,7 +539,6 @@ /*Features=*/"", llvm::TargetOptions(), llvm::None)); assert(tm && "Failed to create TargetMachine"); - llvmModule->setDataLayout(tm->createDataLayout()); } static std::unique_ptr<llvm::raw_pwrite_stream> @@ -615,18 +615,60 @@ /// \param [in] tm Target machine to aid the code-gen pipeline set-up /// \param [in] llvmModule LLVM module to lower to assembly/machine-code /// \param [out] os Output stream to emit the generated code to -static void generateLLVMBCImpl(llvm::TargetMachine &tm, - llvm::Module &llvmModule, - llvm::raw_pwrite_stream &os) { - // Set-up the pass manager - llvm::ModulePassManager mpm; + +static llvm::OptimizationLevel +mapToLevel(const Fortran::frontend::CodeGenOptions &opts) { + switch (opts.OptimizationLevel) { + default: + llvm_unreachable("Invalid optimization level!"); + case 0: + return llvm::OptimizationLevel::O0; + case 1: + return llvm::OptimizationLevel::O1; + case 2: + return llvm::OptimizationLevel::O2; + case 3: + return llvm::OptimizationLevel::O3; + } +} + +void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) { + auto opts = getInstance().getInvocation().getCodeGenOpts(); + llvm::OptimizationLevel level = mapToLevel(opts); + + // Create the analysis managers. + llvm::LoopAnalysisManager lam; + llvm::FunctionAnalysisManager fam; + llvm::CGSCCAnalysisManager cgam; llvm::ModuleAnalysisManager mam; - llvm::PassBuilder pb(&tm); + + // Create the pass manager builder. + llvm::PassInstrumentationCallbacks pic; + llvm::PipelineTuningOptions pto; + llvm::Optional<llvm::PGOOptions> pgoOpt; + llvm::StandardInstrumentations si(opts.DebugPassManager); + si.registerCallbacks(pic, &fam); + llvm::PassBuilder pb(tm.get(), pto, pgoOpt, &pic); + + // Register all the basic analyses with the managers. pb.registerModuleAnalyses(mam); - mpm.addPass(llvm::BitcodeWriterPass(os)); + pb.registerCGSCCAnalyses(cgam); + pb.registerFunctionAnalyses(fam); + pb.registerLoopAnalyses(lam); + pb.crossRegisterProxies(lam, fam, cgam, mam); + + // Create the pass manager. + llvm::ModulePassManager mpm; + if (opts.OptimizationLevel == 0) + mpm = pb.buildO0DefaultPipeline(level, false); + else + mpm = pb.buildPerModuleDefaultPipeline(level); - // run the passes - mpm.run(llvmModule, mam); + if (action == BackendActionTy::Backend_EmitBC) + mpm.addPass(llvm::BitcodeWriterPass(os)); + + // Run the passes. + mpm.run(*llvmModule, mam); } void CodeGenAction::executeAction() { @@ -661,11 +703,14 @@ return; } - // generate an LLVM module if it's not already present (it will already be + // Generate an LLVM module if it's not already present (it will already be // present if the input file is an LLVM IR/BC file). if (!llvmModule) generateLLVMIR(); + // Run LLVM's middle-end (i.e. the optimizer) + runOptimizationPipeline(*os); + if (action == BackendActionTy::Backend_EmitLL) { llvmModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream(), /*AssemblyAnnotationWriter=*/nullptr); @@ -673,11 +718,14 @@ } setUpTargetMachine(); + llvmModule->setDataLayout(tm->createDataLayout()); + if (action == BackendActionTy::Backend_EmitBC) { - generateLLVMBCImpl(*tm, *llvmModule, *os); + // This action has effectively been completed in runOptimizationPipeline return; } + // Run LLVM's backend and generate either assembly or machine code if (action == BackendActionTy::Backend_EmitAssembly || action == BackendActionTy::Backend_EmitObj) { generateMachineCodeOrAssemblyImpl( Index: flang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- flang/lib/Frontend/CompilerInvocation.cpp +++ flang/lib/Frontend/CompilerInvocation.cpp @@ -12,6 +12,7 @@ #include "flang/Frontend/CompilerInvocation.h" #include "flang/Common/Fortran-features.h" +#include "flang/Frontend/CodeGenOptions.h" #include "flang/Frontend/PreprocessorOptions.h" #include "flang/Frontend/TargetOptions.h" #include "flang/Semantics/semantics.h" @@ -20,6 +21,7 @@ #include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/OptionUtils.h" #include "clang/Driver/Options.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" @@ -95,6 +97,18 @@ return true; } +static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts, + llvm::opt::ArgList &args, + clang::DiagnosticsEngine &diags) { + unsigned defaultOpt = llvm::CodeGenOpt::None; + opts.OptimizationLevel = clang::getLastArgIntValue( + args, clang::driver::options::OPT_O, defaultOpt, diags); + + if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager, + clang::driver::options::OPT_fno_debug_pass_manager, false)) + opts.DebugPassManager = 1; +} + /// Parses all target input arguments and populates the target /// options accordingly. /// @@ -612,6 +626,7 @@ success &= parseFrontendArgs(res.getFrontendOpts(), args, diags); parseTargetArgs(res.getTargetOpts(), args); parsePreprocessorArgs(res.getPreprocessorOpts(), args); + parseCodeGenArgs(res.getCodeGenOpts(), args, diags); success &= parseSemaArgs(res, args, diags); success &= parseDialectArgs(res, args, diags); success &= parseDiagArgs(res, args, diags); Index: flang/lib/Frontend/CodeGenOptions.cpp =================================================================== --- /dev/null +++ flang/lib/Frontend/CodeGenOptions.cpp @@ -0,0 +1,20 @@ +//===--- CodeGenOptions.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang/Frontend/CodeGenOptions.h" +#include <string.h> + +namespace Fortran::frontend { + +CodeGenOptions::CodeGenOptions() { +#define CODEGENOPT(Name, Bits, Default) Name = Default; +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) set##Name(Default); +#include "flang/Frontend/CodeGenOptions.def" +} + +} // end namespace Fortran::frontend Index: flang/lib/Frontend/CMakeLists.txt =================================================================== --- flang/lib/Frontend/CMakeLists.txt +++ flang/lib/Frontend/CMakeLists.txt @@ -3,6 +3,7 @@ add_flang_library(flangFrontend CompilerInstance.cpp CompilerInvocation.cpp + CodeGenOptions.cpp FrontendAction.cpp FrontendActions.cpp FrontendOptions.cpp Index: flang/include/flang/Frontend/FrontendActions.h =================================================================== --- flang/include/flang/Frontend/FrontendActions.h +++ flang/include/flang/Frontend/FrontendActions.h @@ -13,6 +13,7 @@ #ifndef FORTRAN_FRONTEND_FRONTENDACTIONS_H #define FORTRAN_FRONTEND_FRONTENDACTIONS_H +#include "flang/Frontend/CodeGenOptions.h" #include "flang/Frontend/FrontendAction.h" #include "flang/Parser/parsing.h" #include "flang/Semantics/semantics.h" @@ -198,7 +199,11 @@ void executeAction() override; /// Runs prescan, parsing, sema and lowers to MLIR. bool beginSourceFileAction() override; + /// Sets up LLVM's TargetMachine, configures llvmModule accordingly void setUpTargetMachine(); + /// Runs the optimization (aka middle-end) pipeline on the LLVM module + /// associated with this action. + void runOptimizationPipeline(llvm::raw_pwrite_stream &os); protected: CodeGenAction(BackendActionTy act) : action{act} {}; Index: flang/include/flang/Frontend/CompilerInvocation.h =================================================================== --- flang/include/flang/Frontend/CompilerInvocation.h +++ flang/include/flang/Frontend/CompilerInvocation.h @@ -13,6 +13,7 @@ #ifndef FORTRAN_FRONTEND_COMPILERINVOCATION_H #define FORTRAN_FRONTEND_COMPILERINVOCATION_H +#include "flang/Frontend/CodeGenOptions.h" #include "flang/Frontend/FrontendOptions.h" #include "flang/Frontend/PreprocessorOptions.h" #include "flang/Frontend/TargetOptions.h" @@ -71,6 +72,9 @@ /// Options controlling the target. Fortran::frontend::TargetOptions targetOpts; + /// Options controlling IRgen and the backend. + Fortran::frontend::CodeGenOptions codeGenOpts; + // Semantics context std::unique_ptr<Fortran::semantics::SemanticsContext> semanticsContext; @@ -130,6 +134,9 @@ TargetOptions &getTargetOpts() { return targetOpts; } const TargetOptions &getTargetOpts() const { return targetOpts; } + CodeGenOptions &getCodeGenOpts() { return codeGenOpts; } + const CodeGenOptions &getCodeGenOpts() const { return codeGenOpts; } + Fortran::semantics::SemanticsContext &getSemanticsContext() { return *semanticsContext; } Index: flang/include/flang/Frontend/CodeGenOptions.h =================================================================== --- /dev/null +++ flang/include/flang/Frontend/CodeGenOptions.h @@ -0,0 +1,61 @@ +//===--- CodeGenOptions.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the CodeGenOptions interface, which holds the +// configuration for LLVM's middle-end and back-end. It controls LLVM's code +// generation into assembly or machine code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H +#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H + +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Regex.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h" +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace Fortran::frontend { + +/// Bitfields of CodeGenOptions, split out from CodeGenOptions to ensure +/// that this large collection of bitfields is a trivial class type. +class CodeGenOptionsBase { + +public: +#define CODEGENOPT(Name, Bits, Default) unsigned Name : Bits; +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) +#include "flang/Frontend/CodeGenOptions.def" + +protected: +#define CODEGENOPT(Name, Bits, Default) +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) unsigned Name : Bits; +#include "flang/Frontend/CodeGenOptions.def" +}; + +/// Tracks various options which control how the code is optimized and passed +/// to the LLVM backend. +class CodeGenOptions : public CodeGenOptionsBase { + +public: + // Define accessors/mutators for code generation options of enumeration type. +#define CODEGENOPT(Name, Bits, Default) +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) \ + Type get##Name() const { return static_cast<Type>(Name); } \ + void set##Name(Type Value) { Name = static_cast<unsigned>(Value); } +#include "flang/Frontend/CodeGenOptions.def" + + CodeGenOptions(); +}; + +} // end namespace Fortran::frontend + +#endif Index: flang/include/flang/Frontend/CodeGenOptions.def =================================================================== --- /dev/null +++ flang/include/flang/Frontend/CodeGenOptions.def @@ -0,0 +1,37 @@ +//===--- CodeGenOptions.def - Code generation option database ----- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the code generation options. Users of this file +// must define the CODEGENOPT macro to make use of this information. +// Optionally, the user may also define ENUM_CODEGENOPT (for options +// that have enumeration type and VALUE_CODEGENOPT is a code +// generation option that describes a value rather than a flag. +// +//===----------------------------------------------------------------------===// +#ifndef CODEGENOPT +# error Define the CODEGENOPT macro to handle language options +#endif + +#ifndef VALUE_CODEGENOPT +# define VALUE_CODEGENOPT(Name, Bits, Default) \ +CODEGENOPT(Name, Bits, Default) +#endif + +#ifndef ENUM_CODEGENOPT +# define ENUM_CODEGENOPT(Name, Type, Bits, Default) \ +CODEGENOPT(Name, Bits, Default) +#endif + +VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified. + +CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new + ///< pass manager. + +#undef CODEGENOPT +#undef ENUM_CODEGENOPT +#undef VALUE_CODEGENOPT Index: clang/lib/Driver/ToolChains/Flang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Flang.cpp +++ clang/lib/Driver/ToolChains/Flang.cpp @@ -126,6 +126,16 @@ A->render(Args, CmdArgs); } + // Optimization level for CodeGen. + if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O4)) { + CmdArgs.push_back("-O3"); + TC.getDriver().Diag(diag::warn_O4_is_O3); + } else { + A->render(Args, CmdArgs); + } + } + if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -721,14 +721,14 @@ MarshallingInfoFlag<DependencyOutputOpts<"OutputFormat">, "DependencyOutputFormat::Make">, Normalizer<"makeFlagToValueNormalizer(DependencyOutputFormat::NMake)">; def Mach : Flag<["-"], "Mach">, Group<Link_Group>; -def O0 : Flag<["-"], "O0">, Group<O_Group>, Flags<[CC1Option, HelpHidden]>; -def O4 : Flag<["-"], "O4">, Group<O_Group>, Flags<[CC1Option, HelpHidden]>; +def O0 : Flag<["-"], "O0">, Group<O_Group>, Flags<[CC1Option, FC1Option, HelpHidden]>; +def O4 : Flag<["-"], "O4">, Group<O_Group>, Flags<[CC1Option, FC1Option, HelpHidden]>; def ObjCXX : Flag<["-"], "ObjC++">, Flags<[NoXarchOption]>, HelpText<"Treat source input files as Objective-C++ inputs">; def ObjC : Flag<["-"], "ObjC">, Flags<[NoXarchOption]>, HelpText<"Treat source input files as Objective-C inputs">; -def O : Joined<["-"], "O">, Group<O_Group>, Flags<[CC1Option]>; -def O_flag : Flag<["-"], "O">, Flags<[CC1Option]>, Alias<O>, AliasArgs<["1"]>; +def O : Joined<["-"], "O">, Group<O_Group>, Flags<[CC1Option,FC1Option]>; +def O_flag : Flag<["-"], "O">, Flags<[CC1Option,FC1Option]>, Alias<O>, AliasArgs<["1"]>; def Ofast : Joined<["-"], "Ofast">, Group<O_Group>, Flags<[CC1Option]>; def P : Flag<["-"], "P">, Flags<[CC1Option,FlangOption,FC1Option]>, Group<Preprocessor_Group>, HelpText<"Disable linemarker output in -E mode">, @@ -5458,10 +5458,6 @@ CodeGenOpts<"LTOUnit">, DefaultFalse, PosFlag<SetTrue, [CC1Option], "Emit IR to support LTO unit features (CFI, whole program vtable opt)">, NegFlag<SetFalse>>; -defm debug_pass_manager : BoolOption<"f", "debug-pass-manager", - CodeGenOpts<"DebugPassManager">, DefaultFalse, - PosFlag<SetTrue, [], "Prints debug information for the new pass manager">, - NegFlag<SetFalse, [], "Disables debug printing for the new pass manager">>; def fverify_debuginfo_preserve : Flag<["-"], "fverify-debuginfo-preserve">, HelpText<"Enable Debug Info Metadata preservation testing in " @@ -6265,6 +6261,10 @@ HelpText<"Load the named plugin (dynamic shared object)">; def plugin : Separate<["-"], "plugin">, MetaVarName<"<name>">, HelpText<"Use the named plugin action instead of the default action (use \"help\" to list available options)">; +defm debug_pass_manager : BoolOption<"f", "debug-pass-manager", + CodeGenOpts<"DebugPassManager">, DefaultFalse, + PosFlag<SetTrue, [], "Prints debug information for the new pass manager">, + NegFlag<SetFalse, [], "Disables debug printing for the new pass manager">>; } // let Flags = [CC1Option, FC1Option, NoDriverOption]
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits