Files
swift-mirror/lib/SIL/Projection.cpp
Xin Tong 562f417ff9 Use address projection instead of projection when expanding projection paths
for a SILType in MemLocation.

We get a sequence of address projections when initializing a memory location, i.e.
trace back to the base and then call getAddrProjectionPath.

To keep it consistent, we use address projections when we expand the projection
tree for a specific SILType as well.

By doing so, to get the type of the MemLocation, we can get just the first entry
in the projectionpath and get its object type.

Swift SVN r32775
2015-10-20 17:07:26 +00:00

1294 lines
44 KiB
C++

//===--- Projection.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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-projection"
#include "swift/SIL/Projection.h"
#include "swift/Basic/NullablePtr.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/DebugUtils.h"
#include "llvm/ADT/None.h"
#include "llvm/Support/Debug.h"
using namespace swift;
//===----------------------------------------------------------------------===//
// Projection Static Asserts
//===----------------------------------------------------------------------===//
/// These are just for performance and verification. If one needs to make
/// changes that cause the asserts the fire, please update them. The purpose is
/// to prevent these predicates from changing values by mistake.
static_assert(std::is_standard_layout<Projection>::value,
"Expected projection to be a standard layout type");
static_assert(sizeof(Projection) == ((sizeof(uintptr_t) * 2)
+ (sizeof (unsigned int) * 2)),
"Projection size changed");
//===----------------------------------------------------------------------===//
// Projection
//===----------------------------------------------------------------------===//
/// Returns true if we are accessing different fields.
static bool areProjectionsToDifferentFields(const Projection &P1,
const Projection &P2) {
// If operands have the same type and we are accessing different fields,
// returns true. Operand's type is not saved in Projection. Instead we check
// Decl's context.
if (!P1.isNominalKind() || !P2.isNominalKind())
return false;
return P1.getDecl()->getDeclContext() == P2.getDecl()->getDeclContext() &&
P1 != P2;
}
bool Projection::matchesValueProjection(SILInstruction *I) const {
llvm::Optional<Projection> P = Projection::valueProjectionForInstruction(I);
if (!P)
return false;
return *this == P.getValue();
}
llvm::Optional<Projection>
Projection::valueProjectionForInstruction(SILInstruction *I) {
switch (I->getKind()) {
case ValueKind::StructExtractInst:
assert(isValueProjection(I) && "isValueProjection out of sync");
return Projection(cast<StructExtractInst>(I));
case ValueKind::TupleExtractInst:
assert(isValueProjection(I) && "isValueProjection out of sync");
return Projection(cast<TupleExtractInst>(I));
case ValueKind::UncheckedEnumDataInst:
assert(isValueProjection(I) && "isValueProjection out of sync");
return Projection(cast<UncheckedEnumDataInst>(I));
default:
assert(!isValueProjection(I) && "isValueProjection out of sync");
return llvm::NoneType::None;
}
}
llvm::Optional<Projection>
Projection::addressProjectionForInstruction(SILInstruction *I) {
switch (I->getKind()) {
case ValueKind::StructElementAddrInst:
assert(isAddrProjection(I) && "isAddrProjection out of sync");
return Projection(cast<StructElementAddrInst>(I));
case ValueKind::TupleElementAddrInst:
assert(isAddrProjection(I) && "isAddrProjection out of sync");
return Projection(cast<TupleElementAddrInst>(I));
case ValueKind::IndexAddrInst:
assert(isAddrProjection(I) && "isAddrProjection out of sync");
return Projection(cast<IndexAddrInst>(I));
case ValueKind::RefElementAddrInst:
assert(isAddrProjection(I) && "isAddrProjection out of sync");
return Projection(cast<RefElementAddrInst>(I));
case ValueKind::UncheckedTakeEnumDataAddrInst:
assert(isAddrProjection(I) && "isAddrProjection out of sync");
return Projection(cast<UncheckedTakeEnumDataAddrInst>(I));
default:
assert(!isAddrProjection(I) && "isAddrProjection out of sync");
return llvm::NoneType::None;
}
}
llvm::Optional<Projection>
Projection::projectionForInstruction(SILInstruction *I) {
if (auto P = addressProjectionForInstruction(I))
return P;
return valueProjectionForInstruction(I);
}
bool
Projection::operator==(const Projection &Other) const {
if (isNominalKind() && Other.isNominalKind()) {
return Other.getDecl() == Decl;
} else {
return !Other.isNominalKind() && Index == Other.getIndex();
}
}
bool
Projection::operator<(Projection Other) const {
// If we have a nominal kind...
if (isNominalKind()) {
// And Other is also nominal...
if (Other.isNominalKind()) {
// Just compare the value decl pointers.
return getDeclIndex() < Other.getDeclIndex();
}
// Otherwise if Other is not nominal, return true since we always sort
// decls before indices.
return true;
} else {
// If this is not a nominal kind and Other is nominal, return
// false. Nominal kinds are always sorted before non-nominal kinds.
if (Other.isNominalKind())
return false;
// Otherwise, we are both index projections. Compare the indices.
return getIndex() < Other.getIndex();
}
}
static unsigned getIndexForValueDecl(ValueDecl *Decl) {
NominalTypeDecl *D = cast<NominalTypeDecl>(Decl->getDeclContext());
unsigned i = 0;
for (auto *V : D->getStoredProperties()) {
if (V == Decl)
return i;
++i;
}
llvm_unreachable("Failed to find Decl in its decl context?!");
}
/// We do not support symbolic projections yet, only 32-bit unsigned integers.
bool swift::getIntegerIndex(SILValue IndexVal, unsigned &IndexConst) {
if (auto *IndexLiteral = dyn_cast<IntegerLiteralInst>(IndexVal)) {
APInt ConstInt = IndexLiteral->getValue();
// IntegerLiterals are signed.
if (ConstInt.isIntN(32) && ConstInt.isNonNegative()) {
IndexConst = (unsigned)ConstInt.getSExtValue();
return true;
}
}
return false;
}
Projection::Projection(StructElementAddrInst *SEA)
: Type(SEA->getType()), Decl(SEA->getField()),
Index(getIndexForValueDecl(Decl)),
Kind(unsigned(ProjectionKind::Struct)) {}
Projection::Projection(TupleElementAddrInst *TEA)
: Type(TEA->getType()), Decl(nullptr), Index(TEA->getFieldNo()),
Kind(unsigned(ProjectionKind::Tuple)) {}
Projection::Projection(IndexAddrInst *IA)
: Type(IA->getType()), Decl(nullptr),
Kind(unsigned(ProjectionKind::Index)) {
bool valid = getIntegerIndex(IA->getIndex(), Index);
(void)valid;
assert(valid && "only index_addr taking integer literal is supported");
}
Projection::Projection(RefElementAddrInst *REA)
: Type(REA->getType()), Decl(REA->getField()),
Index(getIndexForValueDecl(Decl)), Kind(unsigned(ProjectionKind::Class)) {
}
/// UncheckedTakeEnumDataAddrInst always have an index of 0 since enums only
/// have one payload.
Projection::Projection(UncheckedTakeEnumDataAddrInst *UTEDAI)
: Type(UTEDAI->getType()), Decl(UTEDAI->getElement()), Index(0),
Kind(unsigned(ProjectionKind::Enum)) {}
Projection::Projection(StructExtractInst *SEI)
: Type(SEI->getType()), Decl(SEI->getField()),
Index(getIndexForValueDecl(Decl)),
Kind(unsigned(ProjectionKind::Struct)) {}
Projection::Projection(TupleExtractInst *TEI)
: Type(TEI->getType()), Decl(nullptr), Index(TEI->getFieldNo()),
Kind(unsigned(ProjectionKind::Tuple)) {}
/// UncheckedEnumData always have an index of 0 since enums only have one
/// payload.
Projection::Projection(UncheckedEnumDataInst *UEDAI)
: Type(UEDAI->getType()), Decl(UEDAI->getElement()), Index(0),
Kind(unsigned(ProjectionKind::Enum)) {}
NullablePtr<SILInstruction>
Projection::
createValueProjection(SILBuilder &B, SILLocation Loc, SILValue Base) const {
// Grab Base's type.
SILType BaseTy = Base.getType();
// If BaseTy is not an object type, bail.
if (!BaseTy.isObject())
return nullptr;
// If this projection is associated with an address type, convert its type to
// an object type.
//
// We explicitly do not convert Type to be an object if it is a local storage
// type since we want it to fail.
SILType Ty = Type.isAddress()? Type.getObjectType() : Type;
if (!Ty.isObject())
return nullptr;
// Ok, we now know that the type of Base and the type represented by the base
// of this projection match and that this projection can be represented as
// value. Create the instruction if we can. Otherwise, return nullptr.
switch (getKind()) {
case ProjectionKind::Struct:
return B.createStructExtract(Loc, Base, cast<VarDecl>(getDecl()));
case ProjectionKind::Tuple:
return B.createTupleExtract(Loc, Base, getIndex());
case ProjectionKind::Index:
return nullptr;
case ProjectionKind::Enum:
return B.createUncheckedEnumData(Loc, Base,
cast<EnumElementDecl>(getDecl()));
case ProjectionKind::Class:
return nullptr;
}
}
NullablePtr<SILInstruction>
Projection::
createAddrProjection(SILBuilder &B, SILLocation Loc, SILValue Base) const {
// Grab Base's type.
SILType BaseTy = Base.getType();
// If BaseTy is not an address type, bail.
if (!BaseTy.isAddress())
return nullptr;
// If this projection is associated with an object type, convert its type to
// an address type.
//
// *NOTE* We purposely do not handle local storage types here since we want to
// always fail in such a case. That is handled by checking that Ty is an
// address.
SILType Ty = Type.isObject()? Type.getAddressType() : Type;
if (!Ty.isAddress())
return nullptr;
// Ok, we now know that the type of Base and the type represented by the base
// of this projection match and that this projection can be represented as
// value. Create the instruction if we can. Otherwise, return nullptr.
switch (getKind()) {
case ProjectionKind::Struct:
return B.createStructElementAddr(Loc, Base, cast<VarDecl>(getDecl()));
case ProjectionKind::Tuple:
return B.createTupleElementAddr(Loc, Base, getIndex());
case ProjectionKind::Index: {
auto Ty = SILType::getBuiltinIntegerType(32, B.getASTContext());
auto *IntLiteral = B.createIntegerLiteral(Loc, Ty, getIndex());
return B.createIndexAddr(Loc, Base, IntLiteral);
}
case ProjectionKind::Enum:
return B.createUncheckedTakeEnumDataAddr(Loc, Base,
cast<EnumElementDecl>(getDecl()));
case ProjectionKind::Class:
return B.createRefElementAddr(Loc, Base, cast<VarDecl>(getDecl()));
}
}
SILValue Projection::getOperandForAggregate(SILInstruction *I) const {
switch (getKind()) {
case ProjectionKind::Struct:
if (isa<StructInst>(I))
return I->getOperand(getDeclIndex());
break;
case ProjectionKind::Tuple:
if (isa<TupleInst>(I))
return I->getOperand(getIndex());
break;
case ProjectionKind::Index:
break;
case ProjectionKind::Enum:
if (EnumInst *EI = dyn_cast<EnumInst>(I)) {
if (EI->getElement() == Decl) {
assert(EI->hasOperand() && "expected data operand");
return EI->getOperand();
}
}
break;
case ProjectionKind::Class:
// There is no SIL instruction to create a class by aggregating values.
break;
}
return SILValue();
}
void Projection::getFirstLevelAddrProjections(
SILType Ty, SILModule &Mod, llvm::SmallVectorImpl<Projection> &Out) {
if (auto *S = Ty.getStructOrBoundGenericStruct()) {
for (auto *V : S->getStoredProperties()) {
Out.push_back(Projection(ProjectionKind::Struct,
Ty.getFieldType(V, Mod).getAddressType(),
V, getIndexForValueDecl(V)));
}
return;
}
if (auto TT = Ty.getAs<TupleType>()) {
for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) {
Out.push_back(Projection(ProjectionKind::Tuple,
Ty.getTupleElementType(i).getAddressType(),
nullptr, i));
}
return;
}
if (auto *C = Ty.getClassOrBoundGenericClass()) {
for (auto *V : C->getStoredProperties()) {
Out.push_back(Projection(ProjectionKind::Class,
Ty.getFieldType(V, Mod).getAddressType(),
V, getIndexForValueDecl(V)));
}
return;
}
}
void Projection::getFirstLevelProjections(
SILType Ty, SILModule &Mod, llvm::SmallVectorImpl<Projection> &Out) {
if (auto *S = Ty.getStructOrBoundGenericStruct()) {
for (auto *V : S->getStoredProperties()) {
Out.push_back(Projection(ProjectionKind::Struct, Ty.getFieldType(V, Mod),
V, getIndexForValueDecl(V)));
}
return;
}
if (auto TT = Ty.getAs<TupleType>()) {
for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) {
Out.push_back(Projection(ProjectionKind::Tuple, Ty.getTupleElementType(i),
nullptr, i));
}
return;
}
if (auto *C = Ty.getClassOrBoundGenericClass()) {
for (auto *V : C->getStoredProperties()) {
Out.push_back(Projection(ProjectionKind::Class, Ty.getFieldType(V, Mod),
V, getIndexForValueDecl(V)));
}
return;
}
}
void Projection::getFirstLevelProjections(
SILValue V, SILModule &Mod, llvm::SmallVectorImpl<Projection> &Out) {
getFirstLevelProjections(V.getType(), Mod, Out);
}
NullablePtr<SILInstruction>
Projection::
createAggFromFirstLevelProjections(SILBuilder &B, SILLocation Loc,
SILType BaseType,
llvm::SmallVectorImpl<SILValue> &Values) {
if (BaseType.getStructOrBoundGenericStruct()) {
return B.createStruct(Loc, BaseType, Values);
}
if (BaseType.is<TupleType>()) {
return B.createTuple(Loc, BaseType, Values);
}
return nullptr;
}
//===----------------------------------------------------------------------===//
// Projection Path
//===----------------------------------------------------------------------===//
Optional<ProjectionPath>
ProjectionPath::getAddrProjectionPath(SILValue Start, SILValue End,
bool IgnoreCasts) {
// Do not inspect the body of structs with unreferenced types such as
// bitfields and unions.
if (Start.getType().aggregateHasUnreferenceableStorage() ||
End.getType().aggregateHasUnreferenceableStorage()) {
return llvm::NoneType::None;
}
ProjectionPath P;
// If Start == End, there is a "trivial" address projection in between the
// two. This is represented by returning an empty ProjectionPath.
if (Start == End)
return std::move(P);
// Otherwise see if End can be projection extracted from Start. First see if
// End is a projection at all.
auto Iter = End;
if (IgnoreCasts)
Iter = Iter.stripCasts();
bool NextAddrIsIndex = false;
while (Projection::isAddrProjection(Iter) && Start != Iter) {
Projection AP = *Projection::addressProjectionForValue(Iter);
P.Path.push_back(AP);
NextAddrIsIndex = (AP.getKind() == ProjectionKind::Index);
Iter = cast<SILInstruction>(*Iter).getOperand(0);
if (IgnoreCasts)
Iter = Iter.stripCasts();
}
// Return None if we have an empty projection list or if Start == Iter.
// If the next project is index_addr, then Start and End actually point to
// disjoint locations (the value at Start has an implicit index_addr #0).
if (P.empty() || Start != Iter || NextAddrIsIndex)
return llvm::NoneType::None;
// Otherwise, return P.
return std::move(P);
}
/// Returns true if the two paths have a non-empty symmetric difference.
///
/// This means that the two objects have the same base but access different
/// fields of the base object.
bool
ProjectionPath::
hasNonEmptySymmetricDifference(const ProjectionPath &RHS) const {
// If either the LHS or RHS is empty, there is no common base class. Return
// false.
if (empty() || RHS.empty())
return false;
// We reverse the projection path to scan from the common object.
auto LHSReverseIter = Path.rbegin();
auto RHSReverseIter = RHS.Path.rbegin();
// For each index i until min path size...
for (unsigned i = 0, e = std::min(size(), RHS.size()); i != e; ++i) {
// Grab the current projections.
const Projection &LHSProj = *LHSReverseIter;
const Projection &RHSProj = *RHSReverseIter;
// If we are accessing different fields of a common object, return
// true. The two projection paths must have a non-empty symmetric
// difference.
if (areProjectionsToDifferentFields(LHSProj, RHSProj)) {
DEBUG(llvm::dbgs() << " Path different at index: " << i << '\n');
return true;
}
// Otherwise, if the two projections equal exactly, they have no symmetric
// difference.
if (LHSProj == RHSProj)
return false;
// Continue if we are accessing the same field.
LHSReverseIter++;
RHSReverseIter++;
}
// We checked
return false;
}
/// TODO: Integrate has empty non-symmetric difference into here.
SubSeqRelation_t
ProjectionPath::
computeSubSeqRelation(const ProjectionPath &RHS) const {
// If either path is empty, we can not prove anything, return Unrelated.
if (empty() || RHS.empty())
return SubSeqRelation_t::Unrelated;
// We reverse the projection path to scan from the common object.
auto LHSReverseIter = rbegin();
auto RHSReverseIter = RHS.rbegin();
unsigned MinPathSize = std::min(size(), RHS.size());
// For each index i until min path size...
for (unsigned i = 0; i != MinPathSize; ++i) {
// Grab the current projections.
const Projection &LHSProj = *LHSReverseIter;
const Projection &RHSProj = *RHSReverseIter;
// If the two projections do not equal exactly, return Unrelated.
//
// TODO: If Index equals zero, then we know that the two lists have nothing
// in common and should return unrelated. If Index is greater than zero,
// then we know that the two projection paths have a common base but a
// non-empty symmetric difference. For now we just return Unrelated since I
// can not remember why I had the special check in the
// hasNonEmptySymmetricDifference code.
if (LHSProj != RHSProj)
return SubSeqRelation_t::Unrelated;
// Otherwise increment reverse iterators.
LHSReverseIter++;
RHSReverseIter++;
}
// Ok, we now know that one of the paths is a subsequence of the other. If
// both size() and RHS.size() equal then we know that the entire sequences
// equal.
if (size() == RHS.size())
return SubSeqRelation_t::Equal;
// If MinPathSize == size(), then we know that LHS is a strict subsequence of
// RHS.
if (MinPathSize == size())
return SubSeqRelation_t::LHSStrictSubSeqOfRHS;
// Otherwise, we know that MinPathSize must be RHS.size() and RHS must be a
// strict subsequence of LHS. Assert to check this and return.
assert(MinPathSize == RHS.size() &&
"Since LHS and RHS don't equal and size() != MinPathSize, RHS.size() "
"must equal MinPathSize");
return SubSeqRelation_t::RHSStrictSubSeqOfLHS;
}
bool ProjectionPath::
findMatchingValueProjectionPaths(SILInstruction *I,
SmallVectorImpl<SILInstruction *> &T) const {
// We maintain the head of our worklist so we can use our worklist as a queue
// and work in breadth first order. This makes sense since we want to process
// in levels so we can maintain one tail list and delete the tail list when we
// move to the next level.
unsigned WorkListHead = 0;
llvm::SmallVector<SILInstruction *, 8> WorkList;
WorkList.push_back(I);
// Start at the root of the list.
for (auto PI = rbegin(), PE = rend(); PI != PE; ++PI) {
// When we start a new level, clear T.
T.clear();
// If we have an empty worklist, return false. We have been unable to
// complete the list.
unsigned WorkListSize = WorkList.size();
if (WorkListHead == WorkListSize)
return false;
// Otherwise, process each instruction in the worklist.
for (; WorkListHead != WorkListSize; WorkListHead++) {
SILInstruction *Ext = WorkList[WorkListHead];
// If the current projection does not match I, continue and process the
// next instruction.
if (!PI->matchesValueProjection(Ext)) {
continue;
}
// Otherwise, we know that Ext matched this projection path and we should
// visit all of its uses and add Ext itself to our tail list.
T.push_back(Ext);
for (auto *Op : Ext->getUses()) {
WorkList.push_back(Op->getUser());
}
}
// Reset the worklist size.
WorkListSize = WorkList.size();
}
return true;
}
Optional<ProjectionPath>
ProjectionPath::subtractPaths(const ProjectionPath &LHS, const ProjectionPath &RHS) {
// If RHS is greater than or equal to LHS in size, RHS can not be a prefix of
// LHS. Return None.
unsigned RHSSize = RHS.size();
unsigned LHSSize = LHS.size();
if (RHSSize >= LHSSize)
return llvm::NoneType::None;
// First make sure that the prefix matches.
Optional<ProjectionPath> P = ProjectionPath();
for (unsigned i = 0; i < RHSSize; i++) {
if (LHS.Path[i] != RHS.Path[i]) {
P.reset();
return P;
}
}
// Add the rest of LHS to P and return P.
for (unsigned i = RHSSize, e = LHSSize; i != e; ++i) {
P->Path.push_back(LHS.Path[i]);
}
return P;
}
void
ProjectionPath::BreadthFirstEnumTypeProjection(SILType B, SILModule *Mod,
ProjectionPathList &Paths,
bool OnlyLeafNode) {
// Perform a BFS to expand the given type into projectionpath each of
// which contains 1 field from the type.
ProjectionPathList Worklist;
llvm::SmallVector<Projection, 8> Projections;
// Push an empty projection path to get started.
SILType Ty;
ProjectionPath P;
Worklist.push_back(std::move(P));
do {
// Get the next level projections based on current projection's type.
Optional<ProjectionPath> PP = Worklist.pop_back_val();
// Get the current type to process, the very first projection path will be
// empty.
Ty = PP.getValue().empty() ? B : PP.getValue().front().getType();
// Get the first level projection of the current type.
Projections.clear();
Projection::getFirstLevelAddrProjections(Ty, *Mod, Projections);
// Reached the end of the projection tree, this field can not be expanded
// anymore.
if (Projections.empty()) {
Paths.push_back(std::move(PP.getValue()));
continue;
}
// If this is a class type, we also have reached the end of the type
// tree for this type.
//
// We do not push its next level projection into the worklist,
// if we do that, we could run into an infinite loop, e.g.
//
// class SelfLoop {
// var p : SelfLoop
// }
//
// struct XYZ {
// var x : Int
// var y : SelfLoop
// }
//
// The worklist would never be empty in this case !.
//
if (Ty.getClassOrBoundGenericClass()) {
Paths.push_back(std::move(PP.getValue()));
continue;
}
// This is NOT a leaf node, keep the intermediate nodes as well.
if (!OnlyLeafNode)
Paths.push_back(std::move(PP.getValue()));
// Keep expanding the location.
for (auto &P : Projections) {
ProjectionPath X;
X.append(P);
X.append(PP.getValue());
Worklist.push_back(std::move(X));
}
// Keep iterating if the worklist is not empty.
} while (!Worklist.empty());
}
//===----------------------------------------------------------------------===//
// ProjectionTreeNode
//===----------------------------------------------------------------------===//
ProjectionTreeNode *
ProjectionTreeNode::getChildForProjection(ProjectionTree &Tree,
const Projection &P) {
for (unsigned Index : ChildProjections) {
ProjectionTreeNode *N = Tree.getNode(Index);
if (N->Proj && N->Proj.getValue() == P) {
return N;
}
}
return nullptr;
}
ProjectionTreeNode *
ProjectionTreeNode::getParent(ProjectionTree &Tree) {
if (!Parent)
return nullptr;
return Tree.getNode(Parent.getValue());
}
const ProjectionTreeNode *
ProjectionTreeNode::getParent(const ProjectionTree &Tree) const {
if (!Parent)
return nullptr;
return Tree.getNode(Parent.getValue());
}
NullablePtr<SILInstruction>
ProjectionTreeNode::
createProjection(SILBuilder &B, SILLocation Loc, SILValue Arg) const {
if (!Proj)
return nullptr;
return Proj->createProjection(B, Loc, Arg);
}
void
ProjectionTreeNode::
processUsersOfValue(ProjectionTree &Tree,
llvm::SmallVectorImpl<ValueNodePair> &Worklist,
SILValue Value) {
DEBUG(llvm::dbgs() << " Looking at Users:\n");
// For all uses of V...
for (Operand *Op : getNonDebugUses(Value)) {
// Grab the User of V.
SILInstruction *User = Op->getUser();
DEBUG(llvm::dbgs() << " " << *User);
// First try to create a Projection for User.
auto P = Projection::projectionForInstruction(User);
// If we fail to create a projection, add User as a user to this node and
// continue.
if (!P) {
DEBUG(llvm::dbgs() << " Failed to create projection. Adding "
"to non projection user!\n");
addNonProjectionUser(Op);
continue;
}
DEBUG(llvm::dbgs() << " Created projection.\n");
assert(User->getNumTypes() == 1 && "Projections should only have one use");
// Look up the Node for this projection add add {User, ChildNode} to the
// worklist.
//
// *NOTE* This means that we will process ChildNode multiple times
// potentially with different projection users.
if (auto *ChildNode = getChildForProjection(Tree, *P)) {
DEBUG(llvm::dbgs() << " Found child for projection: "
<< ChildNode->getType() << "\n");
SILValue V = SILValue(User);
Worklist.push_back({V, ChildNode});
} else {
DEBUG(llvm::dbgs() << " Did not find a child for projection!. "
"Adding to non projection user!\b");
// The only projection which we do not currently handle are enums since we
// may not know the correct case. This can be xtended in the future.
addNonProjectionUser(Op);
}
}
}
void
ProjectionTreeNode::
createChildrenForStruct(ProjectionTree &Tree,
llvm::SmallVectorImpl<ProjectionTreeNode *> &Worklist,
StructDecl *SD) {
SILModule &Mod = Tree.getModule();
unsigned ChildIndex = 0;
SILType Ty = getType();
for (VarDecl *VD : SD->getStoredProperties()) {
assert(Tree.getNode(Index) == this && "Node is not mapped to itself?");
SILType NodeTy = Ty.getFieldType(VD, Mod);
auto *Node = Tree.createChildForStruct(this, NodeTy, VD, ChildIndex++);
DEBUG(llvm::dbgs() << " Creating child for: " << NodeTy << "\n");
DEBUG(llvm::dbgs() << " Projection: " << Node->getProjection().getValue().getGeneralizedIndex() << "\n");
ChildProjections.push_back(Node->getIndex());
assert(getChildForProjection(Tree, Node->getProjection().getValue()) == Node &&
"Child not matched to its projection in parent!");
assert(Node->getParent(Tree) == this && "Parent of Child is not Parent?!");
Worklist.push_back(Node);
}
}
void
ProjectionTreeNode::
createChildrenForTuple(ProjectionTree &Tree,
llvm::SmallVectorImpl<ProjectionTreeNode *> &Worklist,
TupleType *TT) {
SILType Ty = getType();
for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) {
assert(Tree.getNode(Index) == this && "Node is not mapped to itself?");
SILType NodeTy = Ty.getTupleElementType(i);
auto *Node = Tree.createChildForTuple(this, NodeTy, i);
DEBUG(llvm::dbgs() << " Creating child for: " << NodeTy << "\n");
DEBUG(llvm::dbgs() << " Projection: " << Node->getProjection().getValue().getGeneralizedIndex() << "\n");
ChildProjections.push_back(Node->getIndex());
assert(getChildForProjection(Tree, Node->getProjection().getValue()) == Node &&
"Child not matched to its projection in parent!");
assert(Node->getParent(Tree) == this && "Parent of Child is not Parent?!");
Worklist.push_back(Node);
}
}
void
ProjectionTreeNode::
createChildren(ProjectionTree &Tree,
llvm::SmallVectorImpl<ProjectionTreeNode *> &Worklist) {
DEBUG(llvm::dbgs() << " Creating children for: " << getType() << "\n");
if (Initialized) {
DEBUG(llvm::dbgs() << " Already initialized! bailing!\n");
return;
}
Initialized = true;
SILType Ty = getType();
if (Ty.aggregateHasUnreferenceableStorage()) {
DEBUG(llvm::dbgs() << " Has unreferenced storage bailing!\n");
return;
}
if (auto *SD = Ty.getStructOrBoundGenericStruct()) {
DEBUG(llvm::dbgs() << " Found a struct!\n");
createChildrenForStruct(Tree, Worklist, SD);
return;
}
auto TT = Ty.getAs<TupleType>();
if (!TT) {
DEBUG(llvm::dbgs() << " Did not find a tuple or struct, "
"bailing!\n");
return;
}
DEBUG(llvm::dbgs() << " Found a tuple.");
createChildrenForTuple(Tree, Worklist, TT);
}
SILInstruction *
ProjectionTreeNode::
createAggregate(SILBuilder &B, SILLocation Loc, ArrayRef<SILValue> Args) const {
assert(Initialized && "Node must be initialized to create aggregates");
SILType Ty = getType();
if (Ty.getStructOrBoundGenericStruct()) {
return B.createStruct(Loc, Ty, Args);
}
if (Ty.getAs<TupleType>()) {
return B.createTuple(Loc, Ty, Args);
}
llvm_unreachable("Unhandled type");
}
//===----------------------------------------------------------------------===//
// ProjectionTree
//===----------------------------------------------------------------------===//
ProjectionTree::ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &BPA,
SILType BaseTy) : Mod(Mod), Allocator(BPA) {
DEBUG(llvm::dbgs() << "Constructing Projection Tree For : " << BaseTy);
// Create the root node of the tree with our base type.
createRoot(BaseTy);
// Initialize the worklist with the root node.
llvm::SmallVector<ProjectionTreeNode *, 8> Worklist;
Worklist.push_back(getRoot());
// Then until the worklist is empty...
while (!Worklist.empty()) {
DEBUG(llvm::dbgs() << "Current Worklist:\n");
DEBUG(for (auto *N : Worklist) {
llvm::dbgs() << " " << N->getType() << "\n";
});
// Pop off the top of the list.
ProjectionTreeNode *Node = Worklist.pop_back_val();
DEBUG(llvm::dbgs() << "Visiting: " << Node->getType() << "\n");
// Initialize the worklist and its children, adding them to the worklist as
// we create them.
Node->createChildren(*this, Worklist);
}
}
void
ProjectionTree::computeUsesAndLiveness(SILValue Base) {
// Propagate liveness and users through the tree.
llvm::SmallVector<ProjectionTreeNode::ValueNodePair, 32> UseWorklist;
UseWorklist.push_back({Base, getRoot()});
// Then until the worklist is empty...
while (!UseWorklist.empty()) {
DEBUG(llvm::dbgs() << "Current Worklist:\n");
DEBUG(for (auto &T : UseWorklist) {
llvm::dbgs() << " Type: " << T.second->getType() << "; Value: ";
if (T.first) {
llvm::dbgs() << T.first;
} else {
llvm::dbgs() << "<null>\n";
}
});
SILValue Value;
ProjectionTreeNode *Node;
// Pop off the top type, value, and node.
std::tie(Value, Node) = UseWorklist.pop_back_val();
DEBUG(llvm::dbgs() << "Visiting: " << Node->getType() << "\n");
// If Value is not null, collate all users of Value the appropriate child
// nodes and add such items to the worklist.
if (Value) {
Node->processUsersOfValue(*this, UseWorklist, Value);
}
// If this node is live due to a non projection user, propagate down its
// liveness to its children and its children with an empty value to the
// worklist so we propagate liveness down to any further descendents.
if (Node->IsLive) {
DEBUG(llvm::dbgs() << "Node Is Live. Marking Children Live!\n");
for (unsigned ChildIdx : Node->ChildProjections) {
ProjectionTreeNode *Child = getNode(ChildIdx);
Child->IsLive = true;
DEBUG(llvm::dbgs() << " Marking child live: " << Child->getType() << "\n");
UseWorklist.push_back({SILValue(), Child});
}
}
}
// Then setup the leaf list by iterating through our Nodes looking for live
// leafs. We use a DFS order, always processing the left leafs first so that
// we match the order in which we will lay out arguments.
llvm::SmallVector<ProjectionTreeNode *, 8> Worklist;
Worklist.push_back(getRoot());
while (!Worklist.empty()) {
ProjectionTreeNode *Node = Worklist.pop_back_val();
// If node is not a leaf, add its children to the worklist and continue.
if (!Node->ChildProjections.empty()) {
for (unsigned ChildIdx : reversed(Node->ChildProjections)) {
Worklist.push_back(getNode(ChildIdx));
}
continue;
}
// If the node is a leaf and is not a live, continue.
if (!Node->IsLive)
continue;
// Otherwise we have a live leaf, add its index to our LeafIndices list.
LeafIndices.push_back(Node->getIndex());
}
#ifndef NDEBUG
DEBUG(llvm::dbgs() << "Final Leafs: \n");
llvm::SmallVector<SILType, 8> LeafTypes;
getLeafTypes(LeafTypes);
for (SILType Leafs : LeafTypes) {
DEBUG(llvm::dbgs() << " " << Leafs << "\n");
}
#endif
}
void
ProjectionTree::
createTreeFromValue(SILBuilder &B, SILLocation Loc, SILValue NewBase,
llvm::SmallVectorImpl<SILValue> &Leafs) const {
DEBUG(llvm::dbgs() << "Recreating tree from value: " << NewBase);
using WorklistEntry =
std::tuple<const ProjectionTreeNode *, SILValue>;
llvm::SmallVector<WorklistEntry, 32> Worklist;
// Start our worklist with NewBase and Root.
Worklist.push_back(std::make_tuple(getRoot(), NewBase));
// Then until our worklist is clear...
while (Worklist.size()) {
// Pop off the top of the list.
const ProjectionTreeNode *Node = std::get<0>(Worklist.back());
SILValue V = std::get<1>(Worklist.back());
Worklist.pop_back();
DEBUG(llvm::dbgs() << "Visiting: " << V.getType() << ": " << V);
// If we have any children...
unsigned NumChildren = Node->ChildProjections.size();
if (NumChildren) {
DEBUG(llvm::dbgs() << " Not Leaf! Adding children to list.\n");
// Create projections for each one of them and the child node and
// projection to the worklist for processing.
for (unsigned ChildIdx : reversed(Node->ChildProjections)) {
const ProjectionTreeNode *ChildNode = getNode(ChildIdx);
SILInstruction *I = ChildNode->createProjection(B, Loc, V).get();
DEBUG(llvm::dbgs() << " Adding Child: " << I->getType(0) << ": " << *I);
Worklist.push_back(std::make_tuple(ChildNode, SILValue(I)));
}
} else {
// Otherwise, we have a leaf node. If the leaf node is not alive, do not
// add it to our leaf list.
if (!Node->IsLive)
continue;
// Otherwise add it to our leaf list.
DEBUG(llvm::dbgs() << " Is a Leaf! Adding to leaf list\n");
Leafs.push_back(V);
}
}
}
class ProjectionTreeNode::AggregateBuilder {
ProjectionTreeNode *Node;
SILBuilder &Builder;
SILLocation Loc;
llvm::SmallVector<SILValue, 8> Values;
// Did this aggregate already create an aggregate and thus is "invalidated".
bool Invalidated;
public:
AggregateBuilder(ProjectionTreeNode *N, SILBuilder &B, SILLocation L)
: Node(N), Builder(B), Loc(L), Values(), Invalidated(false) {
assert(N->Initialized && "N must be initialized since we are mapping Node "
"Children -> SILValues");
// Initialize the Values array with empty SILValues.
for (unsigned Child : N->ChildProjections) {
(void)Child;
Values.push_back(SILValue());
}
}
bool isInvalidated() const { return Invalidated; }
/// If all SILValues have been set, we are complete.
bool isComplete() const {
return std::all_of(Values.begin(), Values.end(), [](SILValue V) -> bool {
return V.getDef();
});
}
SILInstruction *createInstruction() const {
assert(isComplete() && "Can not create instruction until the aggregate is "
"complete");
assert(!Invalidated && "Must not be invalidated to create an instruction");
const_cast<AggregateBuilder *>(this)->Invalidated = true;
return Node->createAggregate(Builder, Loc, Values);
}
void setValueForChild(ProjectionTreeNode *Child, SILValue V) {
assert(!Invalidated && "Must not be invalidated to set value for child");
Values[Child->Proj.getValue().getGeneralizedIndex()] = V;
}
};
namespace {
using AggregateBuilder = ProjectionTreeNode::AggregateBuilder;
/// A wrapper around a MapVector with generalized operations on the map.
///
/// TODO: Replace this with a simple RPOT and use GraphUtils. Since we do not
/// look through enums or classes, in the current type system it should not be
/// possible to have a cycle implying that a RPOT should be fine.
class AggregateBuilderMap {
SILBuilder &Builder;
SILLocation Loc;
llvm::MapVector<ProjectionTreeNode *, AggregateBuilder> NodeBuilderMap;
public:
AggregateBuilderMap(SILBuilder &B, SILLocation Loc)
: Builder(B), Loc(Loc), NodeBuilderMap() {}
/// Get the AggregateBuilder associated with Node or if none is created,
/// create one for Node.
AggregateBuilder &getBuilder(ProjectionTreeNode *Node) {
auto I = NodeBuilderMap.find(Node);
if (I != NodeBuilderMap.end()) {
return I->second;
} else {
auto AggIt = NodeBuilderMap.insert({Node, AggregateBuilder(Node, Builder,
Loc)});
return AggIt.first->second;
}
}
/// Get the AggregateBuilder associated with Node. Assert on failure.
AggregateBuilder &get(ProjectionTreeNode *Node) {
auto It = NodeBuilderMap.find(Node);
assert(It != NodeBuilderMap.end() && "Every item in the worklist should have "
"an AggregateBuilder associated with it");
return It->second;
}
bool isComplete(ProjectionTreeNode *Node) {
return get(Node).isComplete();
}
bool isInvalidated(ProjectionTreeNode *Node) {
return get(Node).isInvalidated();
}
ProjectionTreeNode *
getNextValidNode(llvm::SmallVectorImpl<ProjectionTreeNode *> &Worklist,
bool CheckForDeadLock=false);
};
} // end anonymous namespace
ProjectionTreeNode *
AggregateBuilderMap::
getNextValidNode(llvm::SmallVectorImpl<ProjectionTreeNode *> &Worklist,
bool CheckForDeadLock) {
if (Worklist.empty())
return nullptr;
ProjectionTreeNode *Node = Worklist.back();
// If the Node is not complete, then we have reached a dead lock. This should never happen.
//
// TODO: Prove this and put the proof here.
if (CheckForDeadLock && !isComplete(Node)) {
llvm_unreachable("Algorithm Dead Locked!");
}
// This block of code, performs the pop back and also if the Node has been
// invalidated, skips until we find a non invalidated value.
while (isInvalidated(Node)) {
assert(isComplete(Node) && "Invalidated values must be complete");
// Pop Node off the back of the worklist.
Worklist.pop_back();
if (Worklist.empty())
return nullptr;
Node = Worklist.back();
}
return Node;
}
void
ProjectionTree::
replaceValueUsesWithLeafUses(SILBuilder &Builder, SILLocation Loc,
llvm::SmallVectorImpl<SILValue> &Leafs) {
assert(Leafs.size() == LeafIndices.size() && "Leafs and leaf indices must "
"equal in size.");
AggregateBuilderMap AggBuilderMap(Builder, Loc);
llvm::SmallVector<ProjectionTreeNode *, 8> Worklist;
DEBUG(llvm::dbgs() << "Replacing all uses in callee with leafs!\n");
// For each Leaf we have as input...
for (unsigned i = 0, e = Leafs.size(); i != e; ++i) {
SILValue Leaf = Leafs[i];
ProjectionTreeNode *Node = getNode(LeafIndices[i]);
DEBUG(llvm::dbgs() << " Visiting leaf: " << Leaf);
assert(Node->IsLive && "Unexpected dead node in LeafIndices!");
// Otherwise replace all uses at this level of the tree with uses of the
// Leaf value.
DEBUG(llvm::dbgs() << " Replacing operands with leaf!\n");
for (auto *Op : Node->NonProjUsers) {
DEBUG(llvm::dbgs() << " User: " << *Op->getUser());
Op->set(Leaf);
}
// Grab the parent of this node.
ProjectionTreeNode *Parent = Node->getParent(*this);
DEBUG(llvm::dbgs() << " Visiting parent of leaf: " <<
Parent->getType() << "\n");
// If the parent is dead, continue.
if (!Parent->IsLive) {
DEBUG(llvm::dbgs() << " Parent is dead... continuing.\n");
continue;
}
DEBUG(llvm::dbgs() << " Parent is alive! Adding to parent "
"builder\n");
// Otherwise either create an aggregate builder for the parent or reuse one
// that has already been created for it.
AggBuilderMap.getBuilder(Parent).setValueForChild(Node, Leaf);
DEBUG(llvm::dbgs() << " Is parent complete: "
<< (AggBuilderMap.isComplete(Parent)? "yes" : "no") << "\n");
// Finally add the parent to the worklist.
Worklist.push_back(Parent);
}
// A utility array to add new Nodes to the list so we maintain while
// processing the current worklist we maintain only completed items at the end
// of the list.
llvm::SmallVector<ProjectionTreeNode *, 8> NewNodes;
DEBUG(llvm::dbgs() << "Processing worklist!\n");
// Then until we have no work left...
while (!Worklist.empty()) {
// Sort the worklist so that complete items are first.
//
// TODO: Just change this into a partition method. Should be significantly
// faster.
std::sort(Worklist.begin(), Worklist.end(),
[&AggBuilderMap](ProjectionTreeNode *N1,
ProjectionTreeNode *N2) -> bool {
bool IsComplete1 = AggBuilderMap.isComplete(N1);
bool IsComplete2 = AggBuilderMap.isComplete(N2);
// Sort N1 after N2 if N1 is complete and N2 is not. This puts
// complete items at the end of our list.
return unsigned(IsComplete1) < unsigned(IsComplete2);
});
DEBUG(llvm::dbgs() << " Current Worklist:\n");
#ifndef NDEBUG
for (auto *_N : Worklist) {
DEBUG(llvm::dbgs() << " Type: " << _N->getType()
<< "; Complete: "
<< (AggBuilderMap.isComplete(_N)? "yes" : "no")
<< "; Invalidated: "
<< (AggBuilderMap.isInvalidated(_N)? "yes" : "no") << "\n");
}
#endif
// Find the first non invalidated node. If we have all invalidated nodes,
// this will return nullptr.
ProjectionTreeNode *Node = AggBuilderMap.getNextValidNode(Worklist, true);
// Then until we find a node that is not complete...
while (Node && AggBuilderMap.isComplete(Node)) {
// Create the aggregate for the current complete Node we are processing...
SILValue Agg = AggBuilderMap.get(Node).createInstruction();
// Replace all uses at this level of the tree with uses of the newly
// constructed aggregate.
for (auto *Op : Node->NonProjUsers) {
Op->set(Agg);
}
// If this node has a parent and that parent is alive...
ProjectionTreeNode *Parent = Node->getParentOrNull(*this);
if (Parent && Parent->IsLive) {
// Create or lookup the node builder for the parent and associate the
// newly created aggregate with this node.
AggBuilderMap.getBuilder(Parent).setValueForChild(Node, SILValue(Agg));
// Then add the parent to NewNodes to be added to our list.
NewNodes.push_back(Parent);
}
// Grab the next non-invalidated node for the next iteration. If we had
// all invalidated nodes, this will return nullptr.
Node = AggBuilderMap.getNextValidNode(Worklist);
}
// Copy NewNodes onto the back of our Worklist now that we have finished
// this iteration.
std::copy(NewNodes.begin(), NewNodes.end(), std::back_inserter(Worklist));
NewNodes.clear();
}
}