Skip to content

Commit

Permalink
[flang] Run algebraic simplification optimization pass.
Browse files Browse the repository at this point in the history
Try 2 to merge 4fbd1d6.

Flang algebraic simplification pass will run algebraic simplification
rewrite patterns for Math/Complex/etc. dialects. It is enabled
under opt-for-speed optimization levels (i.e. for O1/O2/O3; Os/Oz will not
enable it).

With this change the FIR/MLIR optimization pipeline becomes affected
by the -O* optimization level switches. Until now these switches
only affected the middle-end and back-end.

Differential Revision: https://reviews.llvm.org/D130035
  • Loading branch information
vzakhari committed Jul 21, 2022
1 parent a935a0b commit 9c0acc4
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 81 deletions.
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Transforms/Passes.h
Expand Up @@ -39,6 +39,7 @@ std::unique_ptr<mlir::Pass>
createMemoryAllocationPass(bool dynOnHeap, std::size_t maxStackSize);
std::unique_ptr<mlir::Pass> createAnnotateConstantOperandsPass();
std::unique_ptr<mlir::Pass> createSimplifyRegionLitePass();
std::unique_ptr<mlir::Pass> createAlgebraicSimplificationPass();

// declarative passes
#define GEN_PASS_REGISTRATION
Expand Down
13 changes: 13 additions & 0 deletions flang/include/flang/Optimizer/Transforms/Passes.td
Expand Up @@ -196,4 +196,17 @@ def SimplifyRegionLite : Pass<"simplify-region-lite", "mlir::ModuleOp"> {
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
18 changes: 15 additions & 3 deletions flang/include/flang/Tools/CLOptions.inc
Expand Up @@ -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) \
Expand Down Expand Up @@ -50,6 +51,10 @@ static llvm::cl::opt<bool> ignoreMissingTypeDescriptors(
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");
Expand Down Expand Up @@ -150,7 +155,8 @@ inline void addExternalNameConversionPass(mlir::PassManager &pm) {
/// 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;
Expand All @@ -159,6 +165,9 @@ inline void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm) {
pm.addNestedPass<mlir::func::FuncOp>(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);

Expand Down Expand Up @@ -191,9 +200,12 @@ inline void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm) {
/// 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);
Expand Down
36 changes: 19 additions & 17 deletions flang/lib/Frontend/FrontendActions.cpp
Expand Up @@ -479,11 +479,29 @@ CodeGenAction::~CodeGenAction() = default;

#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);
Expand All @@ -495,7 +513,7 @@ void CodeGenAction::generateLLVMIR() {
pm.enableVerifier(/*verifyPasses=*/true);

// Create the pass pipeline
fir::createMLIRToLLVMPassPipeline(pm);
fir::createMLIRToLLVMPassPipeline(pm, level);
mlir::applyPassManagerCLOptions(pm);

// run the pass manager
Expand Down Expand Up @@ -630,22 +648,6 @@ static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
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);
Expand Down
37 changes: 37 additions & 0 deletions 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<AlgebraicSimplification> {

void runOnOperation() override;
};
} // namespace

void AlgebraicSimplification::runOnOperation() {
RewritePatternSet patterns(&getContext());
populateMathAlgebraicSimplificationPatterns(patterns);
(void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns));
}

std::unique_ptr<mlir::Pass> fir::createAlgebraicSimplificationPass() {
return std::make_unique<AlgebraicSimplification>();
}
2 changes: 2 additions & 0 deletions flang/lib/Optimizer/Transforms/CMakeLists.txt
Expand Up @@ -10,6 +10,7 @@ add_flang_library(FIRTransforms
MemRefDataFlowOpt.cpp
RewriteLoop.cpp
SimplifyRegionLite.cpp
AlgebraicSimplification.cpp

DEPENDS
FIRBuilder
Expand All @@ -23,6 +24,7 @@ add_flang_library(FIRTransforms
MLIRAffineUtils
MLIRFuncDialect
MLIRLLVMDialect
MLIRMathTransforms
MLIROpenACCDialect
MLIROpenMPDialect
FIRSupport
Expand Down
1 change: 1 addition & 0 deletions flang/lib/Optimizer/Transforms/PassDetail.h
Expand Up @@ -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"
Expand Down
45 changes: 45 additions & 0 deletions 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
85 changes: 54 additions & 31 deletions 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

0 comments on commit 9c0acc4

Please sign in to comment.