mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Also remove the overload of createApply() that does not take a SubstitutionMap. It accomplishes nothing except creating ambiguity.
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
//===--- ReleaseDevirtualizer.cpp - Devirtualizes release-instructions ----===//
|
|
//
|
|
// 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 "release-devirtualizer"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/AST/GenericSignature.h"
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
|
|
STATISTIC(NumReleasesDevirtualized, "Number of devirtualized releases");
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
|
|
/// Devirtualizes release instructions which are known to destruct the object.
|
|
///
|
|
/// This means, it replaces a sequence of
|
|
/// %x = alloc_ref [stack] $X
|
|
/// ...
|
|
/// strong_release %x
|
|
/// dealloc_ref [stack] %x
|
|
/// with
|
|
/// %x = alloc_ref [stack] $X
|
|
/// ...
|
|
/// set_deallocating %x
|
|
/// %d = function_ref @dealloc_of_X
|
|
/// %a = apply %d(%x)
|
|
/// dealloc_ref [stack] %x
|
|
///
|
|
/// The optimization is only done for stack promoted objects because they are
|
|
/// known to have no associated objects (which are not explicitly released
|
|
/// in the deinit method).
|
|
class ReleaseDevirtualizer : public SILFunctionTransform {
|
|
|
|
public:
|
|
ReleaseDevirtualizer() {}
|
|
|
|
private:
|
|
/// The entry point to the transformation.
|
|
void run() override;
|
|
|
|
/// Devirtualize releases of array buffers.
|
|
bool devirtualizeReleaseOfObject(SILInstruction *ReleaseInst,
|
|
DeallocRefInst *DeallocInst);
|
|
|
|
/// Replace the release-instruction \p ReleaseInst with an explicit call to
|
|
/// the deallocating destructor of \p AllocType for \p object.
|
|
bool createDeallocCall(SILType AllocType, SILInstruction *ReleaseInst,
|
|
SILValue object);
|
|
|
|
RCIdentityFunctionInfo *RCIA = nullptr;
|
|
};
|
|
|
|
void ReleaseDevirtualizer::run() {
|
|
LLVM_DEBUG(llvm::dbgs() << "** ReleaseDevirtualizer **\n");
|
|
|
|
SILFunction *F = getFunction();
|
|
RCIA = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
|
|
|
|
bool Changed = false;
|
|
for (SILBasicBlock &BB : *F) {
|
|
|
|
// The last release_value or strong_release instruction before the
|
|
// deallocation.
|
|
SILInstruction *LastRelease = nullptr;
|
|
|
|
for (SILInstruction &I : BB) {
|
|
if (LastRelease) {
|
|
if (auto *DRI = dyn_cast<DeallocRefInst>(&I)) {
|
|
Changed |= devirtualizeReleaseOfObject(LastRelease, DRI);
|
|
LastRelease = nullptr;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (isa<ReleaseValueInst>(&I) ||
|
|
isa<StrongReleaseInst>(&I)) {
|
|
LastRelease = &I;
|
|
} else if (I.mayReleaseOrReadRefCount()) {
|
|
LastRelease = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (Changed) {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
|
|
}
|
|
}
|
|
|
|
bool ReleaseDevirtualizer::
|
|
devirtualizeReleaseOfObject(SILInstruction *ReleaseInst,
|
|
DeallocRefInst *DeallocInst) {
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " try to devirtualize " << *ReleaseInst);
|
|
|
|
// We only do the optimization for stack promoted object, because for these
|
|
// we know that they don't have associated objects, which are _not_ released
|
|
// by the deinit method.
|
|
// This restriction is no problem because only stack promotion result in this
|
|
// alloc-release-dealloc pattern.
|
|
if (!DeallocInst->canAllocOnStack())
|
|
return false;
|
|
|
|
// Is the dealloc_ref paired with an alloc_ref?
|
|
auto *ARI = dyn_cast<AllocRefInst>(DeallocInst->getOperand());
|
|
if (!ARI)
|
|
return false;
|
|
|
|
// Does the last release really release the allocated object?
|
|
SILValue rcRoot = RCIA->getRCIdentityRoot(ReleaseInst->getOperand(0));
|
|
if (rcRoot != ARI)
|
|
return false;
|
|
|
|
SILType AllocType = ARI->getType();
|
|
return createDeallocCall(AllocType, ReleaseInst, ARI);
|
|
}
|
|
|
|
bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType,
|
|
SILInstruction *ReleaseInst,
|
|
SILValue object) {
|
|
LLVM_DEBUG(llvm::dbgs() << " create dealloc call\n");
|
|
|
|
ClassDecl *Cl = AllocType.getClassOrBoundGenericClass();
|
|
assert(Cl && "no class type allocated with alloc_ref");
|
|
|
|
// Find the destructor of the type.
|
|
DestructorDecl *Destructor = Cl->getDestructor();
|
|
SILDeclRef DeallocRef(Destructor, SILDeclRef::Kind::Deallocator);
|
|
SILModule &M = ReleaseInst->getFunction()->getModule();
|
|
SILFunction *Dealloc = M.lookUpFunction(DeallocRef);
|
|
if (!Dealloc)
|
|
return false;
|
|
|
|
CanSILFunctionType DeallocType = Dealloc->getLoweredFunctionType();
|
|
auto *NTD = AllocType.getASTType()->getAnyNominal();
|
|
auto AllocSubMap = AllocType.getASTType()
|
|
->getContextSubstitutionMap(M.getSwiftModule(), NTD);
|
|
|
|
DeallocType = DeallocType->substGenericArgs(M, AllocSubMap);
|
|
|
|
SILBuilder B(ReleaseInst);
|
|
if (object->getType() != AllocType)
|
|
object = B.createUncheckedRefCast(ReleaseInst->getLoc(), object, AllocType);
|
|
|
|
// Do what a release would do before calling the deallocator: set the object
|
|
// in deallocating state, which means set the RC_DEALLOCATING_FLAG flag.
|
|
B.createSetDeallocating(ReleaseInst->getLoc(), object,
|
|
cast<RefCountingInst>(ReleaseInst)->getAtomicity());
|
|
|
|
// Create the call to the destructor with the allocated object as self
|
|
// argument.
|
|
auto *MI = B.createFunctionRef(ReleaseInst->getLoc(), Dealloc);
|
|
|
|
B.createApply(ReleaseInst->getLoc(), MI, AllocSubMap, {object});
|
|
|
|
NumReleasesDevirtualized++;
|
|
ReleaseInst->eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createReleaseDevirtualizer() {
|
|
return new ReleaseDevirtualizer();
|
|
}
|