| // 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 |