| //===-- CLOptions.inc -- command line options -------------------*- 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 some shared command-line options that can be used when |
| /// debugging the test tools. This file must be included into the tool. |
| |
| #include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h" |
| #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" |
| #include "mlir/Pass/PassManager.h" |
| #include "mlir/Transforms/GreedyPatternRewriteDriver.h" |
| #include "mlir/Transforms/Passes.h" |
| #include "flang/Optimizer/CodeGen/CodeGen.h" |
| #include "flang/Optimizer/HLFIR/Passes.h" |
| #include "flang/Optimizer/Transforms/Passes.h" |
| #include "llvm/Passes/OptimizationLevel.h" |
| #include "llvm/Support/CommandLine.h" |
| |
| #define DisableOption(DOName, DOOption, DODescription) \ |
| static llvm::cl::opt<bool> disable##DOName("disable-" DOOption, \ |
| llvm::cl::desc("disable " DODescription " pass"), llvm::cl::init(false), \ |
| llvm::cl::Hidden) |
| |
| /// Shared option in tools to control whether dynamically sized array |
| /// allocations should always be on the heap. |
| static llvm::cl::opt<bool> dynamicArrayStackToHeapAllocation( |
| "fdynamic-heap-array", |
| llvm::cl::desc("place all array allocations of dynamic size on the heap"), |
| llvm::cl::init(false), llvm::cl::Hidden); |
| |
| /// Shared option in tools to set a maximum value for the number of elements in |
| /// a compile-time sized array that can be allocated on the stack. |
| static llvm::cl::opt<std::size_t> arrayStackAllocationThreshold( |
| "fstack-array-size", |
| llvm::cl::desc( |
| "place all array allocations more than <size> elements on the heap"), |
| llvm::cl::init(~static_cast<std::size_t>(0)), llvm::cl::Hidden); |
| |
| /// Shared option in tools to ignore missing runtime type descriptor objects |
| /// when translating FIR to LLVM. The resulting program will crash if the |
| /// runtime needs the derived type descriptors, this is only a debug option to |
| /// allow compiling manually written FIR programs involving derived types |
| /// without having to write the derived type descriptors which are normally |
| /// generated by the frontend. |
| static llvm::cl::opt<bool> ignoreMissingTypeDescriptors( |
| "ignore-missing-type-desc", |
| llvm::cl::desc("ignore failures to find derived type descriptors when " |
| "translating FIR to LLVM"), |
| 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}; |
| |
| const static llvm::codegenoptions::DebugInfoKind &NoDebugInfo{ |
| llvm::codegenoptions::NoDebugInfo}; |
| |
| /// Optimizer Passes |
| DisableOption(CfgConversion, "cfg-conversion", "disable FIR to CFG pass"); |
| DisableOption(FirAvc, "avc", "array value copy analysis and transformation"); |
| DisableOption( |
| FirMao, "memory-allocation-opt", "memory allocation optimization"); |
| |
| DisableOption(FirAliasTags, "fir-alias-tags", "fir alias analysis"); |
| static llvm::cl::opt<bool> useOldAliasTags("use-old-alias-tags", |
| llvm::cl::desc("Use a single TBAA tree for all functions and do not use " |
| "the FIR alias tags pass"), |
| llvm::cl::init(false), llvm::cl::Hidden); |
| |
| /// CodeGen Passes |
| #if !defined(FLANG_EXCLUDE_CODEGEN) |
| DisableOption(CodeGenRewrite, "codegen-rewrite", "rewrite FIR for codegen"); |
| DisableOption(TargetRewrite, "target-rewrite", "rewrite FIR for target"); |
| DisableOption(DebugFoundation, "debug-foundation", "Add debug foundation"); |
| DisableOption(FirToLlvmIr, "fir-to-llvmir", "FIR to LLVM-IR dialect"); |
| DisableOption(LlvmIrToLlvm, "llvm", "conversion to LLVM"); |
| DisableOption(BoxedProcedureRewrite, "boxed-procedure-rewrite", |
| "rewrite boxed procedures"); |
| #endif |
| |
| DisableOption(ExternalNameConversion, "external-name-interop", |
| "convert names with external convention"); |
| |
| /// Generic for adding a pass to the pass manager if it is not disabled. |
| template <typename F> |
| void addPassConditionally( |
| mlir::PassManager &pm, llvm::cl::opt<bool> &disabled, F ctor) { |
| if (!disabled) |
| pm.addPass(ctor()); |
| } |
| |
| template <typename OP, typename F> |
| void addNestedPassConditionally( |
| mlir::PassManager &pm, llvm::cl::opt<bool> &disabled, F ctor) { |
| if (!disabled) |
| pm.addNestedPass<OP>(ctor()); |
| } |
| |
| } // namespace |
| |
| namespace fir { |
| |
| /// Add MLIR Canonicalizer pass with region simplification disabled. |
| /// FIR does not support the promotion of some SSA value to block arguments (or |
| /// into arith.select operands) that may be done by mlir block merging in the |
| /// region simplification (e.g., !fir.shape<> SSA values are not supported as |
| /// block arguments). |
| /// Aside from the fir.shape issue, moving some abstract SSA value into block |
| /// arguments may have a heavy cost since it forces their code generation that |
| /// may be expensive (array temporary). The MLIR pass does not take these |
| /// extra costs into account when doing block merging. |
| static void addCanonicalizerPassWithoutRegionSimplification( |
| mlir::OpPassManager &pm) { |
| mlir::GreedyRewriteConfig config; |
| config.enableRegionSimplification = false; |
| pm.addPass(mlir::createCanonicalizerPass(config)); |
| } |
| |
| inline void addCfgConversionPass(mlir::PassManager &pm) { |
| addNestedPassConditionally<mlir::func::FuncOp>( |
| pm, disableCfgConversion, fir::createFirToCfgOnFuncPass); |
| addNestedPassConditionally<mlir::omp::DeclareReductionOp>( |
| pm, disableCfgConversion, fir::createFirToCfgOnReductionPass); |
| } |
| |
| inline void addAVC( |
| mlir::PassManager &pm, const llvm::OptimizationLevel &optLevel) { |
| ArrayValueCopyOptions options; |
| options.optimizeConflicts = optLevel.isOptimizingForSpeed(); |
| addNestedPassConditionally<mlir::func::FuncOp>( |
| pm, disableFirAvc, [&]() { return createArrayValueCopyPass(options); }); |
| } |
| |
| inline void addMemoryAllocationOpt(mlir::PassManager &pm) { |
| addNestedPassConditionally<mlir::func::FuncOp>(pm, disableFirMao, [&]() { |
| return fir::createMemoryAllocationPass( |
| dynamicArrayStackToHeapAllocation, arrayStackAllocationThreshold); |
| }); |
| } |
| |
| #if !defined(FLANG_EXCLUDE_CODEGEN) |
| inline void addCodeGenRewritePass(mlir::PassManager &pm) { |
| addPassConditionally( |
| pm, disableCodeGenRewrite, fir::createFirCodeGenRewritePass); |
| } |
| |
| inline void addTargetRewritePass(mlir::PassManager &pm) { |
| addPassConditionally(pm, disableTargetRewrite, []() { |
| return fir::createFirTargetRewritePass(fir::TargetRewriteOptions{}); |
| }); |
| } |
| |
| inline void addDebugInfoPass(mlir::PassManager &pm) { |
| addPassConditionally(pm, disableDebugFoundation, |
| [&]() { return fir::createAddDebugInfoPass(); }); |
| } |
| |
| inline void addFIRToLLVMPass( |
| mlir::PassManager &pm, const MLIRToLLVMPassPipelineConfig &config) { |
| fir::FIRToLLVMPassOptions options; |
| options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors; |
| options.applyTBAA = config.AliasAnalysis; |
| options.forceUnifiedTBAATree = useOldAliasTags; |
| addPassConditionally(pm, disableFirToLlvmIr, |
| [&]() { return fir::createFIRToLLVMPass(options); }); |
| } |
| |
| inline void addLLVMDialectToLLVMPass( |
| mlir::PassManager &pm, llvm::raw_ostream &output) { |
| addPassConditionally(pm, disableLlvmIrToLlvm, |
| [&]() { return fir::createLLVMDialectToLLVMPass(output); }); |
| } |
| |
| inline void addBoxedProcedurePass(mlir::PassManager &pm) { |
| addPassConditionally(pm, disableBoxedProcedureRewrite, |
| [&]() { return fir::createBoxedProcedurePass(); }); |
| } |
| #endif |
| |
| inline void addExternalNameConversionPass( |
| mlir::PassManager &pm, bool appendUnderscore = true) { |
| addPassConditionally(pm, disableExternalNameConversion, [&]() { |
| return fir::createExternalNameConversionPass(appendUnderscore); |
| }); |
| } |
| |
| /// Create a pass pipeline for running default optimization passes for |
| /// incremental conversion of FIR. |
| /// |
| /// \param pm - MLIR pass manager that will hold the pipeline definition |
| inline void createDefaultFIROptimizerPassPipeline( |
| mlir::PassManager &pm, const MLIRToLLVMPassPipelineConfig &pc) { |
| // simplify the IR |
| mlir::GreedyRewriteConfig config; |
| config.enableRegionSimplification = false; |
| pm.addPass(mlir::createCSEPass()); |
| fir::addAVC(pm, pc.OptLevel); |
| pm.addNestedPass<mlir::func::FuncOp>(fir::createCharacterConversionPass()); |
| pm.addPass(mlir::createCanonicalizerPass(config)); |
| pm.addPass(fir::createSimplifyRegionLitePass()); |
| if (pc.OptLevel.isOptimizingForSpeed()) { |
| // These passes may increase code size. |
| pm.addPass(fir::createSimplifyIntrinsicsPass()); |
| pm.addPass(fir::createAlgebraicSimplificationPass(config)); |
| } |
| |
| if (pc.LoopVersioning) |
| pm.addPass(fir::createLoopVersioningPass()); |
| |
| pm.addPass(mlir::createCSEPass()); |
| |
| if (pc.StackArrays) |
| pm.addPass(fir::createStackArraysPass()); |
| else |
| fir::addMemoryAllocationOpt(pm); |
| |
| // The default inliner pass adds the canonicalizer pass with the default |
| // configuration. Create the inliner pass with tco config. |
| llvm::StringMap<mlir::OpPassManager> pipelines; |
| pm.addPass(mlir::createInlinerPass( |
| pipelines, addCanonicalizerPassWithoutRegionSimplification)); |
| pm.addPass(fir::createSimplifyRegionLitePass()); |
| pm.addPass(mlir::createCSEPass()); |
| |
| // Polymorphic types |
| pm.addPass(fir::createPolymorphicOpConversionPass()); |
| |
| if (pc.AliasAnalysis && !disableFirAliasTags && !useOldAliasTags) |
| pm.addPass(fir::createAliasTagsPass()); |
| |
| // convert control flow to CFG form |
| fir::addCfgConversionPass(pm); |
| pm.addPass(mlir::createConvertSCFToCFPass()); |
| |
| pm.addPass(mlir::createCanonicalizerPass(config)); |
| pm.addPass(fir::createSimplifyRegionLitePass()); |
| pm.addPass(mlir::createCSEPass()); |
| } |
| |
| /// Create a pass pipeline for lowering from HLFIR to FIR |
| /// |
| /// \param pm - MLIR pass manager that will hold the pipeline definition |
| /// \param optLevel - optimization level used for creating FIR optimization |
| /// passes pipeline |
| inline void createHLFIRToFIRPassPipeline( |
| mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel) { |
| if (optLevel.isOptimizingForSpeed()) { |
| addCanonicalizerPassWithoutRegionSimplification(pm); |
| pm.addPass(hlfir::createSimplifyHLFIRIntrinsicsPass()); |
| } |
| pm.addPass(hlfir::createInlineElementalsPass()); |
| if (optLevel.isOptimizingForSpeed()) { |
| addCanonicalizerPassWithoutRegionSimplification(pm); |
| pm.addPass(mlir::createCSEPass()); |
| pm.addPass(hlfir::createOptimizedBufferizationPass()); |
| } |
| pm.addPass(hlfir::createLowerHLFIROrderedAssignmentsPass()); |
| pm.addPass(hlfir::createLowerHLFIRIntrinsicsPass()); |
| pm.addPass(hlfir::createBufferizeHLFIRPass()); |
| pm.addPass(hlfir::createConvertHLFIRtoFIRPass()); |
| } |
| |
| /// Create a pass pipeline for handling certain OpenMP transformations needed |
| /// prior to FIR lowering. |
| /// |
| /// WARNING: These passes must be run immediately after the lowering to ensure |
| /// that the FIR is correct with respect to OpenMP operations/attributes. |
| /// |
| /// \param pm - MLIR pass manager that will hold the pipeline definition. |
| /// \param isTargetDevice - Whether code is being generated for a target device |
| /// rather than the host device. |
| inline void createOpenMPFIRPassPipeline( |
| mlir::PassManager &pm, bool isTargetDevice) { |
| pm.addPass(fir::createOMPDescriptorMapInfoGenPass()); |
| pm.addPass(fir::createOMPMarkDeclareTargetPass()); |
| if (isTargetDevice) |
| pm.addPass(fir::createOMPFunctionFilteringPass()); |
| } |
| |
| #if !defined(FLANG_EXCLUDE_CODEGEN) |
| inline void createDebugPasses( |
| mlir::PassManager &pm, llvm::codegenoptions::DebugInfoKind debugLevel) { |
| // Currently only -g1, -g, -gline-tables-only supported |
| switch (debugLevel) { |
| case llvm::codegenoptions::DebugLineTablesOnly: |
| addDebugInfoPass(pm); |
| return; |
| case llvm::codegenoptions::NoDebugInfo: |
| return; |
| default: |
| // TODO: Add cases and passes for other debug options. |
| // All other debug options not implemented yet, currently emits warning |
| // and generates as much debug information as possible. |
| addDebugInfoPass(pm); |
| return; |
| } |
| } |
| |
| inline void createDefaultFIRCodeGenPassPipeline( |
| mlir::PassManager &pm, MLIRToLLVMPassPipelineConfig config) { |
| fir::addBoxedProcedurePass(pm); |
| pm.addNestedPass<mlir::func::FuncOp>( |
| fir::createAbstractResultOnFuncOptPass()); |
| pm.addNestedPass<fir::GlobalOp>(fir::createAbstractResultOnGlobalOptPass()); |
| fir::addCodeGenRewritePass(pm); |
| fir::addTargetRewritePass(pm); |
| fir::addExternalNameConversionPass(pm, config.Underscoring); |
| fir::createDebugPasses(pm, config.DebugInfo); |
| |
| if (config.VScaleMin != 0) |
| pm.addPass(fir::createVScaleAttrPass({config.VScaleMin, config.VScaleMax})); |
| |
| // Add function attributes |
| fir::FunctionAttrTypes functionAttrs; |
| |
| if (config.FramePointerKind != llvm::FramePointerKind::None || |
| config.NoInfsFPMath || config.NoNaNsFPMath || config.ApproxFuncFPMath || |
| config.NoSignedZerosFPMath || config.UnsafeFPMath) { |
| if (config.FramePointerKind == llvm::FramePointerKind::NonLeaf) |
| functionAttrs.framePointerKind = |
| mlir::LLVM::framePointerKind::FramePointerKind::NonLeaf; |
| else if (config.FramePointerKind == llvm::FramePointerKind::All) |
| functionAttrs.framePointerKind = |
| mlir::LLVM::framePointerKind::FramePointerKind::All; |
| else |
| functionAttrs.framePointerKind = |
| mlir::LLVM::framePointerKind::FramePointerKind::None; |
| |
| pm.addPass(fir::createFunctionAttrPass(functionAttrs, config.NoInfsFPMath, |
| config.NoNaNsFPMath, config.ApproxFuncFPMath, |
| config.NoSignedZerosFPMath, config.UnsafeFPMath)); |
| } |
| |
| fir::addFIRToLLVMPass(pm, config); |
| } |
| |
| /// Create a pass pipeline for lowering from MLIR to LLVM IR |
| /// |
| /// \param pm - MLIR pass manager that will hold the pipeline definition |
| /// \param optLevel - optimization level used for creating FIR optimization |
| /// passes pipeline |
| inline void createMLIRToLLVMPassPipeline( |
| mlir::PassManager &pm, const MLIRToLLVMPassPipelineConfig &config) { |
| fir::createHLFIRToFIRPassPipeline(pm, config.OptLevel); |
| |
| // Add default optimizer pass pipeline. |
| fir::createDefaultFIROptimizerPassPipeline(pm, config); |
| |
| // Add codegen pass pipeline. |
| fir::createDefaultFIRCodeGenPassPipeline(pm, config); |
| } |
| #undef FLANG_EXCLUDE_CODEGEN |
| #endif |
| |
| } // namespace fir |