blob: 1b556bc9b6457e3f47ced0e2fca7302dc80a798d [file] [edit]
// Copyright 2023 The IREE Authors //
// Licensed 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 "iree/compiler/GlobalOptimization/Passes.h"
#include "iree/compiler/Dialect/Flow/Transforms/Passes.h"
#include "iree/compiler/Dialect/TensorExt/IR/TensorExtDialect.h"
#include "iree/compiler/Dialect/Util/Transforms/Passes.h"
#include "iree/compiler/DispatchCreation/Passes.h"
#include "iree/compiler/Modules/IO/Parameters/Transforms/Passes.h"
#include "iree/compiler/Utils/PassUtils.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/MemRef/Transforms/Passes.h"
#include "mlir/Transforms/Passes.h"
namespace mlir::iree_compiler::GlobalOptimization {
using FunctionLikeNest =
MultiOpNest<IREE::Util::InitializerOp, IREE::Util::FuncOp>;
static llvm::cl::opt<bool> clEnableQuantizedMatmulReassociation(
"iree-global-opt-enable-quantized-matmul-reassociation",
llvm::cl::desc(
"Enables reassociation of quantized matmul ops (experimental)."),
llvm::cl::init(false));
static llvm::cl::opt<bool> clConvertQDQToIntegerMath(
"iree-global-opt-enable-qdq-to-integer-math",
llvm::cl::desc("Rewrites contractions over dequantized operands into "
"integer contractions with zero point corrections ("
"changes floating-point rounding and exceptional-value "
"behavior)."),
llvm::cl::init(true));
static llvm::cl::opt<bool> clEnableTransposePropagation(
"iree-global-opt-propagate-transposes",
llvm::cl::desc(
"Enables propagation of transpose ops to improve fusion chances."),
llvm::cl::init(true));
static llvm::cl::opt<bool> clEnableAttentionVTranspose(
"iree-global-opt-enable-attention-v-transpose",
llvm::cl::desc("Enables transposition of v operand of attention ops,"),
llvm::cl::init(true));
// TODO(hanchung): Remove the flag. We don't want to do early materialization by
// default. Because it won't work for heterogeneous computing. This is not the
// right layer for handling such information.
static llvm::cl::opt<bool> clEnableEarlyMaterialization(
"iree-global-opt-enable-early-materialization",
llvm::cl::desc(
"Enables early materialization on encodings. Note, this flag should be "
"false eventually. This does not work for heterogeneous computing."),
llvm::cl::init(true));
static llvm::cl::opt<DemoteType> clDemoteContractionInputsType(
"iree-global-opt-demote-contraction-inputs-type",
llvm::cl::desc(
"Demotes inputs (LHS, RHS) of contraction ops to a narrow type."),
llvm::cl::values(clEnumValN(DemoteType::F16, "f16", "Demote to f16."),
clEnumValN(DemoteType::BF16, "bf16", "Demote to bf16.")));
static llvm::cl::opt<DemoteOperation> clDemoteContractionInputsOperations(
"iree-global-opt-demote-contraction-inputs-operations",
llvm::cl::desc("Select the type of contraction ops to demote."),
llvm::cl::values(clEnumValN(DemoteOperation::All, "all",
"Demote all contraction ops."),
clEnumValN(DemoteOperation::Conv, "conv",
"Only demote convolution ops."),
clEnumValN(DemoteOperation::Matmul, "matmul",
"Only demote matmul ops."),
clEnumValN(DemoteOperation::None, "none",
"Demote no contraction ops.")),
llvm::cl::init(DemoteOperation::None));
static llvm::cl::opt<DispatchCreation::EncodingOptions> clSetEncodingStrategy(
"iree-global-opt-set-encoding-strategy",
llvm::cl::desc("Set the encoding strategy for operations."),
llvm::cl::values(clEnumValN(
DispatchCreation::EncodingOptions::Generic, "generic",
"Using EncodingAttr which encodes as much information as possible")),
llvm::cl::init(DispatchCreation::EncodingOptions::Generic));
static llvm::cl::opt<bool> clWarnOnUninitializedValues(
"iree-global-opt-enable-warn-on-uninitialized-values",
llvm::cl::desc("Warn on some classes of uses of uninitialized values."),
llvm::cl::init(true));
static llvm::cl::opt<bool> clEnableEdgeReshapePropagation(
"iree-global-opt-experimental-enable-edge-reshape-propagation",
llvm::cl::desc(
"Enables propagation of reshapes on the edges of the program "
"in transpose propagation. This workaround for better performance and "
"will be removed soon."),
llvm::cl::init(false));
void buildGlobalOptExprHoistingPassPipeline(
OpPassManager &passManager, const TransformOptions &transformOptions) {
IREE::Util::ExprHoistingOptions options;
options.maxSizeIncreaseThreshold =
transformOptions.constExprMaxSizeIncreaseThreshold;
options.registerDependentDialectsFn = [](DialectRegistry &registry) {
registry.insert<IREE::TensorExt::IREETensorExtDialect>();
};
passManager.addPass(IREE::Util::createHoistIntoGlobalsPass(options));
}
void buildGlobalOptimizationPassPipeline(
OpPassManager &mainPassManager, const TransformOptions &transformOptions) {
// Import parameters before any global optimization passes so that the inlined
// parameters are available for folding.
if (!transformOptions.parameterImportPaths.empty()) {
IREE::IO::Parameters::ImportParametersPassOptions importParametersOptions;
importParametersOptions.scopePaths.assign(
transformOptions.parameterImportPaths.begin(),
transformOptions.parameterImportPaths.end());
importParametersOptions.keys.assign(
transformOptions.parameterImportKeys.begin(),
transformOptions.parameterImportKeys.end());
importParametersOptions.maximumSize =
transformOptions.parameterImportMaximumSize;
mainPassManager.addPass(IREE::IO::Parameters::createImportParametersPass(
importParametersOptions));
}
// Preprocessing passes to get the program into a canonical state.
FunctionLikeNest(mainPassManager)
.addPredicatedPass(clWarnOnUninitializedValues,
createWarnOnUninitializedValuesPass)
.addPredicatedPass(transformOptions.stripAssertions,
IREE::Util::createStripDebugOpsPass)
.addPass(IREE::Util::createOptimizeIntArithmeticPass)
.addPass(createLinalgQuantizedConvToConvPass)
.addPass(createLinalgQuantizedMatmulToMatmulPass)
// Match direct dequantize producers before convolution lowering and
// unit-dimension folding insert reshapes between them and the
// contraction.
.addPredicatedPass(clConvertQDQToIntegerMath,
createConvertQDQToIntegerMathPass)
.addPredicatedPass(transformOptions.useIm2colForConvs,
createConvertConv2DToImg2ColPass)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(createRemoveZeroExtentTensorsPass)
.addPass(createDetachElementwiseFromNamedOpsPass)
.addPass(mlir::createSimplifyDepthwiseConvPass);
mainPassManager.addPass(createEraseUnusedLinalgOperandsPass());
// Expand tensor shapes into SSA values and optimize the whole program.
// The more we are able to equate shape dimensions at this level the
// better our fusions will be.
mainPassManager.addPass(createExpandTensorShapesPass());
FunctionLikeNest(mainPassManager)
// Preprocess the input to a form more amenable for fusion
// - Convert all elementwise ops to Linalg
// - Remove unit-extent dimensions.
.addPass(mlir::createConvertElementwiseToLinalgPass)
// RaiseSpecialOps, by virtue of implementing various peephole
// optimizations, is sensitive to surrounding IR structure. Thus we run
// this pass both before unit dim folding + consteval, as well as after.
.addPass(createRaiseSpecialOpsPass)
// We decompose and transpose concatenations immediately before folding
// unit extent dims because this allows decoupling unit dims in the
// concatenation from the transposes that are introduced.
.addPass([&]() {
return createDecomposeConcatPass(transformOptions.outerDimConcat);
})
// We generalize certain named ops immediately before folding unit extent
// dims as the unit dim folding pass updates indexing maps and is better
// at working with generics. By this point we have already done any
// specialized raising and the op names are no longer useful.
.addPass([&]() {
GeneralizeLinalgNamedOpsPassOptions opt;
opt.enableGeneralizeMatmul = transformOptions.generalizeMatmul;
return createGeneralizeLinalgNamedOpsPass(opt);
})
.addPredicatedPass(!clEnableEdgeReshapePropagation,
DispatchCreation::createInsertTensorBarriersPass);
mainPassManager.addPass(DispatchCreation::createFoldUnitExtentDimsPass());
FunctionLikeNest(mainPassManager)
.addPass(DispatchCreation::createFoldReshapesIntoTensorBarriersPass)
.addPass([&]() {
return createDemoteContractionInputsPass(
clDemoteContractionInputsType, clDemoteContractionInputsOperations);
})
.addPredicatedPass(clEnableQuantizedMatmulReassociation,
createFuseDequantizationMatmulPass)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(mlir::createCSEPass)
// Convert broadcast + batch_matmul before transpose propagation so we
// can match the pure broadcast patterns before they get fused with
// transposes.
.addPass(GlobalOptimization::createConvertBatchMatmulToMatmulPass);
FunctionLikeNest(mainPassManager)
// Propagate transposes immediately before set encoding/data tiling
// because transpose propagation cannot take an opinion on the preferred
// layout of various operations. This simplifies local propagation
// decisions as SetEncoding is expected to pick the ideal layout for
// that operation anyway, and this way we only need to make such a
// decision once.
.addPredicatedPass(clEnableTransposePropagation,
[&]() {
PropagateLinalgTransposePassOptions options;
options.enableAggressivePropagation =
transformOptions.aggressiveTransposePropagation;
options.enableConvolutionPropagation =
transformOptions.propagateTransposesThroughConv;
options.enableSinkTransposeThroughPad =
transformOptions.sinkTransposeThroughPad;
options.enableAttentionVTranspose =
clEnableAttentionVTranspose;
options.enableEdgeReshapePropagation =
clEnableEdgeReshapePropagation;
return createPropagateLinalgTransposePass(options);
})
// Retry for contractions whose dequantize producers were exposed by
// reshape/transpose propagation, before selecting integer layouts.
.addPredicatedPass(clConvertQDQToIntegerMath,
createConvertQDQToIntegerMathPass)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(mlir::createCSEPass);
mainPassManager.addPass(
GlobalOptimization::createConvertStridedContractionToContractionPass());
// Enable data tiling after they are in a canonical form.
if (transformOptions.dataTiling) {
FunctionLikeNest(mainPassManager)
.addPass(DispatchCreation::createAnnotateDataTilingHintsPass)
.addPass([&]() {
return DispatchCreation::createSetEncodingPass(
DispatchCreation::SetEncodingPassOptions{clSetEncodingStrategy});
});
if (clEnableEarlyMaterialization) {
mainPassManager.addPass(createMaterializeHomogeneousEncodingsPass());
}
mainPassManager.addPass(IREE::Flow::createCanonicalizePass());
mainPassManager.addPass(createCSEPass());
mainPassManager.addPass(createSimplifyPackUnpackPass());
FunctionLikeNest(mainPassManager).addPass(createDataLayoutPropagationPass);
}
FunctionLikeNest(mainPassManager)
// Generalize transposes and any other remaining named linalg ops that can
// now be represented as generics.
.addPass(createGeneralizeLinalgNamedOpsPass)
// Hoist loop invariants (e.g. from scf loops) with zero-trip-check.
.addPass(createGlobalLoopInvariantCodeMotionPass)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(mlir::createCSEPass)
// Simplify util.global accesses early on; this can help with dispatch
// region formation as redundant store-loads are removed.
.addPass(IREE::Util::createSimplifyGlobalAccessesPass)
// Aggressive cleanup.
.addPass(IREE::Util::createApplyPatternsPass);
// Module level cleanup and canonicalization of util.global (and other
// util ops).
mainPassManager.addPass(IREE::Util::createFoldGlobalsPass());
mainPassManager.addPass(IREE::Util::createIPOPass());
FunctionLikeNest(mainPassManager)
.addPass(IREE::Util::createOptimizeIntArithmeticPass)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(createCSEPass);
if (transformOptions.constExprHoisting) {
buildGlobalOptExprHoistingPassPipeline(mainPassManager, transformOptions);
}
if (transformOptions.buildConstEvalPassPipeline) {
transformOptions.buildConstEvalPassPipeline(mainPassManager);
}
if (transformOptions.numericPrecisionReduction) {
mainPassManager.addPass(createInferNumericNarrowingPass());
mainPassManager.addPass(createOptimizeNumericsPass());
mainPassManager.addPass(createCleanupNumericNarrowingPass());
}
FunctionLikeNest(mainPassManager)
.addPass(IREE::Flow::createCanonicalizePass)
.addPass(mlir::createCSEPass)
// After running const-eval to a fixed point and folding unit extent dims,
// try any new raising opportunities.
.addPass(createRaiseSpecialOpsPass);
// Export after const-eval. If the user wants to keep the input constants
// as is in the final parameter archive, they will probably want to disable
// const-eval, or could run this pass as preprocessing. There might be a
// configuration in the future where users want to limit const-eval to smaller
// constants that aren't exported and skip it for larger parameters, but this
// is a sensible place for the common case of wanting const-eval in the final
// artifact + archive.
if (!transformOptions.parameterExportPath.empty()) {
IREE::IO::Parameters::ExportParametersPassOptions exportParametersOptions;
exportParametersOptions.scopePath = transformOptions.parameterExportPath;
exportParametersOptions.minimumSize =
transformOptions.parameterExportMinimumSize;
mainPassManager.addPass(IREE::IO::Parameters::createExportParametersPass(
exportParametersOptions));
}
if (!transformOptions.parameterSplatPath.empty()) {
IREE::IO::Parameters::GenerateSplatParameterArchivePassOptions
generateSplatOptions;
generateSplatOptions.filePath = transformOptions.parameterSplatPath;
mainPassManager.addPass(
IREE::IO::Parameters::createGenerateSplatParameterArchivePass(
generateSplatOptions));
}
}
namespace {
#define GEN_PASS_REGISTRATION
#include "iree/compiler/GlobalOptimization/Passes.h.inc" // IWYU pragma: export
} // namespace
void registerGlobalOptimizationPipeline() {
registerPasses();
PassPipelineRegistration<TransformOptions>
globalOptimizationTransformPassPipeline(
"iree-global-optimization-transformation-pipeline",
"Runs the IREE global optimization transformation pipeline",
[](OpPassManager &passManager,
const TransformOptions &transformOptions) {
buildGlobalOptimizationPassPipeline(passManager, transformOptions);
});
PassPipelineRegistration<TransformOptions>
globalOptimizationConstantHoistingPassPipeline(
"iree-global-optimization-hoist-constant-expressions",
"Hoists constant expressions with the preferred storage types for "
"global optimization",
[](OpPassManager &passManager,
const TransformOptions &transformOptions) {
buildGlobalOptExprHoistingPassPipeline(passManager,
transformOptions);
});
}
} // namespace mlir::iree_compiler::GlobalOptimization