//===--- GuaranteedARCOpts.cpp --------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-guaranteed-arc-opts" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SIL/SILVisitor.h" #include "llvm/ADT/Statistic.h" using namespace swift; STATISTIC(NumInstsEliminated, "Number of instructions eliminated"); namespace { struct GuaranteedARCOptsVisitor : SILInstructionVisitor { bool visitValueBase(ValueBase *V) { return false; } bool visitDestroyAddrInst(DestroyAddrInst *DAI); bool visitStrongReleaseInst(StrongReleaseInst *SRI); bool visitDestroyValueInst(DestroyValueInst *DVI); bool visitReleaseValueInst(ReleaseValueInst *RVI); }; } // end anonymous namespace static SILBasicBlock::reverse_iterator getPrevReverseIterator(SILInstruction *I) { return std::next(I->getIterator().getReverse()); } bool GuaranteedARCOptsVisitor::visitDestroyAddrInst(DestroyAddrInst *DAI) { SILValue Operand = DAI->getOperand(); for (auto II = getPrevReverseIterator(DAI), IE = DAI->getParent()->rend(); II != IE;) { auto *Inst = &*II; ++II; if (auto *CA = dyn_cast(Inst)) { if (CA->getSrc() == Operand && !CA->isTakeOfSrc()) { CA->setIsTakeOfSrc(IsTake); DAI->eraseFromParent(); NumInstsEliminated += 2; return true; } } // destroy_addrs commonly exist in a block of dealloc_stack's, which don't // affect take-ability. if (isa(Inst)) continue; // This code doesn't try to prove tricky validity constraints about whether // it is safe to push the destroy_addr past interesting instructions. if (Inst->mayHaveSideEffects()) break; } // If we didn't find a copy_addr to fold this into, emit the destroy_addr. return false; } static bool couldReduceStrongRefcount(SILInstruction *Inst) { // Simple memory accesses cannot reduce refcounts. if (isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst) || isa(Inst)) return false; // Assign and copyaddr of trivial types cannot drop refcounts, and 'inits' // never can either. Nontrivial ones can though, because the overwritten // value drops a retain. We would have to do more alias analysis to be able // to safely ignore one of those. if (auto *AI = dyn_cast(Inst)) { SILType StoredType = AI->getOperand(0)->getType(); if (StoredType.isTrivial(Inst->getModule()) || StoredType.is()) return false; } if (auto *CAI = dyn_cast(Inst)) { // Initializations can only increase refcounts. if (CAI->isInitializationOfDest()) return false; SILType StoredType = CAI->getOperand(0)->getType().getObjectType(); if (StoredType.isTrivial(Inst->getModule()) || StoredType.is()) return false; } // This code doesn't try to prove tricky validity constraints about whether // it is safe to push the release past interesting instructions. return Inst->mayHaveSideEffects(); } bool GuaranteedARCOptsVisitor::visitStrongReleaseInst(StrongReleaseInst *SRI) { SILValue Operand = SRI->getOperand(); // Release on a functionref is a noop. if (isa(Operand)) { SRI->eraseFromParent(); ++NumInstsEliminated; return true; } // Check to see if the instruction immediately before the insertion point is a // strong_retain of the specified operand. If so, we can zap the pair. for (auto II = getPrevReverseIterator(SRI), IE = SRI->getParent()->rend(); II != IE;) { auto *Inst = &*II; ++II; if (auto *SRA = dyn_cast(Inst)) { if (SRA->getOperand() == Operand) { SRA->eraseFromParent(); SRI->eraseFromParent(); NumInstsEliminated += 2; return true; } // Skip past unrelated retains. continue; } // Scan past simple instructions that cannot reduce strong refcounts. if (couldReduceStrongRefcount(Inst)) break; } // If we didn't find a retain to fold this into, return false. return false; } bool GuaranteedARCOptsVisitor::visitDestroyValueInst(DestroyValueInst *DVI) { SILValue Operand = DVI->getOperand(); for (auto II = getPrevReverseIterator(DVI), IE = DVI->getParent()->rend(); II != IE;) { auto *Inst = &*II; ++II; if (auto *CVI = dyn_cast(Inst)) { if (SILValue(CVI) == Operand || CVI->getOperand() == Operand) { CVI->replaceAllUsesWith(CVI->getOperand()); CVI->eraseFromParent(); DVI->eraseFromParent(); NumInstsEliminated += 2; return true; } // Skip past unrelated retains. continue; } // Scan past simple instructions that cannot reduce refcounts. if (couldReduceStrongRefcount(Inst)) break; } return false; } bool GuaranteedARCOptsVisitor::visitReleaseValueInst(ReleaseValueInst *RVI) { SILValue Operand = RVI->getOperand(); for (auto II = getPrevReverseIterator(RVI), IE = RVI->getParent()->rend(); II != IE;) { auto *Inst = &*II; ++II; if (auto *SRA = dyn_cast(Inst)) { if (SRA->getOperand() == Operand) { SRA->eraseFromParent(); RVI->eraseFromParent(); NumInstsEliminated += 2; return true; } // Skip past unrelated retains. continue; } // Scan past simple instructions that cannot reduce refcounts. if (couldReduceStrongRefcount(Inst)) break; } // If we didn't find a retain to fold this into, emit the release. return false; } //===----------------------------------------------------------------------===// // Top Level Entrypoint //===----------------------------------------------------------------------===// namespace { struct GuaranteedARCOpts : SILFunctionTransform { void run() override { GuaranteedARCOptsVisitor Visitor; bool MadeChange = false; SILFunction *F = getFunction(); for (auto &BB : *F) { for (auto II = BB.begin(), IE = BB.end(); II != IE;) { SILInstruction *I = &*II; ++II; MadeChange |= Visitor.visit(I); } } if (MadeChange) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } } }; } // end anonymous namespace SILTransform *swift::createGuaranteedARCOpts() { return new GuaranteedARCOpts(); }