blob: 890d9c5eb0ce28860bc322aa82764f3131ba61b5 [file]
// Copyright 2019 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/Dialect/VM/Transforms/Passes.h"
#include <memory>
#include "iree/compiler/Dialect/Util/IR/UtilOps.h"
#include "iree/compiler/Dialect/Util/Transforms/Passes.h"
#include "iree/compiler/Dialect/VM/IR/VMOps.h"
#include "iree/compiler/Utils/PassUtils.h"
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
#include "mlir/Dialect/Affine/Passes.h"
#include "mlir/Dialect/Arith/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/SCF/Transforms/Passes.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Pass/PassRegistry.h"
#include "mlir/Transforms/Passes.h"
namespace mlir::iree_compiler::IREE::VM {
using FunctionLikeNest =
MultiOpNest<func::FuncOp, IREE::Util::InitializerOp, IREE::Util::FuncOp>;
//===----------------------------------------------------------------------===//
// Utilities
//===----------------------------------------------------------------------===//
static void addCleanupPatterns(OpPassManager &passManager) {
// TODO(benvanik): run in a fixed-point iteration pipeline.
// Standard MLIR cleanup.
FunctionLikeNest(passManager)
.addPass(mlir::createCanonicalizerPass)
.addPass(mlir::createCSEPass);
// Aggressive MLIR cleanup.
passManager.addNestedPass<IREE::VM::ModuleOp>(
IREE::VM::createDropUnusedCallsPass());
passManager.addPass(mlir::createSymbolDCEPass());
FunctionLikeNest(passManager)
// Simplify util.global accesses; this can help with data flow tracking as
// redundant store-loads are removed.
.addPass(IREE::Util::createSimplifyGlobalAccessesPass)
// Aggressive cleanup.
.addPass(IREE::Util::createApplyPatternsPass);
// Cleanup and canonicalization of util.global (and other util ops).
passManager.addPass(IREE::Util::createFoldGlobalsPass());
passManager.addPass(IREE::Util::createFuseGlobalsPass());
}
//===----------------------------------------------------------------------===//
// -iree-vm-transformation-pipeline
//===----------------------------------------------------------------------===//
void buildVMTransformPassPipeline(OpPassManager &passManager,
TargetOptions targetOptions) {
// HACK: we'd really prefer not to inline here but most of the subsequent
// passes are local only. We need to inline before we convert to the vm
// dialect as they all operate on our various input dialects. If we could
// control the inliner a bit more and not have it inline *everything* we could
// do a more conservative inlining here and then the final one after lowering
// to vm below.
passManager.addPass(mlir::createInlinerPass());
passManager.addPass(mlir::createSymbolDCEPass());
// Combine the initializers for all globals to allow us to optimize them
// together.
passManager.addPass(IREE::Util::createCombineInitializersPass());
FunctionLikeNest(passManager)
.addPass(mlir::createSCFForLoopCanonicalizationPass);
// This pass is sketchy as it can pessimize tight loops due to affine
// treating all indices as signed and the unsigned conversion pass not being
// able to handle that. The scf.for canonicalization does a decent job of
// removing trivial loops above and this catches the rest. It inserts nasty
// rem/div ops that we can never safely remove inside of the hot inner loop
// and that sucks. We still have this here for now as the cost of the rem/div
// are less than the cost of an additional loop that this could remove.
passManager.addNestedPass<func::FuncOp>(affine::createLoopCoalescingPass());
FunctionLikeNest(passManager)
.addPass(mlir::createLoopInvariantCodeMotionPass)
.addPass(mlir::createConvertSCFToCFPass)
// TODO: Maybe this should be a part of Affine lowering pass.
// Remove if it is added there.
// https://github.com/llvm/llvm-project/issues/78458
.addPass(affine::createAffineExpandIndexOpsPass)
.addPass(mlir::createLowerAffinePass)
.addPass(mlir::arith::createArithUnsignedWhenEquivalentPass);
// Propagate buffer subranges throughout the program - this should remove any
// remaining subspans and give us a smaller surface area during conversion.
passManager.addPass(IREE::Util::createPropagateSubrangesPass());
addCleanupPatterns(passManager);
// Convert std/util/etc -> VM, along with any other dialects implementing the
// VM conversion dialect interface.
passManager.addPass(createConversionPass(targetOptions));
// Hoist globals and get the final set that need to be initialized.
passManager.addNestedPass<IREE::VM::ModuleOp>(createReifyRodataTablesPass());
passManager.addNestedPass<IREE::VM::ModuleOp>(createHoistInlinedRodataPass());
passManager.addNestedPass<IREE::VM::ModuleOp>(createDeduplicateRodataPass());
addCleanupPatterns(passManager);
passManager.addNestedPass<IREE::VM::ModuleOp>(createResolveRodataLoadsPass());
// Catch any inlining opportunities we created during lowering.
passManager.addPass(mlir::createInlinerPass());
passManager.addPass(mlir::createSymbolDCEPass());
// Create global initialization functions (__init/__deinit).
addCleanupPatterns(passManager);
passManager.addNestedPass<IREE::VM::ModuleOp>(
createGlobalInitializationPass());
// Ideally we'd run this as part of a fixed-point iteration: CSE may need the
// canonicalizer to remove ops in order to get the equivalences it needs to
// work while the canonicalizer may require CSE for patterns to kick in (like
// "fold cmp if lhs=%0 && rhs=%0").
passManager.addPass(mlir::createCanonicalizerPass());
passManager.addPass(mlir::createCSEPass());
passManager.addPass(mlir::createCanonicalizerPass());
// Now that we've inlined/canonicalized/etc the initializers we can remove
// them if they are empty to save a few bytes in the binary and avoid a
// runtime initialization call.
passManager.addNestedPass<IREE::VM::ModuleOp>(
createDropEmptyModuleInitializersPass());
if (targetOptions.optimizeForStackSize) {
passManager.addNestedPass<IREE::VM::ModuleOp>(createSinkDefiningOpsPass());
}
}
void registerVMTransformPassPipeline() {
PassPipelineRegistration<> transformPassPipeline(
"iree-vm-transformation-pipeline",
"Runs the full IREE VM dialect transformation pipeline",
[](OpPassManager &passManager) {
buildVMTransformPassPipeline(passManager,
TargetOptions::FromFlags::get());
});
}
} // namespace mlir::iree_compiler::IREE::VM