mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
introduce a common superclass, SILNode. This is in preparation for allowing instructions to have multiple results. It is also a somewhat more elegant representation for instructions that have zero results. Instructions that are known to have exactly one result inherit from a class, SingleValueInstruction, that subclasses both ValueBase and SILInstruction. Some care must be taken when working with SILNode pointers and testing for equality; please see the comment on SILNode for more information. A number of SIL passes needed to be updated in order to handle this new distinction between SIL values and SIL instructions. Note that the SIL parser is now stricter about not trying to assign a result value from an instruction (like 'return' or 'strong_retain') that does not produce any.
168 lines
5.7 KiB
C++
168 lines
5.7 KiB
C++
//===--- StackPromotion.cpp - Promotes allocations to the stack -----------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "swift/SILOptimizer/Analysis/EscapeAnalysis.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "swift/SILOptimizer/Utils/StackNesting.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SIL/CFG.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
|
|
#define DEBUG_TYPE "stack-promotion"
|
|
|
|
STATISTIC(NumStackPromoted, "Number of objects promoted to the stack");
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
|
|
/// Promotes heap allocated objects to the stack.
|
|
///
|
|
/// It handles alloc_ref instructions of native swift classes: if promoted,
|
|
/// the [stack] attribute is set in the alloc_ref and a dealloc_ref [stack] is
|
|
/// inserted at the end of the object's lifetime.
|
|
class StackPromotion : public SILFunctionTransform {
|
|
|
|
public:
|
|
StackPromotion() {}
|
|
|
|
private:
|
|
/// The entry point to the transformation.
|
|
void run() override;
|
|
|
|
/// Promotes allocations in \p BB.
|
|
bool promoteInBlock(SILBasicBlock *BB, EscapeAnalysis *EA,
|
|
DeadEndBlocks &DEBlocks);
|
|
|
|
/// Tries to promote the allocation \p ARI.
|
|
bool tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA,
|
|
DeadEndBlocks &DEBlocks);
|
|
};
|
|
|
|
void StackPromotion::run() {
|
|
SILFunction *F = getFunction();
|
|
DEBUG(llvm::dbgs() << "** StackPromotion in " << F->getName() << " **\n");
|
|
|
|
auto *EA = PM->getAnalysis<EscapeAnalysis>();
|
|
DeadEndBlocks DEBlocks(F);
|
|
bool Changed = false;
|
|
|
|
// Search the whole function for stack promotable allocations.
|
|
for (SILBasicBlock &BB : *F) {
|
|
Changed |= promoteInBlock(&BB, EA, DEBlocks);
|
|
}
|
|
if (!Changed)
|
|
return;
|
|
|
|
// Make sure that all stack allocating instructions are nested correctly.
|
|
StackNesting SN;
|
|
if (SN.correctStackNesting(F) == StackNesting::Changes::CFG) {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::BranchesAndInstructions);
|
|
} else {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
|
|
}
|
|
}
|
|
|
|
bool StackPromotion::promoteInBlock(SILBasicBlock *BB, EscapeAnalysis *EA,
|
|
DeadEndBlocks &DEBlocks) {
|
|
bool Changed = false;
|
|
for (auto Iter = BB->begin(); Iter != BB->end();) {
|
|
// The allocation instruction may be moved, so increment Iter prior to
|
|
// doing the optimization.
|
|
SILInstruction *I = &*Iter++;
|
|
if (auto *ARI = dyn_cast<AllocRefInst>(I)) {
|
|
// Don't stack promote any allocation inside a code region which ends up
|
|
// in a no-return block. Such allocations may missing their final release.
|
|
// We would insert the deallocation too early, which may result in a
|
|
// use-after-free problem.
|
|
if (DEBlocks.isDeadEnd(BB))
|
|
return false;
|
|
|
|
Changed |= tryPromoteAlloc(ARI, EA, DEBlocks);
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA,
|
|
DeadEndBlocks &DEBlocks) {
|
|
if (ARI->isObjC() || ARI->canAllocOnStack())
|
|
return false;
|
|
|
|
auto *ConGraph = EA->getConnectionGraph(ARI->getFunction());
|
|
auto *Node = ConGraph->getNodeOrNull(ARI, EA);
|
|
if (!Node)
|
|
return false;
|
|
|
|
// The most important check: does the object escape the current function?
|
|
if (Node->escapes())
|
|
return false;
|
|
|
|
DEBUG(llvm::dbgs() << "Promote " << *ARI);
|
|
|
|
// Collect all use-points of the allocation. These are refcount instructions
|
|
// and apply instructions.
|
|
llvm::SmallVector<SILNode *, 8> BaseUsePoints;
|
|
llvm::SmallVector<SILInstruction *, 8> UsePoints;
|
|
ConGraph->getUsePoints(Node, BaseUsePoints);
|
|
for (SILNode *UsePoint : BaseUsePoints) {
|
|
if (SILInstruction *I = dyn_cast<SILInstruction>(UsePoint)) {
|
|
UsePoints.push_back(I);
|
|
} else {
|
|
// Also block arguments can be use points.
|
|
SILBasicBlock *UseBB = cast<SILPHIArgument>(UsePoint)->getParent();
|
|
// For simplicity we just add the first instruction of the block as use
|
|
// point.
|
|
UsePoints.push_back(&UseBB->front());
|
|
}
|
|
}
|
|
|
|
ValueLifetimeAnalysis VLA(ARI, UsePoints);
|
|
// Check if there is a use point before the allocation (this can happen e.g.
|
|
// if the allocated object is stored into another object, which is already
|
|
// alive at the allocation point).
|
|
// In such a case the value lifetime extends up to the function entry.
|
|
if (VLA.isAliveAtBeginOfBlock(ARI->getFunction()->getEntryBlock())) {
|
|
DEBUG(llvm::dbgs() << " use before allocation -> don't promote");
|
|
return false;
|
|
}
|
|
|
|
// Compute the places where the lifetime of the object ends.
|
|
ValueLifetimeAnalysis::Frontier Frontier;
|
|
if (!VLA.computeFrontier(Frontier, ValueLifetimeAnalysis::UsersMustPostDomDef,
|
|
&DEBlocks)) {
|
|
DEBUG(llvm::dbgs() << " uses don't post-dom allocation -> don't promote");
|
|
return false;
|
|
}
|
|
NumStackPromoted++;
|
|
|
|
// We set the [stack] attribute in the alloc_ref.
|
|
ARI->setStackAllocatable();
|
|
|
|
/// And create dealloc_ref [stack] at the end of the object's lifetime.
|
|
for (SILInstruction *FrontierInst : Frontier) {
|
|
SILBuilder B(FrontierInst);
|
|
B.createDeallocRef(ARI->getLoc(), ARI, true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createStackPromotion() {
|
|
return new StackPromotion();
|
|
}
|