//===--- SILGenCleanup.cpp ------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// /// Perform peephole-style "cleanup" to aid SIL diagnostic passes. /// //===----------------------------------------------------------------------===// #define DEBUG_TYPE "silgen-cleanup" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h" #include "swift/SILOptimizer/Utils/Local.h" using namespace swift; // Define a CanonicalizeInstruction subclass for use in SILGenCleanup. struct SILGenCanonicalize final : CanonicalizeInstruction { bool changed = false; llvm::SmallPtrSet deadOperands; SILGenCanonicalize() : CanonicalizeInstruction(DEBUG_TYPE) {} void notifyNewInstruction(SILInstruction *) override { changed = true; } // Just delete the given 'inst' and record its operands. The callback isn't // allowed to mutate any other instructions. void killInstruction(SILInstruction *inst) override { deadOperands.erase(inst); for (auto &operand : inst->getAllOperands()) { if (auto *operInst = operand.get()->getDefiningInstruction()) deadOperands.insert(operInst); } inst->eraseFromParent(); changed = true; } void notifyHasNewUsers(SILValue) override { changed = true; } SILBasicBlock::iterator deleteDeadOperands(SILBasicBlock::iterator nextII) { // Delete trivially dead instructions in non-determistic order. while (!deadOperands.empty()) { SILInstruction *deadOperInst = *deadOperands.begin(); // Make sure at least the first instruction is removed from the set. deadOperands.erase(deadOperInst); recursivelyDeleteTriviallyDeadInstructions( deadOperInst, false, [&](SILInstruction *deadInst) { LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); if (nextII == deadInst->getIterator()) ++nextII; deadOperands.erase(deadInst); }); } return nextII; } }; //===----------------------------------------------------------------------===// // SILGenCleanup: Top-Level Module Transform //===----------------------------------------------------------------------===// namespace { // SILGenCleanup must run on all functions that will be seen by any analysis // used by diagnostics before transforming the function that requires the // analysis. e.g. Closures need to be cleaned up before the closure's parent can // be diagnosed. // // TODO: This pass can be converted to a function transform if the mandatory // pipeline runs in bottom-up closure order. struct SILGenCleanup : SILModuleTransform { void run() override; }; void SILGenCleanup::run() { auto &module = *getModule(); for (auto &function : module) { LLVM_DEBUG(llvm::dbgs() << "\nRunning SILGenCleanup on " << function.getName() << "\n"); SILGenCanonicalize sgCanonicalize; // Iterate over all blocks even if they aren't reachable. No phi-less // dataflow cycles should have been created yet, and these transformations // are simple enough they shouldn't be affected by cycles. for (auto &bb : function) { for (auto ii = bb.begin(), ie = bb.end(); ii != ie;) { ii = sgCanonicalize.canonicalize(&*ii); ii = sgCanonicalize.deleteDeadOperands(ii); } } if (sgCanonicalize.changed) { auto invalidKind = SILAnalysis::InvalidationKind::Instructions; invalidateAnalysis(&function, invalidKind); } } } } // end anonymous namespace SILTransform *swift::createSILGenCleanup() { return new SILGenCleanup(); }