//===- RemoveDeadValues.cpp - Remove Dead Values --------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // The goal of this pass is optimization (reducing runtime) by removing // unnecessary instructions. Unlike other passes that rely on local information // gathered from patterns to accomplish optimization, this pass uses a full // analysis of the IR, specifically, liveness analysis, and is thus more // powerful. // // Currently, this pass performs the following optimizations: // (A) Removes function arguments that are not live, // (B) Removes function return values that are not live across all callers of // the function, // (C) Removes unneccesary operands, results, region arguments, and region // terminator operands of region branch ops, and, // (D) Removes simple and region branch ops that have all non-live results and // don't affect memory in any way, // // iff // // the IR doesn't have any non-function symbol ops, non-call symbol user ops and // branch ops. // // Here, a "simple op" refers to an op that isn't a symbol op, symbol-user op, // region branch op, branch op, region branch terminator op, or return-like. // //===----------------------------------------------------------------------===// #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h" #include "mlir/Analysis/DataFlow/LivenessAnalysis.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Dialect.h" #include "mlir/IR/IRMapping.h" #include "mlir/IR/OperationSupport.h" #include "mlir/IR/SymbolTable.h" #include "mlir/IR/Value.h" #include "mlir/IR/ValueRange.h" #include "mlir/IR/Visitors.h" #include "mlir/Interfaces/CallInterfaces.h" #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/FunctionInterfaces.h" #include "mlir/Interfaces/SideEffectInterfaces.h" #include "mlir/Pass/Pass.h" #include "mlir/Support/LLVM.h" #include "mlir/Transforms/FoldUtils.h" #include "mlir/Transforms/Passes.h" #include "llvm/ADT/STLExtras.h" #include <cassert> #include <cstddef> #include <memory> #include <optional> #include <vector> namespace mlir { #define GEN_PASS_DEF_REMOVEDEADVALUES #include "mlir/Transforms/Passes.h.inc" } // namespace mlir usingnamespacemlir; usingnamespacemlir::dataflow; //===----------------------------------------------------------------------===// // RemoveDeadValues Pass //===----------------------------------------------------------------------===// namespace { // Some helper functions... /// Return true iff at least one value in `values` is live, given the liveness /// information in `la`. static bool hasLive(ValueRange values, RunLivenessAnalysis &la) { … } /// Return a BitVector of size `values.size()` where its i-th bit is 1 iff the /// i-th value in `values` is live, given the liveness information in `la`. static BitVector markLives(ValueRange values, RunLivenessAnalysis &la) { … } /// Drop the uses of the i-th result of `op` and then erase it iff toErase[i] /// is 1. static void dropUsesAndEraseResults(Operation *op, BitVector toErase) { … } /// Convert a list of `Operand`s to a list of `OpOperand`s. static SmallVector<OpOperand *> operandsToOpOperands(OperandRange operands) { … } /// Clean a simple op `op`, given the liveness analysis information in `la`. /// Here, cleaning means: /// (1) Dropping all its uses, AND /// (2) Erasing it /// iff it has no memory effects and none of its results are live. /// /// It is assumed that `op` is simple. Here, a simple op is one which isn't a /// symbol op, a symbol-user op, a region branch op, a branch op, a region /// branch terminator op, or return-like. static void cleanSimpleOp(Operation *op, RunLivenessAnalysis &la) { … } /// Clean a function-like op `funcOp`, given the liveness information in `la` /// and the IR in `module`. Here, cleaning means: /// (1) Dropping the uses of its unnecessary (non-live) arguments, /// (2) Erasing these arguments, /// (3) Erasing their corresponding operands from its callers, /// (4) Erasing its unnecessary terminator operands (return values that are /// non-live across all callers), /// (5) Dropping the uses of these return values from its callers, AND /// (6) Erasing these return values /// iff it is not public or declaration. static void cleanFuncOp(FunctionOpInterface funcOp, Operation *module, RunLivenessAnalysis &la) { … } /// Clean a region branch op `regionBranchOp`, given the liveness information in /// `la`. Here, cleaning means: /// (1') Dropping all its uses, AND /// (2') Erasing it /// if it has no memory effects and none of its results are live, AND /// (1) Erasing its unnecessary operands (operands that are forwarded to /// unneccesary results and arguments), /// (2) Cleaning each of its regions, /// (3) Dropping the uses of its unnecessary results (results that are /// forwarded from unnecessary operands and terminator operands), AND /// (4) Erasing these results /// otherwise. /// Note that here, cleaning a region means: /// (2.a) Dropping the uses of its unnecessary arguments (arguments that are /// forwarded from unneccesary operands and terminator operands), /// (2.b) Erasing these arguments, AND /// (2.c) Erasing its unnecessary terminator operands (terminator operands /// that are forwarded to unneccesary results and arguments). /// It is important to note that values in this op flow from operands and /// terminator operands (successor operands) to arguments and results (successor /// inputs). static void cleanRegionBranchOp(RegionBranchOpInterface regionBranchOp, RunLivenessAnalysis &la) { … } struct RemoveDeadValues : public impl::RemoveDeadValuesBase<RemoveDeadValues> { … }; } // namespace void RemoveDeadValues::runOnOperation() { … } std::unique_ptr<Pass> mlir::createRemoveDeadValuesPass() { … }