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 @@ -721,14 +721,14 @@ MarshallingInfoFlag, "DependencyOutputFormat::Make">, Normalizer<"makeFlagToValueNormalizer(DependencyOutputFormat::NMake)">; def Mach : Flag<["-"], "Mach">, Group; -def O0 : Flag<["-"], "O0">, Group, Flags<[CC1Option, HelpHidden]>; -def O4 : Flag<["-"], "O4">, Group, Flags<[CC1Option, HelpHidden]>; +def O0 : Flag<["-"], "O0">, Group, Flags<[CC1Option, FC1Option, HelpHidden]>; +def O4 : Flag<["-"], "O4">, 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, Flags<[CC1Option]>; -def O_flag : Flag<["-"], "O">, Flags<[CC1Option]>, Alias, AliasArgs<["1"]>; +def O : Joined<["-"], "O">, Group, Flags<[CC1Option,FC1Option]>; +def O_flag : Flag<["-"], "O">, Flags<[CC1Option,FC1Option]>, Alias, AliasArgs<["1"]>; def Ofast : Joined<["-"], "Ofast">, Group, Flags<[CC1Option]>; def P : Flag<["-"], "P">, Flags<[CC1Option,FlangOption,FC1Option]>, Group, HelpText<"Disable linemarker output in -E mode">, @@ -5461,10 +5461,6 @@ CodeGenOpts<"LTOUnit">, DefaultFalse, PosFlag, NegFlag>; -defm debug_pass_manager : BoolOption<"f", "debug-pass-manager", - CodeGenOpts<"DebugPassManager">, DefaultFalse, - PosFlag, - NegFlag>; def fverify_debuginfo_preserve : Flag<["-"], "fverify-debuginfo-preserve">, HelpText<"Enable Debug Info Metadata preservation testing in " @@ -6268,6 +6264,10 @@ HelpText<"Load the named plugin (dynamic shared object)">; def plugin : Separate<["-"], "plugin">, MetaVarName<"">, 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, + NegFlag>; } // let Flags = [CC1Option, FC1Option, NoDriverOption] 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 @@ -135,6 +135,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"); + D.Diag(diag::warn_O4_is_O3); + } else { + A->render(Args, CmdArgs); + } + } + if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); diff --git a/flang/include/flang/Frontend/CodeGenOptions.h b/flang/include/flang/Frontend/CodeGenOptions.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Frontend/CodeGenOptions.h @@ -0,0 +1,52 @@ +//===--- 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 +#include +#include +#include + +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; +#include "flang/Frontend/CodeGenOptions.def" + +protected: +#define CODEGENOPT(Name, Bits, Default) +#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: + CodeGenOptions(); +}; + +} // end namespace Fortran::frontend + +#endif diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def new file mode 100644 --- /dev/null +++ b/flang/include/flang/Frontend/CodeGenOptions.def @@ -0,0 +1,22 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +#ifndef CODEGENOPT +# error Define the CODEGENOPT macro to handle language options +#endif + +CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified. + +CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new + ///< pass manager. + +#undef CODEGENOPT diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h --- a/flang/include/flang/Frontend/CompilerInvocation.h +++ b/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" @@ -70,6 +71,9 @@ /// Options controlling the target. Fortran::frontend::TargetOptions targetOpts; + /// Options controlling IRgen and the backend. + Fortran::frontend::CodeGenOptions codeGenOpts; + // Semantics context std::unique_ptr semanticsContext; @@ -129,6 +133,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; } diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/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} {}; diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt --- a/flang/lib/Frontend/CMakeLists.txt +++ b/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 diff --git a/flang/lib/Frontend/CodeGenOptions.cpp b/flang/lib/Frontend/CodeGenOptions.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Frontend/CodeGenOptions.cpp @@ -0,0 +1,23 @@ +//===--- 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 +// +//===----------------------------------------------------------------------===// +// +// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ +// +//===----------------------------------------------------------------------===// + +#include "flang/Frontend/CodeGenOptions.h" +#include + +namespace Fortran::frontend { + +CodeGenOptions::CodeGenOptions() { +#define CODEGENOPT(Name, Bits, Default) Name = Default; +#include "flang/Frontend/CodeGenOptions.def" +} + +} // end namespace Fortran::frontend 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 @@ -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. /// @@ -616,6 +630,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); 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 @@ -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 @@ -610,23 +610,59 @@ codeGenPasses.run(llvmModule); } -/// Generate LLVM byte code file from the input LLVM module. -/// -/// \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 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 +697,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 +712,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( diff --git a/flang/test/Driver/default-optimization-pipelines.f90 b/flang/test/Driver/default-optimization-pipelines.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/default-optimization-pipelines.f90 @@ -0,0 +1,24 @@ +! Verify that`-O{n}` is indeed taken into account when defining 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 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 @@ -96,6 +96,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 @@ -120,6 +121,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.