diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -39,6 +39,7 @@ createMemoryAllocationPass(bool dynOnHeap, std::size_t maxStackSize); std::unique_ptr createAnnotateConstantOperandsPass(); std::unique_ptr createSimplifyRegionLitePass(); +std::unique_ptr createAlgebraicSimplificationPass(); // declarative passes #define GEN_PASS_REGISTRATION diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -196,4 +196,17 @@ let constructor = "::fir::createSimplifyRegionLitePass()"; } +def AlgebraicSimplification : Pass<"flang-algebraic-simplification"> { + let summary = ""; + let description = [{ + Run algebraic simplifications for Math/Complex/etc. dialect operations. + This is a flang specific pass, because we may want to "tune" + the rewrite patterns specifically for Fortran (e.g. increase + the limit for constant exponent value that defines the cases + when pow(x, constant) is transformed into a set of multiplications, etc.). + }]; + let dependentDialects = [ "mlir::math::MathDialect" ]; + let constructor = "::fir::createAlgebraicSimplificationPass()"; +} + #endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -15,6 +15,7 @@ #include "mlir/Transforms/Passes.h" #include "flang/Optimizer/CodeGen/CodeGen.h" #include "flang/Optimizer/Transforms/Passes.h" +#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Support/CommandLine.h" #define DisableOption(DOName, DOOption, DODescription) \ @@ -50,6 +51,9 @@ llvm::cl::init(false), llvm::cl::Hidden); namespace { +const static llvm::OptimizationLevel &defaultOptLevel{ + llvm::OptimizationLevel::O2}; + /// Optimizer Passes DisableOption(CfgConversion, "cfg-conversion", "disable FIR to CFG pass"); DisableOption(FirAvc, "avc", "array value copy analysis and transformation"); @@ -150,7 +154,8 @@ /// incremental conversion of FIR. /// /// \param pm - MLIR pass manager that will hold the pipeline definition -inline void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm) { +inline void createDefaultFIROptimizerPassPipeline( + mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel) { // simplify the IR mlir::GreedyRewriteConfig config; config.enableRegionSimplification = false; @@ -159,6 +164,9 @@ pm.addNestedPass(fir::createCharacterConversionPass()); pm.addPass(mlir::createCanonicalizerPass(config)); pm.addPass(fir::createSimplifyRegionLitePass()); + // Algebraic simplifications may increase code size. + if (optLevel.isOptimizingForSpeed()) + pm.addPass(fir::createAlgebraicSimplificationPass()); pm.addPass(mlir::createCSEPass()); fir::addMemoryAllocationOpt(pm); @@ -191,9 +199,10 @@ /// Create a pass pipeline for lowering from MLIR to LLVM IR /// /// \param pm - MLIR pass manager that will hold the pipeline definition -inline void createMLIRToLLVMPassPipeline(mlir::PassManager &pm) { +inline void createMLIRToLLVMPassPipeline( + mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel) { // Add default optimizer pass pipeline. - fir::createDefaultFIROptimizerPassPipeline(pm); + fir::createDefaultFIROptimizerPassPipeline(pm, optLevel); // Add codegen pass pipeline. fir::createDefaultFIRCodeGenPassPipeline(pm); 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 @@ -103,7 +103,13 @@ unsigned defaultOpt = llvm::CodeGenOpt::None; opts.OptimizationLevel = clang::getLastArgIntValue( args, clang::driver::options::OPT_O, defaultOpt, diags); - + // -O0 is not classified as OPT_O, so we have to check for it + // explicitly, otherwise, "-O2 -O0" sets opts.OptimizationLevel + // to 2 above. + if (const llvm::opt::Arg *a = + args.getLastArg(clang::driver::options::OPT_O_Group)) + if (a->getOption().matches(clang::driver::options::OPT_O0)) + opts.OptimizationLevel = 0; if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager, clang::driver::options::OPT_fno_debug_pass_manager, false)) opts.DebugPassManager = 1; 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 @@ -479,11 +479,29 @@ #include "flang/Tools/CLOptions.inc" +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; + } +} + // Lower the previously generated MLIR module into an LLVM IR module void CodeGenAction::generateLLVMIR() { assert(mlirModule && "The MLIR module has not been generated yet."); CompilerInstance &ci = this->getInstance(); + auto opts = ci.getInvocation().getCodeGenOpts(); + llvm::OptimizationLevel level = mapToLevel(opts); fir::support::loadDialects(*mlirCtx); fir::support::registerLLVMTranslation(*mlirCtx); @@ -495,7 +513,7 @@ pm.enableVerifier(/*verifyPasses=*/true); // Create the pass pipeline - fir::createMLIRToLLVMPassPipeline(pm); + fir::createMLIRToLLVMPassPipeline(pm, level); mlir::applyPassManagerCLOptions(pm); // run the pass manager @@ -630,22 +648,6 @@ codeGenPasses.run(llvmModule); } -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); diff --git a/flang/lib/Optimizer/Transforms/AlgebraicSimplification.cpp b/flang/lib/Optimizer/Transforms/AlgebraicSimplification.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Transforms/AlgebraicSimplification.cpp @@ -0,0 +1,30 @@ +//===- AlgebraicSimplification.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 "PassDetail.h" +#include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/Math/Transforms/Passes.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" + +using namespace mlir; + +namespace { +struct AlgebraicSimplification + : public fir::AlgebraicSimplificationBase { + + void runOnOperation() override { + RewritePatternSet patterns(&getContext()); + populateMathAlgebraicSimplificationPatterns(patterns); + (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + } +}; +} // namespace + +std::unique_ptr fir::createAlgebraicSimplificationPass() { + return std::make_unique(); +} diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt --- a/flang/lib/Optimizer/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt @@ -10,6 +10,7 @@ MemRefDataFlowOpt.cpp RewriteLoop.cpp SimplifyRegionLite.cpp + AlgebraicSimplification.cpp DEPENDS FIRBuilder @@ -23,6 +24,7 @@ MLIRAffineUtils MLIRFuncDialect MLIRLLVMDialect + MLIRMathTransforms MLIROpenACCDialect MLIROpenMPDialect FIRSupport diff --git a/flang/lib/Optimizer/Transforms/PassDetail.h b/flang/lib/Optimizer/Transforms/PassDetail.h --- a/flang/lib/Optimizer/Transforms/PassDetail.h +++ b/flang/lib/Optimizer/Transforms/PassDetail.h @@ -12,6 +12,7 @@ #include "mlir/Dialect/Affine/IR/AffineOps.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/Math/IR/Math.h" #include "mlir/Dialect/OpenACC/OpenACC.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/Pass/Pass.h" diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90 --- a/flang/test/Driver/mlir-pass-pipeline.f90 +++ b/flang/test/Driver/mlir-pass-pipeline.f90 @@ -1,35 +1,52 @@ ! Test the MLIR pass pipeline -! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -o - 2>&1 | FileCheck %s +! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s 2>&1 | FileCheck --check-prefixes=ALL %s +! -O0 is the default: +! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -O0 2>&1 | FileCheck --check-prefixes=ALL %s +! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -O2 2>&1 | FileCheck --check-prefixes=ALL,O2 %s +! Verify that -O0 overrides -O2: +! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -O2 -O0 2>&1 | FileCheck --check-prefixes=ALL %s end program -! CHECK: Pass statistics report +! ALL: Pass statistics report -! CHECK: CSE -! CHECK-LABEL: 'func.func' Pipeline -! CHECK: ArrayValueCopy -! CHECK: CharacterConversion -! CHECK: Canonicalizer -! CHECK: SimplifyRegionLite -! CHECK: CSE - -! CHECK-LABEL: 'func.func' Pipeline -! CHECK: MemoryAllocationOpt -! CHECK: Inliner -! CHECK: CSE - -! CHECK-LABEL: 'func.func' Pipeline -! CHECK: CFGConversion -! CHECK: SCFToControlFlow -! CHECK: Canonicalizer -! CHECK: SimplifyRegionLite -! CHECK: CSE -! CHECK: BoxedProcedurePass - -! CHECK-LABEL: 'func.func' Pipeline -! CHECK: AbstractResultOpt -! CHECK: CodeGenRewrite -! CHECK: TargetRewrite -! CHECK: ExternalNameConversion -! CHECK: FIRToLLVMLowering -! CHECK-NOT: LLVMIRLoweringPass +! ALL: Fortran::lower::VerifierPass +! ALL-NEXT: CSE +! Ideally, we need an output with only the pass names, but +! there is currently no way to get that, so in order to +! guarantee that the passes are in the expected order +! (i.e. use -NEXT) we have to check the statistics output as well. +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +! ALL-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! ALL-NEXT: 'func.func' Pipeline +! ALL-NEXT: ArrayValueCopy +! ALL-NEXT: CharacterConversion +! ALL-NEXT: Canonicalizer +! ALL-NEXT: SimplifyRegionLite +! O2-NEXT: AlgebraicSimplification +! ALL-NEXT: CSE +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +! ALL-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! ALL-NEXT: 'func.func' Pipeline +! ALL-NEXT: MemoryAllocationOpt +! ALL-NEXT: Inliner +! ALL-NEXT: CSE +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +! ALL-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! ALL-NEXT: 'func.func' Pipeline +! ALL-NEXT: CFGConversion +! ALL-NEXT: SCFToControlFlow +! ALL-NEXT: Canonicalizer +! ALL-NEXT: SimplifyRegionLite +! ALL-NEXT: CSE +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +! ALL-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! ALL-NEXT: BoxedProcedurePass +! ALL-NEXT: 'func.func' Pipeline +! ALL-NEXT: AbstractResultOpt +! ALL-NEXT: CodeGenRewrite +! ALL-NEXT: (S) 0 num-dce'd - Number of operations eliminated +! ALL-NEXT: TargetRewrite +! ALL-NEXT: ExternalNameConversion +! ALL-NEXT: FIRToLLVMLowering +! ALL-NOT: LLVMIRLoweringPass