[flang] Run algebraic simplification optimization pass.

Try 2 to merge 4fbd1d6c87.

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
This commit is contained in:
Slava Zakharin 2022-07-14 16:50:41 -07:00
parent a935a0bf50
commit 9c0acc42e8
14 changed files with 230 additions and 74 deletions

View File

@ -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

View File

@ -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

View File

@ -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 @@ 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");
@ -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;
@ -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);
@ -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);

View File

@ -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);
@ -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
@ -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);

View File

@ -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>();
}

View File

@ -10,6 +10,7 @@ add_flang_library(FIRTransforms
MemRefDataFlowOpt.cpp
RewriteLoop.cpp
SimplifyRegionLite.cpp
AlgebraicSimplification.cpp
DEPENDS
FIRBuilder
@ -23,6 +24,7 @@ add_flang_library(FIRTransforms
MLIRAffineUtils
MLIRFuncDialect
MLIRLLVMDialect
MLIRMathTransforms
MLIROpenACCDialect
MLIROpenMPDialect
FIRSupport

View File

@ -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"

View File

@ -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

View File

@ -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
! ALL: Pass statistics report
! CHECK: CSE
! CHECK-LABEL: 'func.func' Pipeline
! CHECK: ArrayValueCopy
! CHECK: CharacterConversion
! CHECK: Canonicalizer
! CHECK: SimplifyRegionLite
! CHECK: CSE
! 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
! CHECK-LABEL: 'func.func' Pipeline
! CHECK: MemoryAllocationOpt
! CHECK: Inliner
! CHECK: CSE
! ALL-NEXT: 'func.func' Pipeline
! ALL-NEXT: ArrayValueCopy
! ALL-NEXT: CharacterConversion
! CHECK-LABEL: 'func.func' Pipeline
! CHECK: CFGConversion
! CHECK: SCFToControlFlow
! CHECK: Canonicalizer
! CHECK: SimplifyRegionLite
! CHECK: CSE
! CHECK: BoxedProcedurePass
! 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
! CHECK-LABEL: 'func.func' Pipeline
! CHECK: AbstractResultOpt
! CHECK: CodeGenRewrite
! CHECK: TargetRewrite
! CHECK: ExternalNameConversion
! CHECK: FIRToLLVMLowering
! CHECK-NOT: LLVMIRLoweringPass
! 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

View File

@ -16,30 +16,45 @@ func.func @_QQmain() {
// PASSES: Pass statistics report
// PASSES: CSE
// PASSES-LABEL: 'func.func' Pipeline
// PASSES: ArrayValueCopy
// PASSES: CharacterConversion
// PASSES: Canonicalizer
// PASSES: SimplifyRegionLite
// PASSES: CSE
// 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-LABEL: 'func.func' Pipeline
// PASSES: MemoryAllocationOpt
// PASSES: Inliner
// PASSES: CSE
// PASSES-NEXT: 'func.func' Pipeline
// PASSES-NEXT: ArrayValueCopy
// PASSES-NEXT: CharacterConversion
// PASSES-LABEL: 'func.func' Pipeline
// PASSES: CFGConversion
// PASSES: SCFToControlFlow
// PASSES: Canonicalizer
// PASSES: SimplifyRegionLite
// PASSES: CSE
// PASSES: BoxedProcedurePass
// 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-LABEL: 'func.func' Pipeline
// PASSES: AbstractResultOpt
// PASSES: CodeGenRewrite
// PASSES: TargetRewrite
// PASSES: FIRToLLVMLowering
// PASSES: LLVMIRLoweringPass
// 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

View File

@ -19,4 +19,5 @@ FortranParser
FortranEvaluate
FortranSemantics
FortranLower
LLVMPasses
)

View File

@ -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 @@ static mlir::LogicalResult convertFortranSourceToMLIR(
// run the default canned pipeline
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
// 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))) {

View File

@ -21,4 +21,5 @@ target_link_libraries(tco PRIVATE
MLIRParser
MLIRSupport
MLIRVectorToLLVM
LLVMPasses
)

View File

@ -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 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
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());
}