mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[cfg] Add a new analysis findAllNonFailureExitBBs.
Even though there is only one "return" from a SILFunction, there may be multiple non-failure exits via no return functions. findAllNonFailureExitBBs The verifier currently enforces that all no-return function applications must be immediately followed by an unreachable. Thus to identify all such functions, we must just visit all unreachables and visit the previous instruction of the unreachable. If we don't have an apply in such case, then we must be in a failure code path. This code attempts to identify all ways of exiting a function in a non-failure code path. Thus it places into the result vector the return from the BB and all no-return function applications that it can not identify as a "failure" function. The function ignores any paths that it identifies as failure paths. Failure functions will be identified in the future via an @semantic tag but for now we just check for functions with the appropriate "fatal error" suffix as we do in the ARC optimizer. I am going to use this in the closure specializer to insert releases for "copied" closures that were originally passed in @guaranteed. Since the closure was passed in originally @guaranteed there will be no matching -1 unless we insert it ourselves. Swift SVN r25051
This commit is contained in:
49
include/swift/SILAnalysis/CFG.h
Normal file
49
include/swift/SILAnalysis/CFG.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//===--- CFG.h - Routines which analyze the CFG of a function ---*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// This source file is part of the Swift.org open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
||||||
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||||
|
//
|
||||||
|
// See http://swift.org/LICENSE.txt for license information
|
||||||
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef SWIFT_SILANALYSIS_CFG_H
|
||||||
|
#define SWIFT_SILANALYSIS_CFG_H
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
template <typename T> class TinyPtrVector;
|
||||||
|
|
||||||
|
} // end namespace llvm
|
||||||
|
|
||||||
|
namespace swift {
|
||||||
|
|
||||||
|
class SILFunction;
|
||||||
|
class SILBasicBlock;
|
||||||
|
|
||||||
|
/// Return true if we conservativly find all BB's that are non-failure exit
|
||||||
|
/// basic blocks and place them in \p BBs. If we find something we don't
|
||||||
|
/// understand, bail.
|
||||||
|
///
|
||||||
|
/// A non-failure exit BB is defined as a BB that:
|
||||||
|
///
|
||||||
|
/// 1. Has a return terminator.
|
||||||
|
/// 2. unreachable + noreturn terminator sequence.
|
||||||
|
///
|
||||||
|
/// If we just have an unreachable without a noreturn call before it, we must
|
||||||
|
/// have a failure BB.
|
||||||
|
///
|
||||||
|
/// We use a TinyPtrVector since in most cases this will only return one
|
||||||
|
/// SILBasicBlock since non-failure noreturn functions should not occur often
|
||||||
|
/// implying in most cases this will be one element.
|
||||||
|
///
|
||||||
|
/// TODO:
|
||||||
|
bool findAllNonFailureExitBBs(SILFunction *F,
|
||||||
|
llvm::TinyPtrVector<SILBasicBlock *> &BBs);
|
||||||
|
|
||||||
|
} // end namespace swift
|
||||||
|
|
||||||
|
#endif
|
||||||
96
lib/SILAnalysis/CFG.cpp
Normal file
96
lib/SILAnalysis/CFG.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
//===--- CFG.cpp ----------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the Swift.org open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
||||||
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||||
|
//
|
||||||
|
// See http://swift.org/LICENSE.txt for license information
|
||||||
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "swift/SILAnalysis/CFG.h"
|
||||||
|
#include "swift/SIL/SILFunction.h"
|
||||||
|
#include "swift/SIL/SILInstruction.h"
|
||||||
|
#include "swift/SIL/SILValue.h"
|
||||||
|
#include "llvm/ADT/TinyPtrVector.h"
|
||||||
|
|
||||||
|
using namespace swift;
|
||||||
|
|
||||||
|
static bool isSafeNonExitTerminator(TermInst *TI) {
|
||||||
|
switch (TI->getKind()) {
|
||||||
|
case ValueKind::BranchInst:
|
||||||
|
case ValueKind::CondBranchInst:
|
||||||
|
case ValueKind::SwitchValueInst:
|
||||||
|
case ValueKind::SwitchEnumInst:
|
||||||
|
case ValueKind::SwitchEnumAddrInst:
|
||||||
|
case ValueKind::DynamicMethodBranchInst:
|
||||||
|
case ValueKind::CheckedCastBranchInst:
|
||||||
|
case ValueKind::CheckedCastAddrBranchInst:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTrapNoReturnFunction(ApplyInst *AI) {
|
||||||
|
const char *fatalName =
|
||||||
|
"_TFSs18_fatalErrorMessageFTVSs12StaticStringS_S_Su_T_";
|
||||||
|
auto *FRI = dyn_cast<FunctionRefInst>(AI->getCallee());
|
||||||
|
|
||||||
|
// We use endswith here since if we specialize fatal error we will always
|
||||||
|
// prepend the specialization records to fatalName.
|
||||||
|
if (!FRI || !FRI->getReferencedFunction()->getName().endswith(fatalName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: Add support for autorelease_return. This is not implemented now to
|
||||||
|
/// cause this to always fail in functions with objc calling convention.
|
||||||
|
bool
|
||||||
|
swift::
|
||||||
|
findAllNonFailureExitBBs(SILFunction *F,
|
||||||
|
llvm::TinyPtrVector<SILBasicBlock *> &BBs) {
|
||||||
|
for (SILBasicBlock &BB : *F) {
|
||||||
|
TermInst *TI = BB.getTerminator();
|
||||||
|
|
||||||
|
// If we know that this terminator is not an exit terminator, continue.
|
||||||
|
if (isSafeNonExitTerminator(TI))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// A return inst is always a non-failure exit bb.
|
||||||
|
if (isa<ReturnInst>(TI)) {
|
||||||
|
BBs.push_back(&BB);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have an unreachable inst at this point, this is a terminator
|
||||||
|
// we don't understand. Be conservative and return false.
|
||||||
|
if (!isa<UnreachableInst>(TI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ok, at this point we know we have a terminator. If it is the only
|
||||||
|
// instruction in our BB, it is a failure BB. continue...
|
||||||
|
if (TI == &*BB.begin())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the unreachable is preceded by a no-return apply inst, then it is a
|
||||||
|
// non-failure exit BB. Add it to our list and continue.
|
||||||
|
auto PrevIter = std::prev(SILBasicBlock::iterator(TI));
|
||||||
|
if (auto *AI = dyn_cast<ApplyInst>(&*PrevIter)) {
|
||||||
|
if (AI->getSubstCalleeType()->isNoReturn() &&
|
||||||
|
!isTrapNoReturnFunction(AI)) {
|
||||||
|
BBs.push_back(&BB);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, it must be a failure BB where we leak, continue.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We understood all terminators, return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -13,4 +13,5 @@ add_swift_library(swiftSILAnalysis
|
|||||||
SimplifyInstruction.cpp
|
SimplifyInstruction.cpp
|
||||||
RCIdentityAnalysis.cpp
|
RCIdentityAnalysis.cpp
|
||||||
DestructorAnalysis.cpp
|
DestructorAnalysis.cpp
|
||||||
|
CFG.cpp
|
||||||
LINK_LIBRARIES swiftSILPassesUtils)
|
LINK_LIBRARIES swiftSILPassesUtils)
|
||||||
|
|||||||
Reference in New Issue
Block a user