|
|
|
|
@@ -1,8 +1,8 @@
|
|
|
|
|
//===--- ArrayBoundsCheckOpts.cpp - Bounds check elim ---------------------===//
|
|
|
|
|
//===--- BoundsCheckOpts.cpp - Bounds check elimination -------------------===//
|
|
|
|
|
//
|
|
|
|
|
// This source file is part of the Swift.org open source project
|
|
|
|
|
//
|
|
|
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
|
|
|
// Copyright (c) 2014 - 2015 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
|
|
|
|
|
@@ -10,7 +10,7 @@
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "sil-abcopts"
|
|
|
|
|
#define DEBUG_TYPE "sil-bcopts"
|
|
|
|
|
|
|
|
|
|
#include "swift/AST/Builtins.h"
|
|
|
|
|
#include "swift/Basic/Assertions.h"
|
|
|
|
|
@@ -52,16 +52,12 @@
|
|
|
|
|
using namespace swift;
|
|
|
|
|
using namespace PatternMatch;
|
|
|
|
|
|
|
|
|
|
static llvm::cl::opt<bool> ShouldReportBoundsChecks("sil-abcopts-report",
|
|
|
|
|
llvm::cl::init(false));
|
|
|
|
|
|
|
|
|
|
static llvm::cl::opt<bool> EnableABCOpts("enable-abcopts",
|
|
|
|
|
llvm::cl::init(true));
|
|
|
|
|
static llvm::cl::opt<bool> ShouldReportBoundsChecks("sil-bcopts-report",
|
|
|
|
|
llvm::cl::init(false));
|
|
|
|
|
|
|
|
|
|
static llvm::cl::opt<bool> EnableABCHoisting("enable-abc-hoisting",
|
|
|
|
|
llvm::cl::init(true));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using ArraySet = llvm::SmallPtrSet<SILValue, 16>;
|
|
|
|
|
// A pair of the array pointer and the array check kind (kCheckIndex or
|
|
|
|
|
// kCheckSubscript).
|
|
|
|
|
@@ -278,9 +274,8 @@ private:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(Array ||
|
|
|
|
|
K == ArrayCallKind::kNone &&
|
|
|
|
|
"Need to have an array for array semantic functions");
|
|
|
|
|
assert(Array || K == ArrayCallKind::kNone &&
|
|
|
|
|
"Need to have an array for array semantic functions");
|
|
|
|
|
|
|
|
|
|
// We need to make sure that the array container is not aliased in ways
|
|
|
|
|
// that we don't understand.
|
|
|
|
|
@@ -311,16 +306,14 @@ getArrayIndexPair(SILValue Array, SILValue ArrayIndex, ArrayCallKind K) {
|
|
|
|
|
K == ArrayCallKind::kCheckSubscript) &&
|
|
|
|
|
"Must be a bounds check call");
|
|
|
|
|
return std::make_pair(
|
|
|
|
|
Array,
|
|
|
|
|
ArrayAccessDesc(ArrayIndex, K == ArrayCallKind::kCheckIndex));
|
|
|
|
|
Array, ArrayAccessDesc(ArrayIndex, K == ArrayCallKind::kCheckIndex));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static CondFailInst *hasCondFailUse(SILValue V) {
|
|
|
|
|
for (auto *Op : V->getUses())
|
|
|
|
|
if (auto C = dyn_cast<CondFailInst>(Op->getUser()))
|
|
|
|
|
return C;
|
|
|
|
|
return nullptr;
|
|
|
|
|
for (auto *Op : V->getUses())
|
|
|
|
|
if (auto C = dyn_cast<CondFailInst>(Op->getUser()))
|
|
|
|
|
return C;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Checks whether the apply instruction is checked for overflow by looking for
|
|
|
|
|
@@ -357,8 +350,7 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) {
|
|
|
|
|
if (match(CF->getOperand(),
|
|
|
|
|
m_ApplyInst(BuiltinValueKind::Xor,
|
|
|
|
|
m_ApplyInst(BuiltinValueKind::ICMP_SLE,
|
|
|
|
|
m_Specific(Start),
|
|
|
|
|
m_Specific(End)),
|
|
|
|
|
m_Specific(Start), m_Specific(End)),
|
|
|
|
|
m_One())))
|
|
|
|
|
return true;
|
|
|
|
|
// Try to match a cond_fail on "SLT End, Start".
|
|
|
|
|
@@ -378,8 +370,7 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) {
|
|
|
|
|
IsPreInclusiveEndLEQ = true;
|
|
|
|
|
if (match(CF->getOperand(),
|
|
|
|
|
m_ApplyInst(BuiltinValueKind::ICMP_SLT,
|
|
|
|
|
m_Specific(PreInclusiveEnd),
|
|
|
|
|
m_Specific(Start))))
|
|
|
|
|
m_Specific(PreInclusiveEnd), m_Specific(Start))))
|
|
|
|
|
IsPreInclusiveEndLEQ = true;
|
|
|
|
|
if (match(CF->getOperand(),
|
|
|
|
|
m_ApplyInst(BuiltinValueKind::Xor,
|
|
|
|
|
@@ -408,35 +399,55 @@ static bool isLessThan(SILValue Start, SILValue End) {
|
|
|
|
|
|
|
|
|
|
static BuiltinValueKind swapCmpID(BuiltinValueKind ID) {
|
|
|
|
|
switch (ID) {
|
|
|
|
|
case BuiltinValueKind::ICMP_EQ: return BuiltinValueKind::ICMP_EQ;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE: return BuiltinValueKind::ICMP_NE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLE: return BuiltinValueKind::ICMP_SGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT: return BuiltinValueKind::ICMP_SGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGE: return BuiltinValueKind::ICMP_SLE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGT: return BuiltinValueKind::ICMP_SLT;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULE: return BuiltinValueKind::ICMP_UGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT: return BuiltinValueKind::ICMP_UGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGE: return BuiltinValueKind::ICMP_ULE;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGT: return BuiltinValueKind::ICMP_ULT;
|
|
|
|
|
default:
|
|
|
|
|
return ID;
|
|
|
|
|
case BuiltinValueKind::ICMP_EQ:
|
|
|
|
|
return BuiltinValueKind::ICMP_EQ;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE:
|
|
|
|
|
return BuiltinValueKind::ICMP_NE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLE:
|
|
|
|
|
return BuiltinValueKind::ICMP_SGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT:
|
|
|
|
|
return BuiltinValueKind::ICMP_SGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGE:
|
|
|
|
|
return BuiltinValueKind::ICMP_SLE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGT:
|
|
|
|
|
return BuiltinValueKind::ICMP_SLT;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULE:
|
|
|
|
|
return BuiltinValueKind::ICMP_UGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT:
|
|
|
|
|
return BuiltinValueKind::ICMP_UGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGE:
|
|
|
|
|
return BuiltinValueKind::ICMP_ULE;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGT:
|
|
|
|
|
return BuiltinValueKind::ICMP_ULT;
|
|
|
|
|
default:
|
|
|
|
|
return ID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BuiltinValueKind invertCmpID(BuiltinValueKind ID) {
|
|
|
|
|
switch (ID) {
|
|
|
|
|
case BuiltinValueKind::ICMP_EQ: return BuiltinValueKind::ICMP_NE;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE: return BuiltinValueKind::ICMP_EQ;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLE: return BuiltinValueKind::ICMP_SGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT: return BuiltinValueKind::ICMP_SGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGE: return BuiltinValueKind::ICMP_SLT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGT: return BuiltinValueKind::ICMP_SLE;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULE: return BuiltinValueKind::ICMP_UGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT: return BuiltinValueKind::ICMP_UGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGE: return BuiltinValueKind::ICMP_ULT;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGT: return BuiltinValueKind::ICMP_ULE;
|
|
|
|
|
default:
|
|
|
|
|
return ID;
|
|
|
|
|
case BuiltinValueKind::ICMP_EQ:
|
|
|
|
|
return BuiltinValueKind::ICMP_NE;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE:
|
|
|
|
|
return BuiltinValueKind::ICMP_EQ;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLE:
|
|
|
|
|
return BuiltinValueKind::ICMP_SGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT:
|
|
|
|
|
return BuiltinValueKind::ICMP_SGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGE:
|
|
|
|
|
return BuiltinValueKind::ICMP_SLT;
|
|
|
|
|
case BuiltinValueKind::ICMP_SGT:
|
|
|
|
|
return BuiltinValueKind::ICMP_SLE;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULE:
|
|
|
|
|
return BuiltinValueKind::ICMP_UGT;
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT:
|
|
|
|
|
return BuiltinValueKind::ICMP_UGE;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGE:
|
|
|
|
|
return BuiltinValueKind::ICMP_ULT;
|
|
|
|
|
case BuiltinValueKind::ICMP_UGT:
|
|
|
|
|
return BuiltinValueKind::ICMP_ULE;
|
|
|
|
|
default:
|
|
|
|
|
return ID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -446,11 +457,11 @@ static SILValue getZeroToCountArray(SILValue Start, SILValue End) {
|
|
|
|
|
auto *IL = dyn_cast<IntegerLiteralInst>(Start);
|
|
|
|
|
if (!IL || IL->getValue() != 0)
|
|
|
|
|
return SILValue();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto *SEI = dyn_cast<StructExtractInst>(End);
|
|
|
|
|
if (!SEI)
|
|
|
|
|
return SILValue();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ArraySemanticsCall SemCall(SEI->getOperand());
|
|
|
|
|
if (SemCall.getKind() != ArrayCallKind::kGetCount)
|
|
|
|
|
return SILValue();
|
|
|
|
|
@@ -471,30 +482,30 @@ static bool isLessThanCheck(SILValue Start, SILValue End,
|
|
|
|
|
|
|
|
|
|
SILValue LeftArg = BI->getOperand(0);
|
|
|
|
|
SILValue RightArg = BI->getOperand(1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (RightArg == Start) {
|
|
|
|
|
std::swap(LeftArg, RightArg);
|
|
|
|
|
Id = swapCmpID(Id);
|
|
|
|
|
}
|
|
|
|
|
if (LeftArg != Start || RightArg != End)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CondBr->getTrueBB() != Preheader) {
|
|
|
|
|
assert(CondBr->getFalseBB() == Preheader);
|
|
|
|
|
Id = invertCmpID(Id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (Id) {
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT:
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT:
|
|
|
|
|
return true;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE:
|
|
|
|
|
// Special case: if it is a 0-to-count loop, we know that the count cannot
|
|
|
|
|
// be negative. In this case the 'Start < End' check can also be done with
|
|
|
|
|
// 'count != 0'.
|
|
|
|
|
return getZeroToCountArray(Start, End);
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
case BuiltinValueKind::ICMP_SLT:
|
|
|
|
|
case BuiltinValueKind::ICMP_ULT:
|
|
|
|
|
return true;
|
|
|
|
|
case BuiltinValueKind::ICMP_NE:
|
|
|
|
|
// Special case: if it is a 0-to-count loop, we know that the count cannot
|
|
|
|
|
// be negative. In this case the 'Start < End' check can also be done with
|
|
|
|
|
// 'count != 0'.
|
|
|
|
|
return getZeroToCountArray(Start, End);
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -577,12 +588,12 @@ struct InductionInfo {
|
|
|
|
|
|
|
|
|
|
SILInstruction *getInstruction() { return Inc; }
|
|
|
|
|
|
|
|
|
|
SILValue getFirstValue(SILLocation &Loc, SILBuilder &B, unsigned AddVal) {
|
|
|
|
|
return AddVal != 0 ? getAdd(Loc, Start, AddVal, B) : Start;
|
|
|
|
|
SILValue getFirstValue(SILLocation loc, SILBuilder &B, unsigned AddVal) {
|
|
|
|
|
return AddVal != 0 ? getAdd(loc, Start, AddVal, B) : Start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SILValue getLastValue(SILLocation &Loc, SILBuilder &B, unsigned SubVal) {
|
|
|
|
|
return SubVal != 0 ? getSub(Loc, End, SubVal, B) : End;
|
|
|
|
|
SILValue getLastValue(SILLocation loc, SILBuilder &B, unsigned SubVal) {
|
|
|
|
|
return SubVal != 0 ? getSub(loc, End, SubVal, B) : End;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// If necessary insert an overflow for this induction variable.
|
|
|
|
|
@@ -633,9 +644,6 @@ public:
|
|
|
|
|
: DT(D), Preheader(Preheader), Header(Header), ExitingBlk(ExitingBlk),
|
|
|
|
|
ExitBlk(ExitBlk), IVs(IVs) {}
|
|
|
|
|
|
|
|
|
|
InductionAnalysis(const InductionAnalysis &) = delete;
|
|
|
|
|
InductionAnalysis &operator=(const InductionAnalysis &) = delete;
|
|
|
|
|
|
|
|
|
|
bool analyze() {
|
|
|
|
|
bool FoundIndVar = false;
|
|
|
|
|
for (auto *Arg : Header->getArguments()) {
|
|
|
|
|
@@ -648,8 +656,8 @@ public:
|
|
|
|
|
|
|
|
|
|
InductionInfo *Info;
|
|
|
|
|
if (!(Info = analyzeIndVar(Arg, IV.Inc, IV.IncVal))) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " could not analyze the induction on: "
|
|
|
|
|
<< *Arg);
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
|
<< " could not analyze the induction on: " << *Arg);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -661,14 +669,13 @@ public:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InductionInfo *operator[](SILArgument *A) {
|
|
|
|
|
InductionInfoMap::iterator It = Map.find(A);
|
|
|
|
|
InductionInfoMap::iterator It = Map.find(A);
|
|
|
|
|
if (It == Map.end())
|
|
|
|
|
return nullptr;
|
|
|
|
|
return It->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
/// Analyze one potential induction variable starting at Arg.
|
|
|
|
|
InductionInfo *analyzeIndVar(SILArgument *HeaderVal, BuiltinInst *Inc,
|
|
|
|
|
IntegerLiteralInst *IncVal) {
|
|
|
|
|
@@ -710,9 +717,9 @@ private:
|
|
|
|
|
if (!dominates(DT, End, Preheader))
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " found an induction variable (ICMP_EQ): "
|
|
|
|
|
<< *HeaderVal << " start: " << *Start
|
|
|
|
|
<< " end: " << *End);
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
|
<< " found an induction variable (ICMP_EQ): " << *HeaderVal
|
|
|
|
|
<< " start: " << *Start << " end: " << *End);
|
|
|
|
|
|
|
|
|
|
// Check whether the addition is overflow checked by a cond_fail or whether
|
|
|
|
|
// code in the preheader's predecessor ensures that we won't overflow.
|
|
|
|
|
@@ -749,7 +756,6 @@ class AccessFunction {
|
|
|
|
|
: Ind(I), preIncrement(isPreIncrement) {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
operator bool() { return Ind != nullptr; }
|
|
|
|
|
|
|
|
|
|
static AccessFunction getLinearFunction(SILValue Idx,
|
|
|
|
|
@@ -771,8 +777,7 @@ public:
|
|
|
|
|
if (!ArrayIndexStruct)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
auto AsArg =
|
|
|
|
|
dyn_cast<SILArgument>(ArrayIndexStruct->getElements()[0]);
|
|
|
|
|
auto AsArg = dyn_cast<SILArgument>(ArrayIndexStruct->getElements()[0]);
|
|
|
|
|
|
|
|
|
|
if (!AsArg) {
|
|
|
|
|
auto *TupleExtract =
|
|
|
|
|
@@ -805,16 +810,15 @@ public:
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns true if the loop iterates from 0 until count of \p ArrayVal.
|
|
|
|
|
bool isZeroToCount(SILValue ArrayVal) {
|
|
|
|
|
return getZeroToCountArray(Ind->Start, Ind->End) == ArrayVal;
|
|
|
|
|
/// Returns true if the loop iterates from 0 until count of \p selfValue.
|
|
|
|
|
bool isZeroToCount(SILValue selfValue) {
|
|
|
|
|
return getZeroToCountOfSelf(Ind->Start, Ind->End) == selfValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Hoists the necessary check for beginning and end of the induction
|
|
|
|
|
/// encapsulated by this access function to the header.
|
|
|
|
|
void hoistCheckToPreheader(ArraySemanticsCall CheckToHoist,
|
|
|
|
|
SILBasicBlock *Preheader,
|
|
|
|
|
DominanceInfo *DT) {
|
|
|
|
|
SILBasicBlock *Preheader, DominanceInfo *DT) {
|
|
|
|
|
ApplyInst *AI = CheckToHoist;
|
|
|
|
|
SILLocation Loc = AI->getLoc();
|
|
|
|
|
SILBuilderWithScope Builder(Preheader->getTerminator(), AI);
|
|
|
|
|
@@ -827,7 +831,7 @@ public:
|
|
|
|
|
// Set the new start index to the first value of the induction.
|
|
|
|
|
Start->setOperand(0, FirstVal);
|
|
|
|
|
|
|
|
|
|
// Clone and fixup the load, retain sequence to the header.
|
|
|
|
|
// Clone and fixup the load, retain sequence to the header.
|
|
|
|
|
auto NewCheck = CheckToHoist.copyTo(Preheader->getTerminator(), DT);
|
|
|
|
|
NewCheck->setOperand(1, Start);
|
|
|
|
|
|
|
|
|
|
@@ -931,7 +935,7 @@ static constexpr int maxRecursionDepth = 500;
|
|
|
|
|
|
|
|
|
|
/// Remove redundant checks in basic blocks and hoist redundant checks out of
|
|
|
|
|
/// loops.
|
|
|
|
|
class ABCOpt : public SILFunctionTransform {
|
|
|
|
|
class BoundsCheckOpts : public SILFunctionTransform {
|
|
|
|
|
private:
|
|
|
|
|
SILLoopInfo *LI;
|
|
|
|
|
DominanceInfo *DT;
|
|
|
|
|
@@ -939,118 +943,129 @@ private:
|
|
|
|
|
RCIdentityFunctionInfo *RCIA;
|
|
|
|
|
DestructorAnalysis *DestAnalysis;
|
|
|
|
|
// Arrays with element type that does not call a deinit function.
|
|
|
|
|
ArraySet ReleaseSafeArrays;
|
|
|
|
|
ArraySet releaseSafeArrays;
|
|
|
|
|
|
|
|
|
|
/// Collect all release safe arrays in this function. A release is only 'safe'
|
|
|
|
|
/// if we know its deinitializer does not have sideeffects that could cause
|
|
|
|
|
/// memory safety issues. A deinit could deallocate array or put a different
|
|
|
|
|
/// array in its location.
|
|
|
|
|
void collectReleaseSafeArrays();
|
|
|
|
|
|
|
|
|
|
bool optimizeBoundsChecksInBlocks();
|
|
|
|
|
|
|
|
|
|
bool optimizeBoundsChecksInLoops();
|
|
|
|
|
|
|
|
|
|
std::pair<bool, std::optional<InductionAnalysis>>
|
|
|
|
|
findAndOptimizeInductionVariables(SILLoop *loop);
|
|
|
|
|
|
|
|
|
|
bool optimizeArrayBoundsCheckInLoop(SILLoop *loop,
|
|
|
|
|
std::optional<InductionAnalysis> indVars);
|
|
|
|
|
|
|
|
|
|
/// Remove redundant checks in a basic block. This function will reset the
|
|
|
|
|
/// state after an instruction that may modify any array allowing removal of
|
|
|
|
|
/// redundant checks up to that point and after that point.
|
|
|
|
|
bool removeRedundantChecksInBlock(SILBasicBlock &BB);
|
|
|
|
|
bool removeRedundantArrayChecksInBlock(SILBasicBlock &BB);
|
|
|
|
|
/// Hoist or remove redundant bound checks in \p Loop
|
|
|
|
|
bool processLoop(SILLoop *Loop);
|
|
|
|
|
bool optimizeArrayBoundsCheckInLoop(SILLoop *Loop);
|
|
|
|
|
/// Walk down the dominator tree inside the loop, removing redundant checks.
|
|
|
|
|
bool removeRedundantChecksInLoop(DominanceInfoNode *CurBB, ABCAnalysis &ABC,
|
|
|
|
|
IndexedArraySet &DominatingSafeChecks,
|
|
|
|
|
SILLoop *Loop,
|
|
|
|
|
int recursionDepth);
|
|
|
|
|
bool removeRedundantArrayBoundsChecksInLoop(
|
|
|
|
|
DominanceInfoNode *CurBB, ABCAnalysis &ABC,
|
|
|
|
|
IndexedArraySet &DominatingSafeChecks, SILLoop *Loop, int recursionDepth);
|
|
|
|
|
/// Analyze the loop for arrays that are not modified and perform dominator
|
|
|
|
|
/// tree based redundant bounds check removal.
|
|
|
|
|
bool hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
InductionAnalysis &IndVars, SILBasicBlock *Preheader,
|
|
|
|
|
SILBasicBlock *Header,
|
|
|
|
|
SILBasicBlock *SingleExitingBlk,
|
|
|
|
|
int recursionDepth);
|
|
|
|
|
bool hoistArrayBoundsChecksInLoop(SILLoop *loop,
|
|
|
|
|
DominanceInfoNode *currentNode,
|
|
|
|
|
ABCAnalysis &abcAnalysis,
|
|
|
|
|
InductionAnalysis &indVars,
|
|
|
|
|
int recursionDepth);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void run() override {
|
|
|
|
|
if (!EnableABCOpts)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SILFunction *F = getFunction();
|
|
|
|
|
LI = PM->getAnalysis<SILLoopAnalysis>()->get(F);
|
|
|
|
|
DT = PM->getAnalysis<DominanceAnalysis>()->get(F);
|
|
|
|
|
IVs = PM->getAnalysis<IVAnalysis>()->get(F);
|
|
|
|
|
RCIA = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
|
|
|
|
|
bool changed = false;
|
|
|
|
|
SILFunction *func = getFunction();
|
|
|
|
|
LI = PM->getAnalysis<SILLoopAnalysis>()->get(func);
|
|
|
|
|
DT = PM->getAnalysis<DominanceAnalysis>()->get(func);
|
|
|
|
|
IVs = PM->getAnalysis<IVAnalysis>()->get(func);
|
|
|
|
|
RCIA = PM->getAnalysis<RCIdentityAnalysis>()->get(func);
|
|
|
|
|
DestAnalysis = PM->getAnalysis<DestructorAnalysis>();
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
if (ShouldReportBoundsChecks) {
|
|
|
|
|
reportBoundsChecks(F);
|
|
|
|
|
reportBoundsChecks(func);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
|
<< "ArrayBoundsCheckOpts on function: " << F->getName() << "\n");
|
|
|
|
|
// Collect all arrays in this function. A release is only 'safe' if we know
|
|
|
|
|
// its deinitializer does not have sideeffects that could cause memory
|
|
|
|
|
// safety issues. A deinit could deallocate array or put a different array
|
|
|
|
|
// in its location.
|
|
|
|
|
for (auto &BB : *F) {
|
|
|
|
|
for (auto &Inst : BB) {
|
|
|
|
|
ArraySemanticsCall Call(&Inst);
|
|
|
|
|
if (Call && Call.hasSelf()) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Gathering " << *(ApplyInst *)Call);
|
|
|
|
|
auto rcRoot = RCIA->getRCIdentityRoot(Call.getSelf());
|
|
|
|
|
// Check the type of the array. We need to have an array element type
|
|
|
|
|
// that is not calling a deinit function.
|
|
|
|
|
if (DestAnalysis->mayStoreToMemoryOnDestruction(rcRoot->getType()))
|
|
|
|
|
continue;
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "ReleaseSafeArray: " << rcRoot << "\n");
|
|
|
|
|
ReleaseSafeArrays.insert(rcRoot);
|
|
|
|
|
ReleaseSafeArrays.insert(
|
|
|
|
|
getArrayStructPointer(ArrayCallKind::kCheckIndex, rcRoot));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Remove redundant checks on a per basic block basis.
|
|
|
|
|
bool Changed = false;
|
|
|
|
|
for (auto &BB : *F)
|
|
|
|
|
Changed |= removeRedundantChecksInBlock(BB);
|
|
|
|
|
<< "BoundsCheckOpts on function: " << func->getName() << "\n");
|
|
|
|
|
|
|
|
|
|
collectReleaseSafeArrays();
|
|
|
|
|
changed |= optimizeBoundsChecksInBlocks();
|
|
|
|
|
changed |= optimizeBoundsChecksInLoops();
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
if (ShouldReportBoundsChecks) {
|
|
|
|
|
reportBoundsChecks(F);
|
|
|
|
|
reportBoundsChecks(func);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (LI->empty()) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "No loops in " << F->getName() << "\n");
|
|
|
|
|
if (Changed) {
|
|
|
|
|
PM->invalidateAnalysis(
|
|
|
|
|
F, SILAnalysis::InvalidationKind::CallsAndInstructions);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Remove redundant checks along the dominator tree in a loop and hoist
|
|
|
|
|
// checks.
|
|
|
|
|
for (auto *LoopIt : *LI) {
|
|
|
|
|
// Process loops recursively bottom-up in the loop tree.
|
|
|
|
|
SmallVector<SILLoop *, 8> Worklist;
|
|
|
|
|
Worklist.push_back(LoopIt);
|
|
|
|
|
for (unsigned i = 0; i < Worklist.size(); ++i) {
|
|
|
|
|
auto *L = Worklist[i];
|
|
|
|
|
for (auto *SubLoop : *L)
|
|
|
|
|
Worklist.push_back(SubLoop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!Worklist.empty()) {
|
|
|
|
|
Changed |= processLoop(Worklist.pop_back_val());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
if (ShouldReportBoundsChecks) {
|
|
|
|
|
reportBoundsChecks(F);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (Changed) {
|
|
|
|
|
if (changed) {
|
|
|
|
|
PM->invalidateAnalysis(
|
|
|
|
|
F, SILAnalysis::InvalidationKind::CallsAndInstructions);
|
|
|
|
|
func, SILAnalysis::InvalidationKind::CallsAndInstructions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool ABCOpt::removeRedundantChecksInBlock(SILBasicBlock &BB) {
|
|
|
|
|
ABCAnalysis ABC(false, ReleaseSafeArrays, RCIA);
|
|
|
|
|
void BoundsCheckOpts::collectReleaseSafeArrays() {
|
|
|
|
|
auto *func = getFunction();
|
|
|
|
|
|
|
|
|
|
for (auto &block : *func) {
|
|
|
|
|
for (auto &inst : block) {
|
|
|
|
|
ArraySemanticsCall semanticsCall(&inst);
|
|
|
|
|
if (!semanticsCall || !semanticsCall.hasSelf()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Gathering " << *(ApplyInst *)semanticsCall);
|
|
|
|
|
auto rcRoot = RCIA->getRCIdentityRoot(semanticsCall.getSelf());
|
|
|
|
|
// Check the type of the array. We need to have an array element type
|
|
|
|
|
// that is not calling a deinit function.
|
|
|
|
|
if (DestAnalysis->mayStoreToMemoryOnDestruction(rcRoot->getType()))
|
|
|
|
|
continue;
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "ReleaseSafeArray: " << rcRoot << "\n");
|
|
|
|
|
releaseSafeArrays.insert(rcRoot);
|
|
|
|
|
releaseSafeArrays.insert(
|
|
|
|
|
getArrayStructPointer(ArrayCallKind::kCheckIndex, rcRoot));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BoundsCheckOpts::optimizeBoundsChecksInBlocks() {
|
|
|
|
|
bool changed = false;
|
|
|
|
|
for (auto &block : *getFunction()) {
|
|
|
|
|
changed |= removeRedundantArrayChecksInBlock(block);
|
|
|
|
|
}
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BoundsCheckOpts::optimizeBoundsChecksInLoops() {
|
|
|
|
|
bool changed = false;
|
|
|
|
|
// Process loops recursively bottom-up in the loop tree.
|
|
|
|
|
for (auto *loop : *LI) {
|
|
|
|
|
SmallVector<SILLoop *, 8> worklist;
|
|
|
|
|
worklist.push_back(loop);
|
|
|
|
|
for (unsigned i = 0; i < worklist.size(); ++i) {
|
|
|
|
|
for (auto *subLoop : *worklist[i]) {
|
|
|
|
|
worklist.push_back(subLoop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!worklist.empty()) {
|
|
|
|
|
changed |= optimizeArrayBoundsCheckInLoop(worklist.pop_back_val());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BoundsCheckOpts::removeRedundantArrayChecksInBlock(SILBasicBlock &BB) {
|
|
|
|
|
ABCAnalysis ABC(false, releaseSafeArrays, RCIA);
|
|
|
|
|
IndexedArraySet RedundantChecks;
|
|
|
|
|
bool Changed = false;
|
|
|
|
|
|
|
|
|
|
@@ -1075,7 +1090,6 @@ bool ABCOpt::removeRedundantChecksInBlock(SILBasicBlock &BB) {
|
|
|
|
|
auto Kind = ArrayCall.getKind();
|
|
|
|
|
if (Kind != ArrayCallKind::kCheckSubscript &&
|
|
|
|
|
Kind != ArrayCallKind::kCheckIndex) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " not a check_bounds call " << *Inst);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto Array = ArrayCall.getSelf();
|
|
|
|
|
@@ -1113,11 +1127,9 @@ bool ABCOpt::removeRedundantChecksInBlock(SILBasicBlock &BB) {
|
|
|
|
|
return Changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB,
|
|
|
|
|
ABCAnalysis &ABC,
|
|
|
|
|
IndexedArraySet &DominatingSafeChecks,
|
|
|
|
|
SILLoop *Loop,
|
|
|
|
|
int recursionDepth) {
|
|
|
|
|
bool BoundsCheckOpts::removeRedundantArrayBoundsChecksInLoop(
|
|
|
|
|
DominanceInfoNode *CurBB, ABCAnalysis &ABC,
|
|
|
|
|
IndexedArraySet &DominatingSafeChecks, SILLoop *Loop, int recursionDepth) {
|
|
|
|
|
auto *BB = CurBB->getBlock();
|
|
|
|
|
if (!Loop->contains(BB))
|
|
|
|
|
return false;
|
|
|
|
|
@@ -1142,7 +1154,6 @@ bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB,
|
|
|
|
|
auto Kind = ArrayCall.getKind();
|
|
|
|
|
if (Kind != ArrayCallKind::kCheckSubscript &&
|
|
|
|
|
Kind != ArrayCallKind::kCheckIndex) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " not a check_bounds call " << *Inst);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto Array = ArrayCall.getSelf();
|
|
|
|
|
@@ -1178,9 +1189,8 @@ bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB,
|
|
|
|
|
|
|
|
|
|
// Traverse the children in the dominator tree inside the loop.
|
|
|
|
|
for (auto Child : *CurBB)
|
|
|
|
|
Changed |=
|
|
|
|
|
removeRedundantChecksInLoop(Child, ABC, DominatingSafeChecks, Loop,
|
|
|
|
|
recursionDepth + 1);
|
|
|
|
|
Changed |= removeRedundantArrayBoundsChecksInLoop(
|
|
|
|
|
Child, ABC, DominatingSafeChecks, Loop, recursionDepth + 1);
|
|
|
|
|
|
|
|
|
|
// Remove checks we have seen for the first time.
|
|
|
|
|
std::for_each(SafeChecksToPop.begin(), SafeChecksToPop.end(),
|
|
|
|
|
@@ -1191,162 +1201,197 @@ bool ABCOpt::removeRedundantChecksInLoop(DominanceInfoNode *CurBB,
|
|
|
|
|
return Changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ABCOpt::processLoop(SILLoop *Loop) {
|
|
|
|
|
auto *Header = Loop->getHeader();
|
|
|
|
|
if (!Header)
|
|
|
|
|
bool BoundsCheckOpts::optimizeArrayBoundsCheckInLoop(SILLoop *loop) {
|
|
|
|
|
auto *header = loop->getHeader();
|
|
|
|
|
if (!header) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto *Preheader = Loop->getLoopPreheader();
|
|
|
|
|
if (!Preheader) {
|
|
|
|
|
auto *preheader = loop->getLoopPreheader();
|
|
|
|
|
if (!preheader) {
|
|
|
|
|
// TODO: create one if necessary.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only handle innermost loops for now.
|
|
|
|
|
if (!Loop->getSubLoops().empty())
|
|
|
|
|
if (!loop->getSubLoops().empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Attempting to remove redundant checks in "
|
|
|
|
|
<< *Loop);
|
|
|
|
|
LLVM_DEBUG(
|
|
|
|
|
llvm::dbgs() << "Attempting to remove redundant array bounds checks in "
|
|
|
|
|
<< *loop);
|
|
|
|
|
|
|
|
|
|
// Collect safe arrays. Arrays are safe if there is no function call that
|
|
|
|
|
// could mutate their size in the loop.
|
|
|
|
|
ABCAnalysis ABC(true, ReleaseSafeArrays, RCIA);
|
|
|
|
|
for (auto *BB : Loop->getBlocks()) {
|
|
|
|
|
ABCAnalysis ABC(true, releaseSafeArrays, RCIA);
|
|
|
|
|
for (auto *BB : loop->getBlocks()) {
|
|
|
|
|
ABC.analyzeBlock(BB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove redundant checks down the dominator tree inside the loop,
|
|
|
|
|
// starting at the header.
|
|
|
|
|
// We may not go to dominated blocks outside the loop, because we didn't
|
|
|
|
|
// check for safety outside the loop (with ABCAnalysis).
|
|
|
|
|
IndexedArraySet DominatingSafeChecks;
|
|
|
|
|
bool Changed = removeRedundantChecksInLoop(DT->getNode(Header), ABC,
|
|
|
|
|
DominatingSafeChecks, Loop,
|
|
|
|
|
/*recursionDepth*/ 0);
|
|
|
|
|
bool changed = false;
|
|
|
|
|
auto result = findAndOptimizeInductionVariables(loop);
|
|
|
|
|
changed |= result.first;
|
|
|
|
|
changed |= optimizeArrayBoundsCheckInLoop(loop, std::move(result.second));
|
|
|
|
|
|
|
|
|
|
if (!EnableABCHoisting)
|
|
|
|
|
return Changed;
|
|
|
|
|
if (changed) {
|
|
|
|
|
preheader->getParent()->verify(
|
|
|
|
|
getAnalysis<BasicCalleeAnalysis>()->getCalleeCache());
|
|
|
|
|
}
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Attempting to hoist checks in " << *Loop);
|
|
|
|
|
|
|
|
|
|
// Find an exiting block.
|
|
|
|
|
SILBasicBlock *SingleExitingBlk = Loop->getExitingBlock();
|
|
|
|
|
SILBasicBlock *ExitingBlk = SingleExitingBlk;
|
|
|
|
|
SILBasicBlock *ExitBlk = Loop->getExitBlock();
|
|
|
|
|
SILBasicBlock *Latch = Loop->getLoopLatch();
|
|
|
|
|
if (!ExitingBlk || !Latch || !ExitBlk) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "No single exiting block or latch found\n");
|
|
|
|
|
if (!Latch)
|
|
|
|
|
return Changed;
|
|
|
|
|
|
|
|
|
|
// Look back a split edge.
|
|
|
|
|
if (!Loop->isLoopExiting(Latch) && Latch->getSinglePredecessorBlock() &&
|
|
|
|
|
Loop->isLoopExiting(Latch->getSinglePredecessorBlock()))
|
|
|
|
|
Latch = Latch->getSinglePredecessorBlock();
|
|
|
|
|
if (Loop->isLoopExiting(Latch) && Latch->getSuccessors().size() == 2) {
|
|
|
|
|
ExitingBlk = Latch;
|
|
|
|
|
ExitBlk = Loop->contains(Latch->getSuccessors()[0])
|
|
|
|
|
? Latch->getSuccessors()[1]
|
|
|
|
|
: Latch->getSuccessors()[0];
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Found a latch ...\n");
|
|
|
|
|
} else return Changed;
|
|
|
|
|
std::pair<bool, std::optional<InductionAnalysis>>
|
|
|
|
|
BoundsCheckOpts::findAndOptimizeInductionVariables(SILLoop *loop) {
|
|
|
|
|
SILBasicBlock *singleExitingBlock = loop->getExitingBlock();
|
|
|
|
|
SILBasicBlock *exitingBlk = singleExitingBlock;
|
|
|
|
|
SILBasicBlock *exitBlock = loop->getExitBlock();
|
|
|
|
|
SILBasicBlock *latch = loop->getLoopLatch();
|
|
|
|
|
if (!exitingBlk || !latch || !exitBlock) {
|
|
|
|
|
if (!latch) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "No latch found\n");
|
|
|
|
|
return {false, std::nullopt};
|
|
|
|
|
}
|
|
|
|
|
if (!loop->isLoopExiting(latch) || latch->getSuccessors().size() != 2) {
|
|
|
|
|
return {false, std::nullopt};
|
|
|
|
|
}
|
|
|
|
|
exitingBlk = latch;
|
|
|
|
|
exitBlock = loop->contains(latch->getSuccessors()[0])
|
|
|
|
|
? latch->getSuccessors()[1]
|
|
|
|
|
: latch->getSuccessors()[0];
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Found a latch ...\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find canonical induction variables.
|
|
|
|
|
InductionAnalysis IndVars(DT, *IVs, Preheader, Header, ExitingBlk, ExitBlk);
|
|
|
|
|
bool IVarsFound = IndVars.analyze();
|
|
|
|
|
if (!IVarsFound) {
|
|
|
|
|
InductionAnalysis indVars(DT, *IVs, loop->getLoopPreheader(),
|
|
|
|
|
loop->getHeader(), exitingBlk, exitBlock);
|
|
|
|
|
bool found = indVars.analyze();
|
|
|
|
|
if (!found) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "No induction variables found\n");
|
|
|
|
|
return {false, std::nullopt};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hoist the overflow check of induction variables out of the loop. This also
|
|
|
|
|
// needs to happen for memory safety. Also remove superfluous range checks.
|
|
|
|
|
if (IVarsFound) {
|
|
|
|
|
SILValue TrueVal;
|
|
|
|
|
SILValue FalseVal;
|
|
|
|
|
for (auto *Arg : Header->getArguments()) {
|
|
|
|
|
if (auto *IV = IndVars[Arg]) {
|
|
|
|
|
SILBuilderWithScope B(Preheader->getTerminator(), IV->getInstruction());
|
|
|
|
|
bool changed = false;
|
|
|
|
|
SILValue trueVal, falseVal;
|
|
|
|
|
for (auto *arg : loop->getHeader()->getArguments()) {
|
|
|
|
|
if (auto *ivar = indVars[arg]) {
|
|
|
|
|
SILBuilderWithScope builder(loop->getLoopPreheader()->getTerminator(),
|
|
|
|
|
ivar->getInstruction());
|
|
|
|
|
|
|
|
|
|
// Only if the loop has a single exiting block (which contains the
|
|
|
|
|
// induction variable check) we may hoist the overflow check.
|
|
|
|
|
if (SingleExitingBlk)
|
|
|
|
|
Changed |= IV->checkOverflow(B);
|
|
|
|
|
|
|
|
|
|
if (!IV->IsOverflowCheckInserted)
|
|
|
|
|
// Only if the loop has a single exiting block (which contains the
|
|
|
|
|
// induction variable check) we may hoist the overflow check.
|
|
|
|
|
if (singleExitingBlock) {
|
|
|
|
|
changed |= ivar->checkOverflow(builder);
|
|
|
|
|
if (!ivar->IsOverflowCheckInserted) {
|
|
|
|
|
continue;
|
|
|
|
|
for (auto *BB : Loop->getBlocks())
|
|
|
|
|
for (auto &Inst : *BB) {
|
|
|
|
|
auto *Builtin = dyn_cast<BuiltinInst>(&Inst);
|
|
|
|
|
if (!Builtin)
|
|
|
|
|
continue;
|
|
|
|
|
if (isComparisonKnownTrue(Builtin, *IV)) {
|
|
|
|
|
if (!TrueVal)
|
|
|
|
|
TrueVal = SILValue(B.createIntegerLiteral(
|
|
|
|
|
Builtin->getLoc(), Builtin->getType(), -1));
|
|
|
|
|
Builtin->replaceAllUsesWith(TrueVal);
|
|
|
|
|
Changed = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (auto *block : loop->getBlocks()) {
|
|
|
|
|
for (auto &inst : *block) {
|
|
|
|
|
auto *builtin = dyn_cast<BuiltinInst>(&inst);
|
|
|
|
|
if (!builtin) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isComparisonKnownTrue(builtin, *ivar)) {
|
|
|
|
|
if (!trueVal)
|
|
|
|
|
trueVal = builder.createIntegerLiteral(builtin->getLoc(),
|
|
|
|
|
builtin->getType(), -1);
|
|
|
|
|
builtin->replaceAllUsesWith(trueVal);
|
|
|
|
|
changed = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isComparisonKnownFalse(builtin, *ivar)) {
|
|
|
|
|
if (!falseVal) {
|
|
|
|
|
falseVal = builder.createIntegerLiteral(builtin->getLoc(),
|
|
|
|
|
builtin->getType(), 0);
|
|
|
|
|
}
|
|
|
|
|
if (isComparisonKnownFalse(Builtin, *IV)) {
|
|
|
|
|
if (!FalseVal) {
|
|
|
|
|
FalseVal = SILValue(B.createIntegerLiteral(
|
|
|
|
|
Builtin->getLoc(), Builtin->getType(), 0));
|
|
|
|
|
builtin->replaceAllUsesWith(falseVal);
|
|
|
|
|
changed = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Check whether a dominating check of the condition let's us
|
|
|
|
|
// replace
|
|
|
|
|
// the condition by false.
|
|
|
|
|
SILValue left, right;
|
|
|
|
|
if (match(builtin, m_Or(m_SILValue(left), m_SILValue(right)))) {
|
|
|
|
|
if (isValueKnownFalseAt(left, builtin, DT)) {
|
|
|
|
|
if (!falseVal) {
|
|
|
|
|
falseVal = builder.createIntegerLiteral(builtin->getLoc(),
|
|
|
|
|
builtin->getType(), 0);
|
|
|
|
|
}
|
|
|
|
|
Builtin->replaceAllUsesWith(FalseVal);
|
|
|
|
|
Changed = true;
|
|
|
|
|
continue;
|
|
|
|
|
builtin->setOperand(0, falseVal);
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
// Check whether a dominating check of the condition let's us
|
|
|
|
|
// replace
|
|
|
|
|
// the condition by false.
|
|
|
|
|
SILValue Left, Right;
|
|
|
|
|
if (match(Builtin, m_Or(m_SILValue(Left), m_SILValue(Right)))) {
|
|
|
|
|
if (isValueKnownFalseAt(Left, Builtin, DT)) {
|
|
|
|
|
if (!FalseVal)
|
|
|
|
|
FalseVal = SILValue(B.createIntegerLiteral(
|
|
|
|
|
Builtin->getLoc(), Builtin->getType(), 0));
|
|
|
|
|
Builtin->setOperand(0, FalseVal);
|
|
|
|
|
Changed = true;
|
|
|
|
|
}
|
|
|
|
|
if (isValueKnownFalseAt(Right, Builtin, DT)) {
|
|
|
|
|
if (!FalseVal)
|
|
|
|
|
FalseVal = SILValue(B.createIntegerLiteral(
|
|
|
|
|
Builtin->getLoc(), Builtin->getType(), 0));
|
|
|
|
|
Builtin->setOperand(1, FalseVal);
|
|
|
|
|
Changed = true;
|
|
|
|
|
if (isValueKnownFalseAt(right, builtin, DT)) {
|
|
|
|
|
if (!falseVal) {
|
|
|
|
|
falseVal = builder.createIntegerLiteral(builtin->getLoc(),
|
|
|
|
|
builtin->getType(), 0);
|
|
|
|
|
}
|
|
|
|
|
builtin->setOperand(1, falseVal);
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hoist bounds checks.
|
|
|
|
|
Changed |= hoistChecksInLoop(DT->getNode(Header), ABC, IndVars, Preheader,
|
|
|
|
|
Header, SingleExitingBlk, /*recursionDepth*/ 0);
|
|
|
|
|
if (Changed) {
|
|
|
|
|
Preheader->getParent()->verify(getAnalysis<BasicCalleeAnalysis>()->getCalleeCache());
|
|
|
|
|
}
|
|
|
|
|
return Changed;
|
|
|
|
|
return {changed, std::make_optional(std::move(indVars))};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
InductionAnalysis &IndVars,
|
|
|
|
|
SILBasicBlock *Preheader, SILBasicBlock *Header,
|
|
|
|
|
SILBasicBlock *SingleExitingBlk,
|
|
|
|
|
int recursionDepth) {
|
|
|
|
|
bool BoundsCheckOpts::optimizeArrayBoundsCheckInLoop(
|
|
|
|
|
SILLoop *loop, std::optional<InductionAnalysis> indVars) {
|
|
|
|
|
|
|
|
|
|
// Collect safe arrays. Arrays are safe if there is no function call that
|
|
|
|
|
// could mutate their size in the loop.
|
|
|
|
|
ABCAnalysis abcAnalysis(true, releaseSafeArrays, RCIA);
|
|
|
|
|
for (auto *block : loop->getBlocks()) {
|
|
|
|
|
abcAnalysis.analyzeBlock(block);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove redundant checks down the dominator tree inside the loop,
|
|
|
|
|
// starting at the header.
|
|
|
|
|
IndexedArraySet dominatingSafeArrayChecks;
|
|
|
|
|
bool changed = removeRedundantArrayBoundsChecksInLoop(
|
|
|
|
|
DT->getNode(loop->getHeader()), abcAnalysis, dominatingSafeArrayChecks,
|
|
|
|
|
loop,
|
|
|
|
|
/*recursionDepth*/ 0);
|
|
|
|
|
|
|
|
|
|
if (!EnableABCHoisting) {
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Attempting to hoist array bounds checks in "
|
|
|
|
|
<< *loop);
|
|
|
|
|
|
|
|
|
|
if (!indVars) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "No induction variables found\n");
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hoist bounds checks.
|
|
|
|
|
changed |=
|
|
|
|
|
hoistArrayBoundsChecksInLoop(loop, DT->getNode(loop->getHeader()),
|
|
|
|
|
abcAnalysis, *indVars, /*recursionDepth*/ 0);
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BoundsCheckOpts::hoistArrayBoundsChecksInLoop(
|
|
|
|
|
SILLoop *loop, DominanceInfoNode *currentNode, ABCAnalysis &abcAnalysis,
|
|
|
|
|
InductionAnalysis &indVars, int recursionDepth) {
|
|
|
|
|
auto preheader = loop->getLoopPreheader();
|
|
|
|
|
auto singleExitingBlock = loop->getExitingBlock();
|
|
|
|
|
// Avoid a stack overflow for very deep dominator trees.
|
|
|
|
|
if (recursionDepth >= maxRecursionDepth)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
bool Changed = false;
|
|
|
|
|
auto *CurBB = DTNode->getBlock();
|
|
|
|
|
bool changed = false;
|
|
|
|
|
auto *curBlock = currentNode->getBlock();
|
|
|
|
|
bool blockAlwaysExecutes =
|
|
|
|
|
isGuaranteedToBeExecuted(DT, CurBB, SingleExitingBlk);
|
|
|
|
|
isGuaranteedToBeExecuted(DT, curBlock, singleExitingBlock);
|
|
|
|
|
|
|
|
|
|
for (auto Iter = CurBB->begin(); Iter != CurBB->end();) {
|
|
|
|
|
for (auto Iter = curBlock->begin(); Iter != curBlock->end();) {
|
|
|
|
|
auto Inst = &*Iter;
|
|
|
|
|
++Iter;
|
|
|
|
|
|
|
|
|
|
@@ -1354,7 +1399,6 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
auto Kind = ArrayCall.getKind();
|
|
|
|
|
if (Kind != ArrayCallKind::kCheckSubscript &&
|
|
|
|
|
Kind != ArrayCallKind::kCheckIndex) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " not a check_bounds call " << *Inst);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto ArrayVal = ArrayCall.getSelf();
|
|
|
|
|
@@ -1363,7 +1407,7 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
SILValue Array = getArrayStructPointer(Kind, ArrayVal);
|
|
|
|
|
|
|
|
|
|
// The array must strictly dominate the header.
|
|
|
|
|
if (!dominates(DT, Array, Preheader)) {
|
|
|
|
|
if (!dominates(DT, Array, preheader)) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " does not dominated header" << *Array);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
@@ -1372,7 +1416,7 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
// This is either a SILValue which is defined outside the loop or it is an
|
|
|
|
|
// array, which loaded from memory and the memory is not changed in the
|
|
|
|
|
// loop.
|
|
|
|
|
if (!dominates(DT, ArrayVal, Preheader) && ABC.isUnsafe(Array)) {
|
|
|
|
|
if (!dominates(DT, ArrayVal, preheader) && abcAnalysis.isUnsafe(Array)) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " not a safe array argument " << *Array);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
@@ -1383,15 +1427,15 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Make sure we know how-to hoist the array call.
|
|
|
|
|
if (!ArrayCall.canHoist(Preheader->getTerminator(), DT))
|
|
|
|
|
if (!ArrayCall.canHoist(preheader->getTerminator(), DT))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Invariant check.
|
|
|
|
|
if (blockAlwaysExecutes && dominates(DT, ArrayIndex, Preheader)) {
|
|
|
|
|
assert(ArrayCall.canHoist(Preheader->getTerminator(), DT) &&
|
|
|
|
|
if (blockAlwaysExecutes && dominates(DT, ArrayIndex, preheader)) {
|
|
|
|
|
assert(ArrayCall.canHoist(preheader->getTerminator(), DT) &&
|
|
|
|
|
"Must be able to hoist the instruction.");
|
|
|
|
|
Changed = true;
|
|
|
|
|
ArrayCall.hoist(Preheader->getTerminator(), DT);
|
|
|
|
|
changed = true;
|
|
|
|
|
ArrayCall.hoist(preheader->getTerminator(), DT);
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
|
<< " could hoist invariant bounds check: " << *Inst);
|
|
|
|
|
continue;
|
|
|
|
|
@@ -1399,7 +1443,7 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
|
|
|
|
|
// Get the access function "a[f(i)]". At the moment this handles only the
|
|
|
|
|
// identity function.
|
|
|
|
|
auto F = AccessFunction::getLinearFunction(ArrayIndex, IndVars);
|
|
|
|
|
auto F = AccessFunction::getLinearFunction(ArrayIndex, indVars);
|
|
|
|
|
if (!F) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " not a linear function " << *Inst);
|
|
|
|
|
continue;
|
|
|
|
|
@@ -1411,7 +1455,7 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
ArrayVal->getType().getASTType()->isArray()) {
|
|
|
|
|
// We can remove the check. This is even possible if the block does not
|
|
|
|
|
// dominate the loop exit block.
|
|
|
|
|
Changed = true;
|
|
|
|
|
changed = true;
|
|
|
|
|
ArrayCall.removeCall();
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " Bounds check removed\n");
|
|
|
|
|
continue;
|
|
|
|
|
@@ -1423,28 +1467,27 @@ bool ABCOpt::hoistChecksInLoop(DominanceInfoNode *DTNode, ABCAnalysis &ABC,
|
|
|
|
|
|
|
|
|
|
// Hoist the access function and the check to the preheader for start and
|
|
|
|
|
// end of the induction.
|
|
|
|
|
assert(ArrayCall.canHoist(Preheader->getTerminator(), DT) &&
|
|
|
|
|
assert(ArrayCall.canHoist(preheader->getTerminator(), DT) &&
|
|
|
|
|
"Must be able to hoist the call");
|
|
|
|
|
|
|
|
|
|
F.hoistCheckToPreheader(ArrayCall, Preheader, DT);
|
|
|
|
|
F.hoistCheckToPreheader(ArrayCall, preheader, DT);
|
|
|
|
|
|
|
|
|
|
// Remove the old check in the loop and the match the retain with a release.
|
|
|
|
|
ArrayCall.removeCall();
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " Bounds check hoisted\n");
|
|
|
|
|
Changed = true;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traverse the children in the dominator tree.
|
|
|
|
|
for (auto Child : *DTNode)
|
|
|
|
|
Changed |= hoistChecksInLoop(Child, ABC, IndVars, Preheader, Header,
|
|
|
|
|
SingleExitingBlk, recursionDepth + 1);
|
|
|
|
|
for (auto child : *currentNode) {
|
|
|
|
|
changed |= hoistArrayBoundsChecksInLoop(loop, child, abcAnalysis, indVars,
|
|
|
|
|
recursionDepth + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Changed;
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
|
|
SILTransform *swift::createABCOpt() {
|
|
|
|
|
return new ABCOpt();
|
|
|
|
|
}
|
|
|
|
|
SILTransform *swift::createBoundsCheckOpts() { return new BoundsCheckOpts(); }
|