|  | // Copyright 2020 Google LLC | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      https://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "integrations/tensorflow/compiler/Passes.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "mlir/IR/SymbolTable.h" | 
|  | #include "mlir/Pass/Pass.h" | 
|  | #include "mlir/Pass/PassRegistry.h" | 
|  | #include "mlir/Support/LLVM.h" | 
|  | #include "mlir/Support/LogicalResult.h" | 
|  | #include "mlir/Transforms/Utils.h" | 
|  |  | 
|  | namespace mlir { | 
|  | namespace iree_compiler { | 
|  |  | 
|  | namespace {}  // namespace | 
|  |  | 
|  | // Clones FuncOp's until they have a single use only (or no users). | 
|  | // | 
|  | // The tf-shape-inference pass doesn't support functions that have more than | 
|  | // a single use. But some real code from frontends does end up creating code | 
|  | // like that. For example, the same LSTM cell function or loop body function | 
|  | // will be reused. | 
|  | // | 
|  | // This pass clones functions as needed to establish the invariant that all | 
|  | // functions have a single use. This can in principle cause exponential code | 
|  | // size bloat, and should in general be guided by a proper cost model. | 
|  | // | 
|  | // There are two factors which should be considered by a principled replacement | 
|  | // to this pass: | 
|  | // | 
|  | // 1. IREE currently relies on "sufficiently good shape inference" for | 
|  | // correctness so for now the cost of doing this seems acceptable since | 
|  | // pathological cases haven't hit us yet. | 
|  | // | 
|  | // 2. Cloning functions can help by allowing code to be specialized (much as | 
|  | // inlining does). In fact, tf-shape-inference attempts to do specialization | 
|  | // of callees which is difficult if callees have multiple uses. | 
|  | class GuaranteeAllFuncsOneUse | 
|  | : public PassWrapper<GuaranteeAllFuncsOneUse, OperationPass<ModuleOp>> { | 
|  | public: | 
|  | void runOnOperation() override { | 
|  | if (failed(run())) { | 
|  | signalPassFailure(); | 
|  | } | 
|  | } | 
|  |  | 
|  | LogicalResult run() { | 
|  | auto module = getOperation(); | 
|  |  | 
|  | // Overall strategy: | 
|  | // Fixed point iteration, iteratively applying a rule that clones | 
|  | // any FuncOp with more than one use to eliminate its uses. | 
|  |  | 
|  | SymbolTable symbolTable(module); | 
|  | bool madeChanges = false; | 
|  | // This value needs to be low enough to actually stop compilation in a | 
|  | // reasonable time, but not too low that it blocks real programs. | 
|  | // This number was chosen semi-randomly. | 
|  | const int kMaxClones = 1000; | 
|  | int numClones = 0; | 
|  | do { | 
|  | madeChanges = false; | 
|  | for (auto func : llvm::make_early_inc_range(module.getOps<FuncOp>())) { | 
|  | auto usesOptional = symbolTable.getSymbolUses(func, module); | 
|  | if (!usesOptional.hasValue()) { | 
|  | return func.emitError() << "could not walk uses of func"; | 
|  | } | 
|  | auto &uses = *usesOptional; | 
|  | if (llvm::size(uses) <= 1) { | 
|  | continue; | 
|  | } | 
|  | // At this point, we know we are going to change the module. | 
|  | madeChanges = true; | 
|  | for (const SymbolTable::SymbolUse &use : llvm::drop_begin(uses, 1)) { | 
|  | auto newFunc = func.clone(); | 
|  | if (numClones++ > kMaxClones) { | 
|  | return func.emitError() | 
|  | << "reached cloning limit (likely recursive call graph or " | 
|  | "repeated diamond-like call structure " | 
|  | "or just very large program)"; | 
|  | } | 
|  | symbolTable.insert(newFunc); | 
|  | newFunc.setVisibility(SymbolTable::Visibility::Private); | 
|  | if (failed(symbolTable.replaceAllSymbolUses(func, newFunc.getName(), | 
|  | use.getUser()))) { | 
|  | return func.emitError() << "could not replace symbol use"; | 
|  | } | 
|  | } | 
|  | } | 
|  | } while (madeChanges); | 
|  |  | 
|  | return success(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<OperationPass<ModuleOp>> createGuaranteeAllFuncsOneUse() { | 
|  | return std::make_unique<GuaranteeAllFuncsOneUse>(); | 
|  | } | 
|  |  | 
|  | static PassRegistration<GuaranteeAllFuncsOneUse> pass( | 
|  | "iree-guarantee-all-funcs-one-use", | 
|  | "Guarantee all func's have only a single use."); | 
|  |  | 
|  | }  // namespace iree_compiler | 
|  | }  // namespace mlir |