1b276dec5SRiver Riddle //===- SymbolDCE.cpp - Pass to delete dead symbols ------------------------===//
2b276dec5SRiver Riddle //
3b276dec5SRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b276dec5SRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
5b276dec5SRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b276dec5SRiver Riddle //
7b276dec5SRiver Riddle //===----------------------------------------------------------------------===//
8b276dec5SRiver Riddle //
9b276dec5SRiver Riddle // This file implements an algorithm for eliminating symbol operations that are
10b276dec5SRiver Riddle // known to be dead.
11b276dec5SRiver Riddle //
12b276dec5SRiver Riddle //===----------------------------------------------------------------------===//
13b276dec5SRiver Riddle
141834ad4aSRiver Riddle #include "PassDetail.h"
15*36d3efeaSRiver Riddle #include "mlir/IR/SymbolTable.h"
16b276dec5SRiver Riddle #include "mlir/Transforms/Passes.h"
17b276dec5SRiver Riddle
18b276dec5SRiver Riddle using namespace mlir;
19b276dec5SRiver Riddle
20b276dec5SRiver Riddle namespace {
211834ad4aSRiver Riddle struct SymbolDCE : public SymbolDCEBase<SymbolDCE> {
22b276dec5SRiver Riddle void runOnOperation() override;
23b276dec5SRiver Riddle
24b276dec5SRiver Riddle /// Compute the liveness of the symbols within the given symbol table.
25b276dec5SRiver Riddle /// `symbolTableIsHidden` is true if this symbol table is known to be
26b276dec5SRiver Riddle /// unaccessible from operations in its parent regions.
27b276dec5SRiver Riddle LogicalResult computeLiveness(Operation *symbolTableOp,
287bc7d0acSRiver Riddle SymbolTableCollection &symbolTable,
29b276dec5SRiver Riddle bool symbolTableIsHidden,
30b276dec5SRiver Riddle DenseSet<Operation *> &liveSymbols);
31b276dec5SRiver Riddle };
32be0a7e9fSMehdi Amini } // namespace
33b276dec5SRiver Riddle
runOnOperation()34b276dec5SRiver Riddle void SymbolDCE::runOnOperation() {
35b276dec5SRiver Riddle Operation *symbolTableOp = getOperation();
36b276dec5SRiver Riddle
37b276dec5SRiver Riddle // SymbolDCE should only be run on operations that define a symbol table.
38b276dec5SRiver Riddle if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>()) {
39b276dec5SRiver Riddle symbolTableOp->emitOpError()
40b276dec5SRiver Riddle << " was scheduled to run under SymbolDCE, but does not define a "
41b276dec5SRiver Riddle "symbol table";
42b276dec5SRiver Riddle return signalPassFailure();
43b276dec5SRiver Riddle }
44b276dec5SRiver Riddle
45b276dec5SRiver Riddle // A flag that signals if the top level symbol table is hidden, i.e. not
46b276dec5SRiver Riddle // accessible from parent scopes.
47b276dec5SRiver Riddle bool symbolTableIsHidden = true;
487c221a7dSRiver Riddle SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(symbolTableOp);
497c221a7dSRiver Riddle if (symbolTableOp->getParentOp() && symbol)
507c221a7dSRiver Riddle symbolTableIsHidden = symbol.isPrivate();
51b276dec5SRiver Riddle
52b276dec5SRiver Riddle // Compute the set of live symbols within the symbol table.
53b276dec5SRiver Riddle DenseSet<Operation *> liveSymbols;
547bc7d0acSRiver Riddle SymbolTableCollection symbolTable;
557bc7d0acSRiver Riddle if (failed(computeLiveness(symbolTableOp, symbolTable, symbolTableIsHidden,
567bc7d0acSRiver Riddle liveSymbols)))
57b276dec5SRiver Riddle return signalPassFailure();
58b276dec5SRiver Riddle
59b276dec5SRiver Riddle // After computing the liveness, delete all of the symbols that were found to
60b276dec5SRiver Riddle // be dead.
61b276dec5SRiver Riddle symbolTableOp->walk([&](Operation *nestedSymbolTable) {
62b276dec5SRiver Riddle if (!nestedSymbolTable->hasTrait<OpTrait::SymbolTable>())
63b276dec5SRiver Riddle return;
64b276dec5SRiver Riddle for (auto &block : nestedSymbolTable->getRegion(0)) {
65973ddb7dSMehdi Amini for (Operation &op : llvm::make_early_inc_range(block)) {
661ebf1afbSNandor Licker if (isa<SymbolOpInterface>(&op) && !liveSymbols.count(&op)) {
67b276dec5SRiver Riddle op.erase();
681ebf1afbSNandor Licker ++numDCE;
691ebf1afbSNandor Licker }
70b276dec5SRiver Riddle }
71b276dec5SRiver Riddle }
72b276dec5SRiver Riddle });
73b276dec5SRiver Riddle }
74b276dec5SRiver Riddle
75b276dec5SRiver Riddle /// Compute the liveness of the symbols within the given symbol table.
76b276dec5SRiver Riddle /// `symbolTableIsHidden` is true if this symbol table is known to be
77b276dec5SRiver Riddle /// unaccessible from operations in its parent regions.
computeLiveness(Operation * symbolTableOp,SymbolTableCollection & symbolTable,bool symbolTableIsHidden,DenseSet<Operation * > & liveSymbols)78b276dec5SRiver Riddle LogicalResult SymbolDCE::computeLiveness(Operation *symbolTableOp,
797bc7d0acSRiver Riddle SymbolTableCollection &symbolTable,
80b276dec5SRiver Riddle bool symbolTableIsHidden,
81b276dec5SRiver Riddle DenseSet<Operation *> &liveSymbols) {
82b276dec5SRiver Riddle // A worklist of live operations to propagate uses from.
83b276dec5SRiver Riddle SmallVector<Operation *, 16> worklist;
84b276dec5SRiver Riddle
85b276dec5SRiver Riddle // Walk the symbols within the current symbol table, marking the symbols that
86b276dec5SRiver Riddle // are known to be live.
87b276dec5SRiver Riddle for (auto &block : symbolTableOp->getRegion(0)) {
887c221a7dSRiver Riddle // Add all non-symbols or symbols that can't be discarded.
89973ddb7dSMehdi Amini for (Operation &op : block) {
907c221a7dSRiver Riddle SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(&op);
917c221a7dSRiver Riddle if (!symbol) {
92b276dec5SRiver Riddle worklist.push_back(&op);
93b276dec5SRiver Riddle continue;
94b276dec5SRiver Riddle }
957c221a7dSRiver Riddle bool isDiscardable = (symbolTableIsHidden || symbol.isPrivate()) &&
967c221a7dSRiver Riddle symbol.canDiscardOnUseEmpty();
977c221a7dSRiver Riddle if (!isDiscardable && liveSymbols.insert(&op).second)
98b276dec5SRiver Riddle worklist.push_back(&op);
99b276dec5SRiver Riddle }
100b276dec5SRiver Riddle }
101b276dec5SRiver Riddle
102b276dec5SRiver Riddle // Process the set of symbols that were known to be live, adding new symbols
103b276dec5SRiver Riddle // that are referenced within.
104b276dec5SRiver Riddle while (!worklist.empty()) {
105b276dec5SRiver Riddle Operation *op = worklist.pop_back_val();
106b276dec5SRiver Riddle
107b276dec5SRiver Riddle // If this is a symbol table, recursively compute its liveness.
108b276dec5SRiver Riddle if (op->hasTrait<OpTrait::SymbolTable>()) {
109b276dec5SRiver Riddle // The internal symbol table is hidden if the parent is, if its not a
110b276dec5SRiver Riddle // symbol, or if it is a private symbol.
1117c221a7dSRiver Riddle SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op);
1127c221a7dSRiver Riddle bool symIsHidden = symbolTableIsHidden || !symbol || symbol.isPrivate();
1137bc7d0acSRiver Riddle if (failed(computeLiveness(op, symbolTable, symIsHidden, liveSymbols)))
114b276dec5SRiver Riddle return failure();
115b276dec5SRiver Riddle }
116b276dec5SRiver Riddle
117b276dec5SRiver Riddle // Collect the uses held by this operation.
118b276dec5SRiver Riddle Optional<SymbolTable::UseRange> uses = SymbolTable::getSymbolUses(op);
119b276dec5SRiver Riddle if (!uses) {
120b276dec5SRiver Riddle return op->emitError()
121b276dec5SRiver Riddle << "operation contains potentially unknown symbol table, "
122b276dec5SRiver Riddle "meaning that we can't reliable compute symbol uses";
123b276dec5SRiver Riddle }
124b276dec5SRiver Riddle
125b276dec5SRiver Riddle SmallVector<Operation *, 4> resolvedSymbols;
126b276dec5SRiver Riddle for (const SymbolTable::SymbolUse &use : *uses) {
127b276dec5SRiver Riddle // Lookup the symbols referenced by this use.
128b276dec5SRiver Riddle resolvedSymbols.clear();
1297bc7d0acSRiver Riddle if (failed(symbolTable.lookupSymbolIn(
1304ca5e95cSMogball op->getParentOp(), use.getSymbolRef(), resolvedSymbols)))
1314ca5e95cSMogball // Ignore references to unknown symbols.
1324ca5e95cSMogball continue;
133b276dec5SRiver Riddle
134b276dec5SRiver Riddle // Mark each of the resolved symbols as live.
135b276dec5SRiver Riddle for (Operation *resolvedSymbol : resolvedSymbols)
136b276dec5SRiver Riddle if (liveSymbols.insert(resolvedSymbol).second)
137b276dec5SRiver Riddle worklist.push_back(resolvedSymbol);
138b276dec5SRiver Riddle }
139b276dec5SRiver Riddle }
140b276dec5SRiver Riddle
141b276dec5SRiver Riddle return success();
142b276dec5SRiver Riddle }
143b276dec5SRiver Riddle
createSymbolDCEPass()144b276dec5SRiver Riddle std::unique_ptr<Pass> mlir::createSymbolDCEPass() {
145b276dec5SRiver Riddle return std::make_unique<SymbolDCE>();
146b276dec5SRiver Riddle }
147