Skip to content

Add an AccessPath abstraction and formalize memory access #34126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
462 changes: 462 additions & 0 deletions docs/SILProgrammersManual.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H
#define SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H

#include "swift/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
Expand All @@ -21,15 +22,18 @@ namespace swift {

// Trie node representing a sequence of unsigned integer indices.
class IndexTrieNode {
static const unsigned RootIdx = ~0U;
unsigned Index;
public:
static const int RootIndex = std::numeric_limits<int>::min();

private:
int Index;
llvm::SmallVector<IndexTrieNode*, 8> Children;
IndexTrieNode *Parent;

public:
IndexTrieNode(): Index(RootIdx), Parent(nullptr) {}
IndexTrieNode() : Index(RootIndex), Parent(nullptr) {}

explicit IndexTrieNode(unsigned V, IndexTrieNode *P): Index(V), Parent(P) {}
explicit IndexTrieNode(int V, IndexTrieNode *P) : Index(V), Parent(P) {}

IndexTrieNode(IndexTrieNode &) =delete;
IndexTrieNode &operator=(const IndexTrieNode&) =delete;
Expand All @@ -39,19 +43,18 @@ class IndexTrieNode {
delete N;
}

bool isRoot() const { return Index == RootIdx; }
bool isRoot() const { return Index == RootIndex; }

bool isLeaf() const { return Children.empty(); }

unsigned getIndex() const { return Index; }
int getIndex() const { return Index; }

IndexTrieNode *getChild(unsigned Idx) {
assert(Idx != RootIdx);
IndexTrieNode *getChild(int Idx) {
assert(Idx != RootIndex);

auto I = std::lower_bound(Children.begin(), Children.end(), Idx,
[](IndexTrieNode *a, unsigned i) {
return a->Index < i;
});
auto I =
std::lower_bound(Children.begin(), Children.end(), Idx,
[](IndexTrieNode *a, int i) { return a->Index < i; });
if (I != Children.end() && (*I)->Index == Idx)
return *I;
auto *N = new IndexTrieNode(Idx, this);
Expand Down
1,026 changes: 777 additions & 249 deletions include/swift/SIL/MemAccessUtils.h

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions include/swift/SIL/PatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,16 @@ using BuiltinApplyTy = typename Apply_match<BuiltinValueKind, Tys...>::Ty;
// Define matchers for most of builtin instructions.
#include "swift/AST/Builtins.def"

#undef BUILTIN_UNARY_OP_MATCH_WITH_ARG_MATCHER
#undef BUILTIN_BINARY_OP_MATCH_WITH_ARG_MATCHER
#undef BUILTIN_VARARGS_OP_MATCH_WITH_ARG_MATCHER
#undef BUILTIN_CAST_OPERATION
#undef BUILTIN_CAST_OR_BITCAST_OPERATION
#undef BUILTIN_BINARY_OPERATION_ALL
#undef BUILTIN_BINARY_PREDICATE
#undef BUILTIN_MISC_OPERATION
#undef BUILTIN

//===
// Convenience compound builtin instructions matchers that succeed
// if any of the sub-matchers succeed.
Expand Down
39 changes: 18 additions & 21 deletions include/swift/SIL/Projection.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ inline bool isStrictSubSeqRelation(SubSeqRelation_t Seq) {

/// Extract an integer index from a SILValue.
///
/// Return true if IndexVal is a constant index representable as unsigned
/// Return true if IndexVal is a constant index representable as an
/// int. We do not support symbolic projections yet.
bool getIntegerIndex(SILValue IndexVal, unsigned &IndexConst);
bool getIntegerIndex(SILValue IndexVal, int &IndexConst);

/// The kind of projection that we are representing.
///
Expand Down Expand Up @@ -136,11 +136,18 @@ static inline bool isCastProjectionKind(ProjectionKind Kind) {
/// that immediately contains it.
///
/// This lightweight utility maps a SIL address projection to an index.
///
/// project_box does not have a projection index. At the SIL level, the box
/// storage is considered part of the same object as the. The box projection is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "... as the."

/// does not affect access path so that box projections can occur on distinct
/// phi paths in the address def-use chain.
struct ProjectionIndex {
static constexpr int TailIndex = std::numeric_limits<int>::max();

SILValue Aggregate;
unsigned Index;
int Index = std::numeric_limits<int>::min();

explicit ProjectionIndex(SILValue V) : Index(~0U) {
explicit ProjectionIndex(SILValue V) {
switch (V->getKind()) {
default:
break;
Expand All @@ -163,16 +170,9 @@ struct ProjectionIndex {
break;
}
case ValueKind::RefTailAddrInst: {
RefTailAddrInst *REA = cast<RefTailAddrInst>(V);
Index = 0;
Aggregate = REA->getOperand();
break;
}
case ValueKind::ProjectBoxInst: {
ProjectBoxInst *PBI = cast<ProjectBoxInst>(V);
// A box has only a single payload.
Index = 0;
Aggregate = PBI->getOperand();
RefTailAddrInst *RTA = cast<RefTailAddrInst>(V);
Index = TailIndex;
Aggregate = RTA->getOperand();
break;
}
case ValueKind::TupleElementAddrInst: {
Expand Down Expand Up @@ -233,8 +233,7 @@ class Projection {
: Projection(dyn_cast<SingleValueInstruction>(I)) {}
explicit Projection(SingleValueInstruction *I);

Projection(ProjectionKind Kind, unsigned NewIndex)
: Value(Kind, NewIndex) {}
Projection(ProjectionKind Kind, int NewIndex) : Value(Kind, NewIndex) {}

Projection(ProjectionKind Kind, TypeBase *Ptr)
: Value(Kind, Ptr) {}
Expand All @@ -252,10 +251,8 @@ class Projection {

/// Convenience method for getting the underlying index. Assumes that this
/// projection is valid. Otherwise it asserts.
unsigned getIndex() const {
return Value.getIndex();
}

int getIndex() const { return Value.getIndex(); }

unsigned getHash() const { return (unsigned)Value.getStorage(); }

/// Determine if I is a value projection instruction whose corresponding
Expand Down Expand Up @@ -359,7 +356,7 @@ class Projection {
return nullptr;
case ValueKind::IndexAddrInst: {
auto *i = cast<IndexAddrInst>(v);
unsigned scalar;
int scalar;
if (getIntegerIndex(i->getIndex(), scalar))
return i;
return nullptr;
Expand Down
8 changes: 3 additions & 5 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5822,13 +5822,11 @@ class TupleElementAddrInst
/// object, including properties declared in a superclass.
unsigned getFieldIndex(NominalTypeDecl *decl, VarDecl *property);

/// Get the property for a struct or class by its unique index.
/// Get the property for a struct or class by its unique index, or nullptr if
/// the index does not match a property declared in this struct or class or
/// one its superclasses.
///
/// Precondition: \p decl must be a non-resilient struct or class.
///
/// Precondition: \p index must be the index of a stored property
/// (as returned by getFieldIndex()) which is declared
/// in \p decl, not in a superclass.
VarDecl *getIndexedField(NominalTypeDecl *decl, unsigned index);

/// A common base for instructions that require a cached field index.
Expand Down
7 changes: 7 additions & 0 deletions include/swift/SIL/SILModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/Builtins.h"
#include "swift/AST/SILLayout.h"
#include "swift/AST/SILOptions.h"
#include "swift/Basic/IndexTrie.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/ProfileCounter.h"
#include "swift/Basic/Range.h"
Expand Down Expand Up @@ -256,6 +257,10 @@ class SILModule {
/// The indexed profile data to be used for PGO, or nullptr.
std::unique_ptr<llvm::IndexedInstrProfReader> PGOReader;

/// A trie of integer indices that gives pointer identity to a path of
/// projections, shared between all functions in the module.
std::unique_ptr<IndexTrieNode> indexTrieRoot;

/// The options passed into this SILModule.
const SILOptions &Options;

Expand Down Expand Up @@ -655,6 +660,8 @@ class SILModule {
PGOReader = std::move(IPR);
}

IndexTrieNode *getIndexTrieRoot() { return indexTrieRoot.get(); }

/// Can value operations (copies and destroys) on the given lowered type
/// be performed in this module?
bool isTypeABIAccessible(SILType type,
Expand Down
15 changes: 3 additions & 12 deletions include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_
#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_

#include "swift/Basic/IndexTrie.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h"
#include "swift/SILOptimizer/Utils/IndexTrie.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"

Expand Down Expand Up @@ -173,22 +173,13 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis {

llvm::SpecificBumpPtrAllocator<FunctionInfo> Allocator;

/// A trie of integer indices that gives pointer identity to a path of
/// projections. This is shared between all functions in the module.
std::unique_ptr<IndexTrieNode> SubPathTrie;

public:
AccessSummaryAnalysis() : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) {
SubPathTrie.reset(new IndexTrieNode());
}
AccessSummaryAnalysis()
: BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) {}

/// Returns a summary of the accesses performed by the given function.
const FunctionSummary &getOrCreateSummary(SILFunction *Fn);

IndexTrieNode *getSubPathTrieRoot() {
return SubPathTrie.get();
}

/// Returns an IndexTrieNode that represents the single subpath accessed from
/// BAI or the root if no such node exists.
const IndexTrieNode *findSubPathAccessed(BeginAccessInst *BAI);
Expand Down
25 changes: 20 additions & 5 deletions include/swift/SILOptimizer/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ bool pointsToLocalObject(SILValue V);
/// Returns true if \p V is a uniquely identified address or reference. Two
/// uniquely identified pointers with distinct roots cannot alias. However, a
/// uniquely identified pointer may alias with unidentified pointers. For
/// example, the uniquely identified pointer may escape to a call that returns an
/// alias of that pointer.
/// example, the uniquely identified pointer may escape to a call that returns
/// an alias of that pointer.
///
/// It may be any of:
///
Expand All @@ -53,10 +53,25 @@ bool pointsToLocalObject(SILValue V);
///
/// - an address projection based on an exclusive argument with no levels of
/// indirection (e.g. ref_element_addr, project_box, etc.).
///
/// TODO: Fold this into the AccessedStorage API. pointsToLocalObject should be
/// performed by AccessedStorage::isUniquelyIdentified.
inline bool isUniquelyIdentified(SILValue V) {
return pointsToLocalObject(V)
|| (V->getType().isAddress()
&& isExclusiveArgument(getAccessedAddress(V)));
SILValue objectRef = V;
if (V->getType().isAddress()) {
auto storage = AccessedStorage::compute(V);
if (!storage)
return false;

if (storage.isUniquelyIdentified())
return true;

if (!storage.isObjectAccess())
return false;

objectRef = storage.getObject();
}
return pointsToLocalObject(objectRef);
}

enum class IsZeroKind {
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ PASS(AccessSummaryDumper, "access-summary-dump",
"Dump Address Parameter Access Summary")
PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump",
"Dump Accessed Storage Analysis Summaries")
PASS(AccessPathVerification, "access-path-verification",
"Verify Access Paths (and Accessed Storage)")
PASS(AccessedStorageDumper, "accessed-storage-dump",
"Dump Accessed Storage Information")
PASS(AccessMarkerElimination, "access-marker-elim",
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4125,7 +4125,7 @@ void IRGenSILFunction::visitRefTailAddrInst(RefTailAddrInst *i) {
}

static bool isInvariantAddress(SILValue v) {
SILValue accessedAddress = getAccessedAddress(v);
SILValue accessedAddress = getTypedAccessAddress(v);
if (auto *ptrRoot = dyn_cast<PointerToAddressInst>(accessedAddress)) {
return ptrRoot->isInvariant();
}
Expand Down
22 changes: 15 additions & 7 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1345,15 +1345,23 @@ unsigned swift::getFieldIndex(NominalTypeDecl *decl, VarDecl *field) {

/// Get the property for a struct or class by its unique index.
VarDecl *swift::getIndexedField(NominalTypeDecl *decl, unsigned index) {
if (auto *classDecl = dyn_cast<ClassDecl>(decl)) {
for (auto *superDecl = classDecl->getSuperclassDecl(); superDecl != nullptr;
superDecl = superDecl->getSuperclassDecl()) {
assert(index >= superDecl->getStoredProperties().size()
&& "field index cannot refer to a superclass field");
index -= superDecl->getStoredProperties().size();
if (auto *structDecl = dyn_cast<StructDecl>(decl)) {
return structDecl->getStoredProperties()[index];
}
auto *classDecl = cast<ClassDecl>(decl);
SmallVector<ClassDecl *, 3> superclasses;
for (auto *superDecl = classDecl; superDecl != nullptr;
superDecl = superDecl->getSuperclassDecl()) {
superclasses.push_back(superDecl);
}
std::reverse(superclasses.begin(), superclasses.end());
for (auto *superDecl : superclasses) {
if (index < superDecl->getStoredProperties().size()) {
return superDecl->getStoredProperties()[index];
}
index -= superDecl->getStoredProperties().size();
}
return decl->getStoredProperties()[index];
return nullptr;
}

unsigned FieldIndexCacheBase::cacheFieldIndex() {
Expand Down
3 changes: 2 additions & 1 deletion lib/SIL/IR/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ class SILModule::SerializationCallback final

SILModule::SILModule(llvm::PointerUnion<FileUnit *, ModuleDecl *> context,
Lowering::TypeConverter &TC, const SILOptions &Options)
: Stage(SILStage::Raw), Options(Options), serialized(false),
: Stage(SILStage::Raw), indexTrieRoot(new IndexTrieNode()),
Options(Options), serialized(false),
regDeserializationNotificationHandlerForNonTransparentFuncOME(false),
regDeserializationNotificationHandlerForAllFuncOME(false),
SerializeSILAction(), Types(TC) {
Expand Down
Loading