//===--- SemanticARCOptVisitor.cpp ----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 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 // //===----------------------------------------------------------------------===// /// /// \file /// /// Implementation of the main optimize loop of the ARC visitor peephole /// optimizer. /// //===----------------------------------------------------------------------===// #include "SemanticARCOptVisitor.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/SIL/DebugUtils.h" using namespace swift; using namespace swift::semanticarc; bool SemanticARCOptVisitor::optimizeWithoutFixedPoint() { bool madeChange = false; // First process the worklist until we reach a fixed point. madeChange |= processWorklist(); return madeChange; } bool SemanticARCOptVisitor::optimize() { bool madeChange = false; // First process the worklist until we reach a fixed point. madeChange |= processWorklist(); { // If we made a change, set that we assume we are at fixed point and then // re-run the worklist so that we can // properly seeded the ARC peephole map. ctx.assumingAtFixedPoint = true; SWIFT_DEFER { ctx.assumingAtFixedPoint = false; }; // Add everything in visitedSinceLastMutation to the worklist so we // recompute our fixed point. drainVisitedSinceLastMutationIntoWorklist(); // Then re-run the worklist. We shouldn't modify anything since we are at a // fixed point and are just using this to seed the // joinedOwnedIntroducerToConsumedOperands after we have finished changing // things. If we did change something, we did something weird, so assert! bool madeAdditionalChanges = processWorklist(); (void)madeAdditionalChanges; assert(!madeAdditionalChanges && "Should be at the fixed point"); } return madeChange; } bool SemanticARCOptVisitor::processWorklist() { // NOTE: The madeChange here is not strictly necessary since we only have // items added to the worklist today if we have already made /some/ sort of // change. That being said, I think there is a low cost to including this here // and makes the algorithm more correct, visually and in the face of potential // refactoring. bool madeChange = false; while (!worklist.empty()) { // Pop the last element off the list. If we were returned None, we blotted // this element, so skip it. SILValue next = worklist.pop_back_val().value_or(SILValue()); if (!next) continue; // First check if this is a value that we have visited since the last time // we erased an instruction. If we have visited it, skip it. Every time we // modify something, we should be deleting an instruction, so we have not // found any further information. if (!visitedSinceLastMutation.insert(next).second) { continue; } // First check if this is an instruction that is trivially dead. This can // occur if we eliminate rr traffic resulting in dead projections and the // like. // // If we delete, we first add all of our deleted instructions operands to // the worklist and then remove all results (since we are going to delete // the instruction). if (auto *defInst = next->getDefiningInstruction()) { if (isInstructionTriviallyDead(defInst)) { assert(!ctx.assumingAtFixedPoint && "Assumed was at fixed point and recomputing state?!"); deleteAllDebugUses(defInst); eraseInstruction(defInst); madeChange = true; ctx.verify(); continue; } } // Otherwise, if we have a single value instruction (to be expanded later // perhaps), try to visit that value recursively. if (auto *svi = dyn_cast(next)) { bool madeSingleChange = visit(svi); assert((!madeSingleChange || !ctx.assumingAtFixedPoint) && "Assumed was at fixed point and modified state?!"); madeChange |= madeSingleChange; if (madeSingleChange) { ctx.verify(); } continue; } } return madeChange; }