Skip to content

Add AccessedStorageAnalysis. #16003

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 1 commit into from
May 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions include/swift/SIL/MemAccessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ namespace swift {

// stripAddressAccess() is declared in InstructionUtils.h.

inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
}

/// Represents the identity of a stored class property as a combination
/// of a base and a single projection. Eventually the goal is to make this
/// more precise and consider, casts, etc.
Expand Down Expand Up @@ -88,6 +92,7 @@ class AccessedStorage {
enum Kind {
Box, Stack, Global, Class, Argument, Nested, Unidentified
};
static const char *getKindName(Kind k);

/// If the given address source is an identified access base, return the kind
/// of access base. Otherwise, return Unidentified.
Expand All @@ -108,6 +113,9 @@ class AccessedStorage {

AccessedStorage(SILValue base, Kind kind);

AccessedStorage(SILValue object, Projection projection)
: kind(Class), objProj(object, projection) {}

// Return true if this is a valid storage location.
operator bool() const {
return kind != Unidentified || value;
Expand Down Expand Up @@ -186,6 +194,9 @@ class AccessedStorage {
/// determined. Otherwise returns null. For diagnostics and checking via the
/// ValueDecl if we are processing a `let` variable.
const ValueDecl *getDecl(SILFunction *F) const;

void print(raw_ostream &os) const;
void dump() const;
};
} // end namespace swift

Expand Down Expand Up @@ -283,6 +294,10 @@ AccessedStorage findAccessedStorageOrigin(SILValue sourceAddr);
/// a begin_access marker. To determine whether to emit begin_access:
/// storage = findAccessedStorage(address)
/// needsAccessMarker = storage && isPossibleFormalAccessBase(storage)
///
/// Warning: This is only valid for SIL with well-formed accessed. For example,
/// it will not handle address-type phis. Optimization passes after
/// DiagnoseStaticExclusivity may violate these assumptions.
bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F);

/// Visit each address accessed by the given memory operation.
Expand Down
6 changes: 4 additions & 2 deletions include/swift/SIL/Projection.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ class Projection {
createAggFromFirstLevelProjections(SILBuilder &B, SILLocation Loc,
SILType BaseType,
llvm::SmallVectorImpl<SILValue> &Values);

void print(raw_ostream &os, SILType baseType) const;
private:
/// Convenience method for getting the raw underlying index as a pointer.
TypeBase *getPointer() const {
Expand Down Expand Up @@ -685,8 +687,8 @@ class ProjectionPath {

void verify(SILModule &M);

raw_ostream &print(raw_ostream &OS, SILModule &M);
void dump(SILModule &M);
raw_ostream &print(raw_ostream &OS, SILModule &M) const;
void dump(SILModule &M) const;
};

/// Returns the hashcode for the new projection path.
Expand Down
163 changes: 163 additions & 0 deletions include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//===--- AccessedStorageAnalysis.h - Accessed Storage Analysis --*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements an interprocedural analysis pass that summarizes the
// dynamically enforced formal accesses within a function. These summaries are
// used by AccessEnforcementOpts to locally fold access scopes and remove
// dynamic checks based on whole module analysis.
//
// Note: This can be easily augmented to simultaneously compute
// FunctionSideEffects by adding FunctionSideEffects as a member of
// FunctionAccessedStorage. However, currently, the only use of
// FunctionAccessedStorage is local to summarizeFunction().
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_
#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_

#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"

namespace swift {

/// Information about a formal access within a function pertaining to a
/// particular AccessedStorage location.
struct StorageAccessInfo {
SILAccessKind accessKind;
bool noNestedConflict = false;

StorageAccessInfo() {}

template <typename B>
explicit StorageAccessInfo(B *beginAccess)
: accessKind(beginAccess->getAccessKind()),
noNestedConflict(beginAccess->hasNoNestedConflict()) {}

bool mergeFrom(const StorageAccessInfo &RHS);
};

/// The per-function result of AccessedStorageAnalysis.
///
/// Maps each unique AccessedStorage location to StorageAccessInfo.
///
/// Any unidentified accesses are summarized as a single unidentifiedAccess
/// property.
class FunctionAccessedStorage {
using AccessedStorageMap =
llvm::SmallDenseMap<AccessedStorage, StorageAccessInfo, 8>;

AccessedStorageMap storageAccessMap;
Optional<SILAccessKind> unidentifiedAccess;

public:
FunctionAccessedStorage() {}

void clear() {
storageAccessMap.clear();
unidentifiedAccess = None;
}

/// Sets the most conservative effects, if we don't know anything about the
/// function.
void setWorstEffects() {
storageAccessMap.clear();
unidentifiedAccess = SILAccessKind::Modify;
}

/// Summarize the given function's effects using this FunctionAccessedStorage
/// object.
//
// Return true if the function's' effects have been fully summarized without
// visiting it's body.
bool summarizeFunction(SILFunction *F);

/// Summarize the callee side effects of a call instruction using this
/// FunctionAccessedStorage object without analyzing the callee function
/// bodies or scheduling the callees for bottom-up propagation.
///
/// The side effects are represented from the callee's perspective. Parameter
/// effects are not translated into information on the caller's argument, and
/// local effects are not dropped.
///
/// Return true if this call-site's effects are summarized without visiting
/// the callee.
///
/// TODO: Summarize ArraySemanticsCall accesses.
bool summarizeCall(FullApplySite fullApply) {
assert(storageAccessMap.empty() && "expected uninitialized results.");
return false;
}

/// Merge effects directly from \p RHS.
bool mergeFrom(const FunctionAccessedStorage &RHS);

/// Merge the effects represented in calleeAccess into this
/// FunctionAccessedStorage object. calleeAccess must correspond to at least
/// one callee at the apply site `fullApply`. Merging drops any local effects,
/// and translates parameter effects into effects on the caller-side
/// arguments.
///
/// The full caller-side effects at a call site can be obtained with
/// AccessedStorageAnalysis::getCallSiteEffects().
bool mergeFromApply(const FunctionAccessedStorage &calleeAccess,
FullApplySite fullApply);

/// Analyze the side-effects of a single SIL instruction \p I.
/// Visited callees are added to \p BottomUpOrder until \p RecursionDepth
/// reaches MaxRecursionDepth.
void analyzeInstruction(SILInstruction *I);

/// Does any of the accesses represented by this FunctionAccessedStorage
/// object conflict with the given access kind and storage.
bool mayConflictWith(SILAccessKind otherAccessKind,
const AccessedStorage &otherStorage);

void print(raw_ostream &os) const;
void dump() const;

protected:
bool updateUnidentifiedAccess(SILAccessKind accessKind);

bool mergeAccesses(
const FunctionAccessedStorage &RHS,
std::function<AccessedStorage(const AccessedStorage &)> transformStorage);

template <typename B> void visitBeginAccess(B *beginAccess);
};

/// Summarizes the dynamic accesses performed within a function and its
/// callees.
///
/// When summarizing multiple accesses, a Read access indicates that all
/// accesses are read only. A Write access indicates potential reads and writes.
///
/// Unidentified accesses are not recorded individually. Rather, the function is
/// marked as potentially executing unidentified reads or writes. An incomplete
/// function, without a known callee set, is considered to have unidentified
/// writes.
class AccessedStorageAnalysis
: public GenericFunctionEffectAnalysis<FunctionAccessedStorage> {
public:
AccessedStorageAnalysis()
: GenericFunctionEffectAnalysis<FunctionAccessedStorage>(
AnalysisKind::AccessedStorage) {}

static bool classof(const SILAnalysis *S) {
return S->getKind() == AnalysisKind::AccessedStorage;
}
};

} // end namespace swift

#endif // SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_
1 change: 1 addition & 0 deletions include/swift/SILOptimizer/Analysis/Analysis.def
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#endif

ANALYSIS(AccessSummary)
ANALYSIS(AccessedStorage)
ANALYSIS(Alias)
ANALYSIS(BasicCallee)
ANALYSIS(Caller)
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 @@ -58,6 +58,8 @@ PASS(AccessEnforcementSelection, "access-enforcement-selection",
"Access Enforcement Selection")
PASS(AccessSummaryDumper, "access-summary-dump",
"Dump Address Parameter Access Summary")
PASS(AccessedStorageDumper, "accessed-storage-dump",
"Dump Accessed Storage Summary")
PASS(AccessMarkerElimination, "access-marker-elim",
"Access Marker Elimination.")
PASS(AddressLowering, "address-lowering",
Expand Down
54 changes: 51 additions & 3 deletions lib/SIL/MemAccessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,53 @@ const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const {
}
}

// An address base is a block argument. Verify that it is actually a box
// projected from a switch_enum.
const char *AccessedStorage::getKindName(AccessedStorage::Kind k) {
switch (k) {
case Box:
return "Box";
case Stack:
return "Stack";
case Nested:
return "Nested";
case Unidentified:
return "Unidentified";
case Argument:
return "Argument";
case Global:
return "Global";
case Class:
return "Class";
}
}

void AccessedStorage::print(raw_ostream &os) const {
os << getKindName(kind) << " ";
switch (kind) {
case Box:
case Stack:
case Nested:
case Unidentified:
os << value;
break;
case Argument:
os << "index: " << paramIndex << "\n";
break;
case Global:
os << *global;
break;
case Class:
os << objProj.getObject() << " ";
objProj.getProjection().print(os, objProj.getObject()->getType());
os << "\n";
}
}

void AccessedStorage::dump() const { print(llvm::dbgs()); }

// Given an address base is a block argument, verify that it is actually a box
// projected from a switch_enum. This is a valid pattern at any SIL stage
// resulting in a block-type phi. In later SIL stages, the optimizer may form
// address-type phis, causing this assert if called on those cases.
static void checkSwitchEnumBlockArg(SILPHIArgument *arg) {
assert(!arg->getType().isAddress());
SILBasicBlock *Pred = arg->getParent()->getSinglePredecessorBlock();
Expand Down Expand Up @@ -135,6 +180,9 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
// A block argument may be a box value projected out of
// switch_enum. Address-type block arguments are not allowed.
case ValueKind::SILPHIArgument:
if (address->getType().isAddress())
return AccessedStorage();

checkSwitchEnumBlockArg(cast<SILPHIArgument>(address));
return AccessedStorage(address, AccessedStorage::Unidentified);

Expand Down Expand Up @@ -279,7 +327,7 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
if (isScratchBuffer(storage.getValue()))
return false;
}
// Additional checks that apply to anything that may fal through.
// Additional checks that apply to anything that may fall through.

// Immutable values are only accessed for initialization.
if (isLetAccess(storage, F))
Expand Down
Loading