mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
One common problem in swift code is the "reforming enum problem". What
happens here is that we have some enum %0 : $Optional<T> and we break it
apart and reform it as a new enum as in the following:
bb9:
...
switch_enum %0 : $Optional<T>, #Optional.None: bb10,
#Optional.Some: bb11
bb10:
%1 = enum $Optional<U>, #Optional.None
br bb12(%1 : $Optional<U>)
bb11:
%2 = some_cast_to_u %0 : ...
%3 = enum $Optional<U>, #Optional.Some, %2 : $U
br bb12(%3 : $Optional<U>)
bb12(%4 : $Optional<U>):
retain_value %0 : $Optional<T> // id %5
release_value %4 : $Optional<U> // id %6
We really would like to know that a retain on %4 is equivalent to a
retain on %0 so we can eliminate the retain, release pair. To be able to
do that safely, we need to know that along all paths %0 and %4 either:
1. Both refer to the same RCIdentity directly. An example of this is the
edge from bb11 -> bb12).
2. Both refer to the "null" RCIdentity (i.e. do not have a payload). An
example of this is the edge from bb10 -> bb12.
Only in such cases is it safe to match up %5, %6 and eliminate them. If
this is not true along all paths like in the following:
bb9:
...
cond_br %foo, bb10, bb11
bb10:
%1 = enum $Optional<U>, #Optional.None
br bb12(%1 : $Optional<U>)
bb11:
%2 = some_cast_to_u %0 : ...
%3 = enum $Optional<U>, #Optional.Some, %2 : $U
br bb12(%3 : $Optional<U>)
bb12(%4 : $Optional<U>):
retain_value %0 : $Optional<T> // id %5
release_value %4 : $Optional<U> // id %6
then we may have that %0 is always non-payloaded coming into bb12. Then
by matching up %0 and %4, if we go from bb9 -> bb11, we will lose a
retain.
Perf Changes:
TITLE..................OLD...........NEW...........NEW/OLD
LevenshteinDistance....1398195.00....1177397.00....0.84
Memset.................26541.00......23701.00......0.89
CaptureProp............5603.00.......5031.00.......0.90
ImageProc..............1281.00.......1196.00.......0.93
InsertionSort..........109828.00.....104129.00.....0.95
StringWalk.............6813.00.......7456.00.......1.09
Chars..................27182.00......30443.00......1.12
The StringWalk, Chars are both reproducible for me. When I turn back on parts of
the recursion (I took the recursion out to make this change more conservative),
the Chars regression goes away, but the StringWalk stays. I have not had a
chance to look at what is going on with StringWalk.
rdar://19724405
Swift SVN r25339
233 lines
6.7 KiB
C++
233 lines
6.7 KiB
C++
//===--- SILArgument.cpp - Arguments for high-level SIL code ---------------==//
|
|
//
|
|
// 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 "llvm/ADT/STLExtras.h"
|
|
#include "swift/SIL/SILBasicBlock.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SILArgument Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SILArgument::SILArgument(SILBasicBlock *ParentBB, SILType Ty,
|
|
const ValueDecl *D)
|
|
: ValueBase(ValueKind::SILArgument, Ty), ParentBB(ParentBB), Decl(D) {
|
|
// Function arguments need to have a decl.
|
|
assert(
|
|
!ParentBB->getParent()->isBare() &&
|
|
ParentBB->getParent()->size() == 1
|
|
? D != nullptr
|
|
: true );
|
|
ParentBB->insertArgument(ParentBB->bbarg_end(), this);
|
|
}
|
|
|
|
SILArgument::SILArgument(SILBasicBlock *ParentBB,
|
|
SILBasicBlock::bbarg_iterator Pos,
|
|
SILType Ty, const ValueDecl *D)
|
|
: ValueBase(ValueKind::SILArgument, Ty), ParentBB(ParentBB), Decl(D) {
|
|
// Function arguments need to have a decl.
|
|
assert(
|
|
!ParentBB->getParent()->isBare() &&
|
|
ParentBB->getParent()->size() == 1
|
|
? D != nullptr
|
|
: true );
|
|
ParentBB->insertArgument(Pos, this);
|
|
}
|
|
|
|
|
|
SILFunction *SILArgument::getFunction() {
|
|
return getParent()->getParent();
|
|
}
|
|
const SILFunction *SILArgument::getFunction() const {
|
|
return getParent()->getParent();
|
|
}
|
|
|
|
SILModule &SILArgument::getModule() const {
|
|
return getFunction()->getModule();
|
|
}
|
|
|
|
bool SILArgument::getIncomingValues(llvm::SmallVectorImpl<SILValue> &OutArray) {
|
|
SILBasicBlock *Parent = getParent();
|
|
|
|
if (Parent->pred_empty())
|
|
return false;
|
|
|
|
unsigned Index = getIndex();
|
|
for (SILBasicBlock *Pred : getParent()->getPreds()) {
|
|
TermInst *TI = Pred->getTerminator();
|
|
|
|
if (auto *BI = dyn_cast<BranchInst>(TI)) {
|
|
OutArray.push_back(BI->getArg(Index));
|
|
continue;
|
|
}
|
|
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(TI)) {
|
|
OutArray.push_back(CBI->getArgForDestBB(getParent(), this));
|
|
continue;
|
|
}
|
|
|
|
if (auto *CCBI = dyn_cast<CheckedCastBranchInst>(TI)) {
|
|
OutArray.push_back(CCBI->getOperand());
|
|
continue;
|
|
}
|
|
|
|
if (auto *SWEI = dyn_cast<SwitchEnumInst>(TI)) {
|
|
OutArray.push_back(SWEI->getOperand());
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SILArgument::getIncomingValues(
|
|
llvm::SmallVectorImpl<std::pair<SILBasicBlock *, SILValue>> &OutArray) {
|
|
SILBasicBlock *Parent = getParent();
|
|
|
|
if (Parent->pred_empty())
|
|
return false;
|
|
|
|
unsigned Index = getIndex();
|
|
for (SILBasicBlock *Pred : getParent()->getPreds()) {
|
|
TermInst *TI = Pred->getTerminator();
|
|
|
|
if (auto *BI = dyn_cast<BranchInst>(TI)) {
|
|
OutArray.push_back({Pred, BI->getArg(Index)});
|
|
continue;
|
|
}
|
|
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(TI)) {
|
|
OutArray.push_back({Pred, CBI->getArgForDestBB(getParent(), this)});
|
|
continue;
|
|
}
|
|
|
|
if (auto *CCBI = dyn_cast<CheckedCastBranchInst>(TI)) {
|
|
OutArray.push_back({Pred, CCBI->getOperand()});
|
|
continue;
|
|
}
|
|
|
|
if (auto *SWEI = dyn_cast<SwitchEnumInst>(TI)) {
|
|
OutArray.push_back({Pred, SWEI->getOperand()});
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SILValue SILArgument::getIncomingValue(unsigned BBIndex) {
|
|
SILBasicBlock *Parent = getParent();
|
|
|
|
if (Parent->pred_empty())
|
|
return SILValue();
|
|
|
|
unsigned Index = getIndex();
|
|
|
|
// We could do an early check if the size of the pred list is <= BBIndex, but
|
|
// that would involve walking the linked list anyways, so we just iterate once
|
|
// over the loop.
|
|
|
|
// We use this funky loop since predecessors are stored in a linked list but
|
|
// we want array like semantics.
|
|
unsigned BBCount = 0;
|
|
for (SILBasicBlock *Pred : Parent->getPreds()) {
|
|
// If BBCount is not BBIndex, continue.
|
|
if (BBCount < BBIndex) {
|
|
BBCount++;
|
|
continue;
|
|
}
|
|
|
|
TermInst *TI = Pred->getTerminator();
|
|
|
|
if (auto *BI = dyn_cast<BranchInst>(TI))
|
|
return BI->getArg(Index);
|
|
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(TI))
|
|
return CBI->getArgForDestBB(Parent, this);
|
|
|
|
if (auto *CCBI = dyn_cast<CheckedCastBranchInst>(TI))
|
|
return CCBI->getOperand();
|
|
|
|
if (auto *SWEI = dyn_cast<SwitchEnumInst>(TI))
|
|
return SWEI->getOperand();
|
|
|
|
// Return an empty SILValue since we ran into something we were unable to
|
|
// understand.
|
|
return SILValue();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue SILArgument::getIncomingValue(SILBasicBlock *BB) {
|
|
SILBasicBlock *Parent = getParent();
|
|
|
|
assert(!Parent->pred_empty() && "Passed in non-predecessor BB!");
|
|
unsigned Index = getIndex();
|
|
|
|
// We could do an early check if the size of the pred list is <= BBIndex, but
|
|
// that would involve walking the linked list anyways, so we just iterate once
|
|
// over the loop.
|
|
|
|
// We use this funky loop since predecessors are stored in a linked list but
|
|
// we want array like semantics.
|
|
for (SILBasicBlock *Pred : Parent->getPreds()) {
|
|
// If BBCount is not BBIndex, continue.
|
|
if (Pred != BB)
|
|
continue;
|
|
|
|
TermInst *TI = Pred->getTerminator();
|
|
|
|
if (auto *BI = dyn_cast<BranchInst>(TI))
|
|
return BI->getArg(Index);
|
|
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(TI))
|
|
return CBI->getArgForDestBB(Parent, this);
|
|
|
|
if (auto *CCBI = dyn_cast<CheckedCastBranchInst>(TI))
|
|
return CCBI->getOperand();
|
|
|
|
if (auto *SWEI = dyn_cast<SwitchEnumInst>(TI))
|
|
return SWEI->getOperand();
|
|
|
|
// Return an empty SILValue since we ran into something we were unable to
|
|
// understand.
|
|
return SILValue();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
bool SILArgument::isSelf() const {
|
|
// First make sure that we are actually a function argument. We use an assert
|
|
// boolean return here since in release builds we want to conservatively
|
|
// return false and in debug builds assert since this is a logic error.
|
|
bool isArg = isFunctionArg();
|
|
assert(isArg && "Only function arguments can be self");
|
|
if (!isArg)
|
|
return false;
|
|
|
|
// Return true if we are the last argument of our BB and that our parent
|
|
// function has a call signature with self.
|
|
return getFunction()->hasSelfArgument() &&
|
|
getParent()->getBBArgs().back() == this;
|
|
}
|