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,10 @@ llvm::cl::init(false), llvm::cl::Hidden); namespace { +/// Default optimization level used to create Flang pass pipeline is O0. +const static llvm::OptimizationLevel &defaultOptLevel{ + llvm::OptimizationLevel::O0}; + /// Optimizer Passes DisableOption(CfgConversion, "cfg-conversion", "disable FIR to CFG pass"); DisableOption(FirAvc, "avc", "array value copy analysis and transformation"); @@ -150,7 +155,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 +165,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 +200,12 @@ /// 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) { +/// \param optLevel - optimization level used for creating FIR optimization +/// passes pipeline +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/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,37 @@ +//===- AlgebraicSimplification.cpp - Simplify algebraic expressions -------===// +// +// 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 a pass that applies algebraic simplifications +// to operations of Math/Complex/etc. dialects that are used by Flang. +// It is done as a Flang specific pass, because we may want to tune +// the parameters of the patterns for Fortran programs. +//===----------------------------------------------------------------------===// + +#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; +}; +} // namespace + +void AlgebraicSimplification::runOnOperation() { + RewritePatternSet patterns(&getContext()); + populateMathAlgebraicSimplificationPatterns(patterns); + (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); +} + +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/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90 @@ -0,0 +1,45 @@ +! Test the MLIR pass pipeline + +! RUN: bbc --mlir-pass-statistics --mlir-pass-statistics-display=pipeline %s 2>&1 | FileCheck %s +end program + +! CHECK: Pass statistics report + +! CHECK: Fortran::lower::VerifierPass +! CHECK-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. +! CHECK-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +! CHECK-NEXT: 'func.func' Pipeline +! CHECK-NEXT: ArrayValueCopy +! CHECK-NEXT: CharacterConversion + +! CHECK-NEXT: Canonicalizer +! CHECK-NEXT: SimplifyRegionLite +! CHECK-NEXT: AlgebraicSimplification +! CHECK-NEXT: CSE +! CHECK-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +! CHECK-NEXT: 'func.func' Pipeline +! CHECK-NEXT: MemoryAllocationOpt + +! CHECK-NEXT: Inliner +! CHECK-NEXT: CSE +! CHECK-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +! CHECK-NEXT: 'func.func' Pipeline +! CHECK-NEXT: CFGConversion + +! CHECK-NEXT: SCFToControlFlow +! CHECK-NEXT: Canonicalizer +! CHECK-NEXT: SimplifyRegionLite +! CHECK-NEXT: CSE +! CHECK-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +! CHECK-NOT: LLVMIRLoweringPass 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,38 +1,61 @@ ! 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 -o /dev/null %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 -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL %s +! RUN: %flang_fc1 -S -mmlir --mlir-pass-statistics -mmlir --mlir-pass-statistics-display=pipeline %s -O2 -o /dev/null 2>&1 | FileCheck --check-prefixes=ALL,O2 %s ! REQUIRES: asserts end program -! CHECK: 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: Pass statistics report + +! 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-cse'd - Number of operations CSE'd +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'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-cse'd - Number of operations CSE'd +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +! ALL-NEXT: 'func.func' Pipeline +! ALL-NEXT: MemoryAllocationOpt + +! ALL-NEXT: Inliner +! ALL-NEXT: CSE +! ALL-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'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-cse'd - Number of operations CSE'd +! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'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 diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir --- a/flang/test/Fir/basic-program.fir +++ b/flang/test/Fir/basic-program.fir @@ -16,30 +16,45 @@ // PASSES: Pass statistics report -// PASSES: CSE -// PASSES-LABEL: 'func.func' Pipeline -// PASSES: ArrayValueCopy -// PASSES: CharacterConversion -// PASSES: Canonicalizer -// PASSES: SimplifyRegionLite -// PASSES: CSE - -// PASSES-LABEL: 'func.func' Pipeline -// PASSES: MemoryAllocationOpt -// PASSES: Inliner -// PASSES: CSE - -// PASSES-LABEL: 'func.func' Pipeline -// PASSES: CFGConversion -// PASSES: SCFToControlFlow -// PASSES: Canonicalizer -// PASSES: SimplifyRegionLite -// PASSES: CSE -// PASSES: BoxedProcedurePass - -// PASSES-LABEL: 'func.func' Pipeline -// PASSES: AbstractResultOpt -// PASSES: CodeGenRewrite -// PASSES: TargetRewrite -// PASSES: FIRToLLVMLowering -// PASSES: LLVMIRLoweringPass +// PASSES: CSE +// PASSES-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +// PASSES-NEXT: 'func.func' Pipeline +// PASSES-NEXT: ArrayValueCopy +// PASSES-NEXT: CharacterConversion + +// PASSES-NEXT: Canonicalizer +// PASSES-NEXT: SimplifyRegionLite +// PASSES-NEXT: AlgebraicSimplification +// PASSES-NEXT: CSE +// PASSES-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +// PASSES-NEXT: 'func.func' Pipeline +// PASSES-NEXT: MemoryAllocationOpt + +// PASSES-NEXT: Inliner +// PASSES-NEXT: CSE +// PASSES-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd + +// PASSES-NEXT: 'func.func' Pipeline +// PASSES-NEXT: CFGConversion + +// PASSES-NEXT: SCFToControlFlow +// PASSES-NEXT: Canonicalizer +// PASSES-NEXT: SimplifyRegionLite +// PASSES-NEXT: CSE +// PASSES-NEXT: (S) 0 num-cse'd - Number of operations CSE'd +// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd +// PASSES-NEXT: BoxedProcedurePass + +// PASSES-NEXT: 'func.func' Pipeline +// PASSES-NEXT: AbstractResultOpt + +// PASSES-NEXT: CodeGenRewrite +// PASSES-NEXT: (S) 0 num-dce'd - Number of operations eliminated +// PASSES-NEXT: TargetRewrite +// PASSES-NEXT: FIRToLLVMLowering +// PASSES-NEXT: LLVMIRLoweringPass diff --git a/flang/tools/bbc/CMakeLists.txt b/flang/tools/bbc/CMakeLists.txt --- a/flang/tools/bbc/CMakeLists.txt +++ b/flang/tools/bbc/CMakeLists.txt @@ -19,4 +19,5 @@ FortranEvaluate FortranSemantics FortranLower +LLVMPasses ) diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -47,6 +47,7 @@ #include "mlir/Pass/PassRegistry.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "mlir/Transforms/Passes.h" +#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" @@ -255,8 +256,8 @@ // run the default canned pipeline pm.addPass(std::make_unique()); - // Add default optimizer pass pipeline. - fir::createDefaultFIROptimizerPassPipeline(pm); + // Add O2 optimizer pass pipeline. + fir::createDefaultFIROptimizerPassPipeline(pm, llvm::OptimizationLevel::O2); } if (mlir::succeeded(pm.run(mlirModule))) { diff --git a/flang/tools/tco/CMakeLists.txt b/flang/tools/tco/CMakeLists.txt --- a/flang/tools/tco/CMakeLists.txt +++ b/flang/tools/tco/CMakeLists.txt @@ -21,4 +21,5 @@ MLIRParser MLIRSupport MLIRVectorToLLVM + LLVMPasses ) diff --git a/flang/tools/tco/tco.cpp b/flang/tools/tco/tco.cpp --- a/flang/tools/tco/tco.cpp +++ b/flang/tools/tco/tco.cpp @@ -24,6 +24,7 @@ #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/Passes.h" +#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" @@ -111,7 +112,8 @@ if (mlir::failed(passPipeline.addToPipeline(pm, errorHandler))) return mlir::failure(); } else { - fir::createMLIRToLLVMPassPipeline(pm); + // Run tco with O2 by default. + fir::createMLIRToLLVMPassPipeline(pm, llvm::OptimizationLevel::O2); fir::addLLVMDialectToLLVMPass(pm, out.os()); }