Skip to content

[4.0] [Exclusivity] Relax intra-procedural diagnostics on separate stored structs #10360

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
16 changes: 8 additions & 8 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,24 @@ NOTE(previous_inout_alias,none,
"previous aliasing argument", ())

WARNING(exclusivity_access_required_swift3,none,
"simultaneous accesses to %0 %1, but "
"%select{initialization|read|modification|deinitialization}2 requires "
"overlapping accesses to %0, but "
"%select{initialization|read|modification|deinitialization}1 requires "
"exclusive access; consider copying to a local variable",
(DescriptiveDeclKind, Identifier, unsigned))
(StringRef, unsigned))

ERROR(exclusivity_access_required,none,
"simultaneous accesses to %0 %1, but "
"%select{initialization|read|modification|deinitialization}2 requires "
"overlapping accesses to %0, but "
"%select{initialization|read|modification|deinitialization}1 requires "
"exclusive access; consider copying to a local variable",
(DescriptiveDeclKind, Identifier, unsigned))
(StringRef, unsigned))

ERROR(exclusivity_access_required_unknown_decl,none,
"simultaneous accesses, but "
"overlapping accesses, but "
"%select{initialization|read|modification|deinitialization}0 requires "
"exclusive access; consider copying to a local variable", (unsigned))

WARNING(exclusivity_access_required_unknown_decl_swift3,none,
"simultaneous accesses, but "
"overlapping accesses, but "
"%select{initialization|read|modification|deinitialization}0 requires "
"exclusive access; consider copying to a local variable", (unsigned))

Expand Down
210 changes: 210 additions & 0 deletions include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//===--- AccessSummaryAnalysis.h - SIL Access Summary Analysis --*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 formal accesses that a function makes to its address-type arguments.
// These summaries are used to statically diagnose violations of exclusive
// accesses for noescape closures.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_
#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_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"

namespace swift {

class AccessSummaryAnalysis : public BottomUpIPAnalysis {
public:
/// Summarizes the accesses that a function begins on an argument.
class ArgumentSummary {
private:
/// The kind of access begun on the argument.
/// 'None' means no access performed.
Optional<SILAccessKind> Kind = None;

/// The location of the access. Used for diagnostics.
SILLocation AccessLoc = SILLocation((Expr *)nullptr);

public:
Optional<SILAccessKind> getAccessKind() const { return Kind; }

SILLocation getAccessLoc() const { return AccessLoc; }

/// The lattice operation on argument summaries.
bool mergeWith(const ArgumentSummary &other);

/// Merge in an access to the argument of the given kind at the given
/// location. Returns true if the merge caused the summary to change.
bool mergeWith(SILAccessKind otherKind, SILLocation otherLoc);

/// Returns a description of the summary. For debugging and testing
/// purposes.
StringRef getDescription() const;
};

/// Summarizes the accesses that a function begins on its arguments.
class FunctionSummary {
private:
llvm::SmallVector<ArgumentSummary, 6> ArgAccesses;

public:
FunctionSummary(unsigned argCount) : ArgAccesses(argCount) {}

/// Returns of summary of the the function accesses that argument at the
/// given index.
ArgumentSummary &getAccessForArgument(unsigned argument) {
return ArgAccesses[argument];
}

const ArgumentSummary &getAccessForArgument(unsigned argument) const {
return ArgAccesses[argument];
}

/// Returns the number of argument in the summary.
unsigned getArgumentCount() const { return ArgAccesses.size(); }
};

friend raw_ostream &operator<<(raw_ostream &os,
const FunctionSummary &summary);

class FunctionInfo;
/// Records a flow of a caller's argument to a called function.
/// This flow is used to iterate the interprocedural analysis to a fixpoint.
struct ArgumentFlow {
/// The index of the argument in the caller.
const unsigned CallerArgumentIndex;

/// The index of the argument in the callee.
const unsigned CalleeArgumentIndex;

FunctionInfo *const CalleeFunctionInfo;
};

/// Records the summary and argument flows for a given function.
/// Used by the BottomUpIPAnalysis to propagate information
/// from callees to callers.
class FunctionInfo : public FunctionInfoBase<FunctionInfo> {
private:
FunctionSummary FS;

SILFunction *F;

llvm::SmallVector<ArgumentFlow, 8> RecordedArgumentFlows;

public:
FunctionInfo(SILFunction *F) : FS(F->getArguments().size()), F(F) {}

SILFunction *getFunction() const { return F; }

ArrayRef<ArgumentFlow> getArgumentFlows() const {
return RecordedArgumentFlows;
}

const FunctionSummary &getSummary() const { return FS; }
FunctionSummary &getSummary() { return FS; }

/// Record a flow of an argument in this function to a callee.
void recordFlow(const ArgumentFlow &flow) {
flow.CalleeFunctionInfo->addCaller(this, nullptr);
RecordedArgumentFlows.push_back(flow);
}
};

private:
/// Maps functions to the information the analysis keeps for each function.
llvm::DenseMap<SILFunction *, FunctionInfo *> FunctionInfos;

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.
IndexTrieNode *SubPathTrie;

public:
AccessSummaryAnalysis() : BottomUpIPAnalysis(AnalysisKind::AccessSummary) {
SubPathTrie = new IndexTrieNode();
}

~AccessSummaryAnalysis() {
delete SubPathTrie;
}

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

IndexTrieNode *getSubPathTrieRoot() {
return SubPathTrie;
}

virtual void initialize(SILPassManager *PM) override {}
virtual void invalidate() override;
virtual void invalidate(SILFunction *F, InvalidationKind K) override;
virtual void notifyAddFunction(SILFunction *F) override {}
virtual void notifyDeleteFunction(SILFunction *F) override {
invalidate(F, InvalidationKind::Nothing);
}
virtual void invalidateFunctionTables() override {}

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

private:
typedef BottomUpFunctionOrder<FunctionInfo> FunctionOrder;

/// Returns the BottomUpIPAnalysis information for the given function.
FunctionInfo *getFunctionInfo(SILFunction *F);

/// Summarizes the given function and iterates the interprocedural analysis
/// to a fixpoint.
void recompute(FunctionInfo *initial);

/// Propagate the access summary from the argument of a called function
/// to the caller.
bool propagateFromCalleeToCaller(FunctionInfo *callerInfo,
ArgumentFlow site);

/// Summarize the given function and schedule it for interprocedural
/// analysis.
void processFunction(FunctionInfo *info, FunctionOrder &order);

/// Summarize how the function uses the given argument.
void processArgument(FunctionInfo *info, SILFunctionArgument *argment,
ArgumentSummary &summary, FunctionOrder &order);

/// Summarize a partial_apply instruction.
void processPartialApply(FunctionInfo *callerInfo,
unsigned callerArgumentIndex,
PartialApplyInst *apply,
Operand *applyArgumentOperand, FunctionOrder &order);

/// Summarize apply or try_apply
void processFullApply(FunctionInfo *callerInfo, unsigned callerArgumentIndex,
FullApplySite apply, Operand *argumentOperand,
FunctionOrder &order);

/// Summarize a call site and schedule it for interprocedural analysis.
void processCall(FunctionInfo *callerInfo, unsigned callerArgumentIndex,
SILFunction *calledFunction, unsigned argumentIndex,
FunctionOrder &order);
};

} // end namespace swift

#endif

1 change: 1 addition & 0 deletions include/swift/SILOptimizer/Analysis/Analysis.def
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define ANALYSIS(NAME)
#endif

ANALYSIS(AccessSummary)
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 @@ -56,6 +56,8 @@ PASS(ABCOpt, "abcopts",
"Array Bounds Check Optimization")
PASS(AccessEnforcementSelection, "access-enforcement-selection",
"Access Enforcement Selection")
PASS(AccessSummaryDumper, "access-summary-dump",
"Dump Address Parameter Access Summary")
PASS(InactiveAccessMarkerElimination, "inactive-access-marker-elim",
"Inactive Access Marker Elimination.")
PASS(FullAccessMarkerElimination, "full-access-marker-elim",
Expand Down
84 changes: 84 additions & 0 deletions include/swift/SILOptimizer/Utils/IndexTrie.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===--- IndexTrie - Trie for a sequence of integer indices ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H
#define SWIFT_SILOPTIMIZER_UTILS_INDEXTREE_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>

namespace swift {

// Trie node representing a sequence of unsigned integer indices.
class IndexTrieNode {
static const unsigned RootIdx = ~0U;
unsigned Index;
llvm::SmallVector<IndexTrieNode*, 8> Children;
IndexTrieNode *Parent;

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

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

IndexTrieNode(IndexTrieNode &) =delete;
IndexTrieNode &operator=(const IndexTrieNode&) =delete;

~IndexTrieNode() {
for (auto *N : Children)
delete N;
}

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

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

unsigned getIndex() const { return Index; }

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

auto I = std::lower_bound(Children.begin(), Children.end(), Idx,
[](IndexTrieNode *a, unsigned i) {
return a->Index < i;
});
if (I != Children.end() && (*I)->Index == Idx)
return *I;
auto *N = new IndexTrieNode(Idx, this);
Children.insert(I, N);
return N;
}

ArrayRef<IndexTrieNode*> getChildren() const { return Children; }

IndexTrieNode *getParent() const { return Parent; }

/// Returns true when the sequence of indices represented by this
/// node is a prefix of the sequence represented by the passed-in node.
bool isPrefixOf(const IndexTrieNode *Other) const {
const IndexTrieNode *I = Other;

do {
if (this == I)
return true;

I = I->getParent();
} while (I);

return false;
}
};

} // end namespace swift

#endif
Loading