diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index a40cf9650fa9b..ed439c8aa04b1 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -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. @@ -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. @@ -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; @@ -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 @@ -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. diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 2371cb70a4abe..4ed1872d617b9 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -474,6 +474,8 @@ class Projection { createAggFromFirstLevelProjections(SILBuilder &B, SILLocation Loc, SILType BaseType, llvm::SmallVectorImpl &Values); + + void print(raw_ostream &os, SILType baseType) const; private: /// Convenience method for getting the raw underlying index as a pointer. TypeBase *getPointer() const { @@ -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. diff --git a/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h new file mode 100644 index 0000000000000..fe175c116bda4 --- /dev/null +++ b/include/swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h @@ -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 + 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; + + AccessedStorageMap storageAccessMap; + Optional 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 transformStorage); + + template 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 { +public: + AccessedStorageAnalysis() + : GenericFunctionEffectAnalysis( + AnalysisKind::AccessedStorage) {} + + static bool classof(const SILAnalysis *S) { + return S->getKind() == AnalysisKind::AccessedStorage; + } +}; + +} // end namespace swift + +#endif // SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H_ diff --git a/include/swift/SILOptimizer/Analysis/Analysis.def b/include/swift/SILOptimizer/Analysis/Analysis.def index 8cbf23cf37ef0..f0ed98afbc004 100644 --- a/include/swift/SILOptimizer/Analysis/Analysis.def +++ b/include/swift/SILOptimizer/Analysis/Analysis.def @@ -24,6 +24,7 @@ #endif ANALYSIS(AccessSummary) +ANALYSIS(AccessedStorage) ANALYSIS(Alias) ANALYSIS(BasicCallee) ANALYSIS(Caller) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index a5062e5a5745c..50167647087aa 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -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", diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp index d35fa5abe98b9..afc5a18a79eda 100644 --- a/lib/SIL/MemAccessUtils.cpp +++ b/lib/SIL/MemAccessUtils.cpp @@ -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(); @@ -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(address)); return AccessedStorage(address, AccessedStorage::Unidentified); @@ -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)) diff --git a/lib/SIL/Projection.cpp b/lib/SIL/Projection.cpp index fe0371b729f61..9c079e11249d6 100644 --- a/lib/SIL/Projection.cpp +++ b/lib/SIL/Projection.cpp @@ -508,7 +508,46 @@ ProjectionPath::removePrefix(const ProjectionPath &Path, return P; } -raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) { +void Projection::print(raw_ostream &os, SILType baseType) const { + if (isNominalKind()) { + auto *Decl = getVarDecl(baseType); + os << "Field: "; + Decl->print(os); + return; + } + + if (getKind() == ProjectionKind::Tuple) { + os << "Index: " << getIndex(); + return; + } + if (getKind() == ProjectionKind::BitwiseCast) { + os << "BitwiseCast"; + return; + } + if (getKind() == ProjectionKind::Index) { + os << "Index: " << getIndex(); + return; + } + if (getKind() == ProjectionKind::Upcast) { + os << "UpCast"; + return; + } + if (getKind() == ProjectionKind::RefCast) { + os << "RefCast"; + return; + } + if (getKind() == ProjectionKind::Box) { + os << " Box over"; + return; + } + if (getKind() == ProjectionKind::TailElems) { + os << " TailElems"; + return; + } + os << ""; +} + +raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { os << "Projection Path ["; SILType IterType = getBaseType(); for (const Projection &IterProj : Path) { @@ -517,50 +556,14 @@ raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) { os << BaseType.getAddressType() << "\n "; - if (IterProj.isNominalKind()) { - auto *Decl = IterProj.getVarDecl(BaseType); - os << "Field: "; - Decl->print(os); - os << " of: "; - continue; - } - - if (IterProj.getKind() == ProjectionKind::Tuple) { - os << "Index: " << IterProj.getIndex() << " into: "; - continue; - } - - if (IterProj.getKind() == ProjectionKind::BitwiseCast) { - os << "BitwiseCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Index) { - os << "Index: " << IterProj.getIndex() << " into: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Upcast) { - os << "UpCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::RefCast) { - os << "RefCast to: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::Box) { - os << " Box over: "; - continue; - } - if (IterProj.getKind() == ProjectionKind::TailElems) { - os << " TailElems of: "; - continue; - } - os << " into: "; + IterProj.print(os, BaseType); + os << " in: "; } os << IterType.getAddressType() << "]\n"; return os; } -void ProjectionPath::dump(SILModule &M) { +void ProjectionPath::dump(SILModule &M) const { print(llvm::dbgs(), M); } diff --git a/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp new file mode 100644 index 0000000000000..d4b6445f957dd --- /dev/null +++ b/lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp @@ -0,0 +1,244 @@ +//===--- AccessedStorageAnalysis.cpp - Accessed Storage Analysis ---------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "accessed-storage" + +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" +#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" +#include "swift/SILOptimizer/Analysis/FunctionOrder.h" +#include "swift/SILOptimizer/PassManager/PassManager.h" + +using namespace swift; + +static bool updateAccessKind(SILAccessKind &LHS, SILAccessKind RHS) { + bool changed = false; + // Assume we don't track Init/Deinit. + if (LHS == SILAccessKind::Read && RHS == SILAccessKind::Modify) { + LHS = RHS; + changed = true; + } + return changed; +} + +static bool updateOptionalAccessKind(Optional &LHS, + Optional RHS) { + if (RHS == None) + return false; + + if (LHS == None) { + LHS = RHS; + return true; + } + return updateAccessKind(LHS.getValue(), RHS.getValue()); +} + +bool StorageAccessInfo::mergeFrom(const StorageAccessInfo &RHS) { + assert(accessKind == SILAccessKind::Read + || accessKind == SILAccessKind::Modify && "uninitialized info"); + bool changed = updateAccessKind(accessKind, RHS.accessKind); + if (noNestedConflict && !RHS.noNestedConflict) { + noNestedConflict = false; + changed = true; + } + return changed; +} + +bool FunctionAccessedStorage::summarizeFunction(SILFunction *F) { + assert(storageAccessMap.empty() && "expected uninitialized results."); + + if (F->isDefinition()) + return false; + + // If the function definition is unavailable, set unidentifiedAccess to a + // conservative value, since analyzeInstruction will never be called. + // + // If FunctionSideEffects can be summarized, use that information. + FunctionSideEffects functionSideEffects; + if (!functionSideEffects.summarizeFunction(F)) { + setWorstEffects(); + // May as well consider this a successful summary since there are no + // instructions to visit anyway. + return true; + } + bool mayRead = functionSideEffects.getGlobalEffects().mayRead(); + bool mayWrite = functionSideEffects.getGlobalEffects().mayWrite(); + for (auto ¶mEffects : functionSideEffects.getParameterEffects()) { + mayRead |= paramEffects.mayRead(); + mayWrite |= paramEffects.mayWrite(); + } + if (mayWrite) + unidentifiedAccess = SILAccessKind::Modify; + else if (mayRead) + unidentifiedAccess = SILAccessKind::Read; + + // If function side effects is "readnone" then this result will have an empty + // storageAccessMap and unidentifiedAccess == None. + return true; +} + +bool FunctionAccessedStorage::updateUnidentifiedAccess( + SILAccessKind accessKind) { + if (unidentifiedAccess == None) { + unidentifiedAccess = accessKind; + return true; + } + return updateAccessKind(unidentifiedAccess.getValue(), accessKind); +} + +bool FunctionAccessedStorage::mergeAccesses( + const FunctionAccessedStorage &RHS, + std::function transformStorage) { + + bool changed = false; + + for (auto &accessEntry : RHS.storageAccessMap) { + + const AccessedStorage &storage = transformStorage(accessEntry.first); + // transformStorage returns invalid storage object for local storage + // that should not be merged with the caller. + if (!storage) + continue; + + if (storage.getKind() == AccessedStorage::Unidentified) + changed |= updateUnidentifiedAccess(accessEntry.second.accessKind); + else { + auto result = storageAccessMap.try_emplace(storage, accessEntry.second); + if (result.second) + changed = true; + else + changed |= result.first->second.mergeFrom(accessEntry.second); + } + } + + if (RHS.unidentifiedAccess != None) + changed |= updateUnidentifiedAccess(RHS.unidentifiedAccess.getValue()); + + return changed; +} + +bool FunctionAccessedStorage::mergeFrom(const FunctionAccessedStorage &RHS) { + // Merge accesses from RHS. Both `this` and `RHS` are either from the same + // function or are both callees of the same call site, so their parameters + // indices coincide. transformStorage is the identity function. + return mergeAccesses(RHS, [](const AccessedStorage &s) { return s; }); +} + +/// Transform AccessedStorage from a callee into the caller context. If this is +/// uniquely identified local storage, then return an invalid storage object. +static AccessedStorage transformCalleeStorage(const AccessedStorage &storage, + FullApplySite fullApply) { + switch (storage.getKind()) { + case AccessedStorage::Box: + case AccessedStorage::Stack: + // Do not merge local storage. + return AccessedStorage(); + case AccessedStorage::Global: + // Global accesses is universal. + return storage; + case AccessedStorage::Class: { + // If the object's value is an argument, translate it into a value on the + // caller side. + SILValue obj = storage.getObjectProjection().getObject(); + if (auto *arg = dyn_cast(obj)) { + return AccessedStorage(fullApply.getArgument(arg->getIndex()), + storage.getObjectProjection().getProjection()); + } + // Otherwise, continue to reference the value in the callee because we don't + // have any better placeholder for a callee-defined object. + return storage; + } + case AccessedStorage::Argument: + // Transitively search for the storage base in the caller. + return findAccessedStorageOrigin(fullApply.getArgument(storage.getParamIndex())); + case AccessedStorage::Nested: + llvm_unreachable("Unexpected nested access"); + case AccessedStorage::Unidentified: + // For unidentified storage, continue to reference the value in the callee + // because we don't have any better placeholder for a callee-defined object. + return storage; + } +} + +bool FunctionAccessedStorage::mergeFromApply( + const FunctionAccessedStorage &calleeAccess, FullApplySite fullApply) { + // Merge accesses from RHS using the identity transform on it's + // AccessedStorage. Transform any Argument type AccessedStorage into the + // caller context to be added to `this` storage map. + return mergeAccesses(calleeAccess, [&fullApply](const AccessedStorage &s) { + return transformCalleeStorage(s, fullApply); + }); +} + +template +void FunctionAccessedStorage::visitBeginAccess(B *beginAccess) { + const AccessedStorage &storage = + findAccessedStorageOrigin(beginAccess->getSource()); + + if (storage.getKind() == AccessedStorage::Unidentified) { + updateOptionalAccessKind(unidentifiedAccess, beginAccess->getAccessKind()); + return; + } + StorageAccessInfo accessInfo(beginAccess); + auto result = storageAccessMap.try_emplace(storage, accessInfo); + if (!result.second) + result.first->second.mergeFrom(accessInfo); +} + +void FunctionAccessedStorage::analyzeInstruction(SILInstruction *I) { + if (auto *BAI = dyn_cast(I)) + visitBeginAccess(BAI); + else if (auto *BUAI = dyn_cast(I)) + visitBeginAccess(BUAI); +} + +bool FunctionAccessedStorage::mayConflictWith( + SILAccessKind otherAccessKind, const AccessedStorage &otherStorage) { + if (unidentifiedAccess != None + && accessKindMayConflict(otherAccessKind, + unidentifiedAccess.getValue())) { + return true; + } + for (auto &accessEntry : storageAccessMap) { + + const AccessedStorage &storage = accessEntry.first; + assert(storage && "FunctionAccessedStorage mapped invalid storage."); + + StorageAccessInfo accessInfo = accessEntry.second; + if (!accessKindMayConflict(otherAccessKind, accessInfo.accessKind)) + continue; + + if (!otherStorage.isDistinct(storage)) + return true; + } + return false; +} + +void FunctionAccessedStorage::print(raw_ostream &os) const { + for (auto &accessEntry : storageAccessMap) { + const AccessedStorage &storage = accessEntry.first; + const StorageAccessInfo &info = accessEntry.second; + os << " [" << getSILAccessKindName(info.accessKind) << "] "; + if (info.noNestedConflict) + os << "[no_nested_conflict] "; + storage.print(os); + } + if (unidentifiedAccess != None) { + os << " unidentified accesses: " + << getSILAccessKindName(unidentifiedAccess.getValue()) << "\n"; + } +} + +void FunctionAccessedStorage::dump() const { print(llvm::errs()); } + +SILAnalysis *swift::createAccessedStorageAnalysis(SILModule *) { + return new AccessedStorageAnalysis(); +} diff --git a/lib/SILOptimizer/Analysis/CMakeLists.txt b/lib/SILOptimizer/Analysis/CMakeLists.txt index b6f4fc42caa8d..825bc0d679969 100644 --- a/lib/SILOptimizer/Analysis/CMakeLists.txt +++ b/lib/SILOptimizer/Analysis/CMakeLists.txt @@ -1,6 +1,7 @@ set(ANALYSIS_SOURCES Analysis/ARCAnalysis.cpp Analysis/AccessSummaryAnalysis.cpp + Analysis/AccessedStorageAnalysis.cpp Analysis/AliasAnalysis.cpp Analysis/Analysis.cpp Analysis/ArraySemantic.cpp diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 26e83a03be32b..1952a4c79986f 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -12,10 +12,11 @@ #define DEBUG_TYPE "sil-sea" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" #include "swift/SILOptimizer/Analysis/FunctionOrder.h" #include "swift/SILOptimizer/PassManager/PassManager.h" -#include "swift/SIL/SILArgument.h" using namespace swift; @@ -187,6 +188,7 @@ void GenericFunctionEffectAnalysis::recompute( // Instantiate template members. template class swift::GenericFunctionEffectAnalysis; +template class swift::GenericFunctionEffectAnalysis; // ----------------------------------------------------------------------------- // FunctionSideEffects diff --git a/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp new file mode 100644 index 0000000000000..93c8af56ac13e --- /dev/null +++ b/lib/SILOptimizer/UtilityPasses/AccessedStorageDumper.cpp @@ -0,0 +1,49 @@ +//===--- AccessedStorageDumper.cpp - Dump accessed storage for functions ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "sil-accessed-storage-dumper" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Dumps per-function information on dynamically enforced formal accesses. +class AccessedStorageDumper : public SILModuleTransform { + + void run() override { + auto *analysis = PM->getAnalysis(); + + for (auto &fn : *getModule()) { + llvm::outs() << "@" << fn.getName() << "\n"; + if (fn.empty()) { + llvm::outs() << "\n"; + continue; + } + const FunctionAccessedStorage &summary = analysis->getEffects(&fn); + summary.print(llvm::outs()); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createAccessedStorageDumper() { + return new AccessedStorageDumper(); +} diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt index 93137a9924e03..4681be81457db 100644 --- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt +++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt @@ -1,6 +1,7 @@ set(UTILITYPASSES_SOURCES UtilityPasses/AADumper.cpp UtilityPasses/AccessSummaryDumper.cpp + UtilityPasses/AccessedStorageDumper.cpp UtilityPasses/BasicCalleePrinter.cpp UtilityPasses/BasicInstructionPropertyDumper.cpp UtilityPasses/BugReducerTester.cpp diff --git a/test/SILOptimizer/accessed_storage_analysis.sil b/test/SILOptimizer/accessed_storage_analysis.sil new file mode 100644 index 0000000000000..b846b8310163f --- /dev/null +++ b/test/SILOptimizer/accessed_storage_analysis.sil @@ -0,0 +1,388 @@ +// RUN: %target-sil-opt %s -accessed-storage-dump -enable-sil-verify-all -o /dev/null -assume-parsing-unqualified-ownership-sil | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +// CHECK-LABEL: @readIdentifiedArg +// CHECK: [read] Argument index: 0 +sil @readIdentifiedArg : $@convention(thin) (@in Int) -> Int { +bb0(%0 : $*Int): + %5 = begin_access [read] [dynamic] %0 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @writeIdentifiedArg +// CHECK: [modify] Argument index: 0 +sil @writeIdentifiedArg : $@convention(thin) (Int) -> (@out Int) { +bb0(%0 : $*Int, %1 : $Int): + %3 = begin_access [modify] [dynamic] %0 : $*Int + store %1 to %3 : $*Int + end_access %3 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteIdentifiedArg +// CHECK: [modify] Argument index: 0 +sil @readWriteIdentifiedArg : $@convention(thin) (Int) -> (@out Int, Int) { +bb0(%0 : $*Int, %1 : $Int): + %2 = function_ref @writeIdentifiedArg : $@convention(thin) (Int) -> (@out Int) + %3 = apply %2(%0, %1) : $@convention(thin) (Int) -> (@out Int) + %5 = begin_access [modify] [dynamic] %0 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @writeIdentifiedStack +// CHECK: [modify] Stack %1 = alloc_stack $Int +sil @writeIdentifiedStack : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + %1 = alloc_stack $Int + %3 = begin_access [modify] [dynamic] %1 : $*Int + store %0 to %3 : $*Int + end_access %3 : $*Int + dealloc_stack %1 : $*Int + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: @readWriteIdentifiedStack +// CHECK: [modify] Stack %1 = alloc_stack $Int +sil @readWriteIdentifiedStack : $@convention(thin) (Int) -> Int { +bb0(%0 : $Int): + %1 = alloc_stack $Int + %2 = function_ref @writeIdentifiedArg : $@convention(thin) (Int) -> (@out Int) + %3 = apply %2(%1, %0) : $@convention(thin) (Int) -> (@out Int) + %5 = begin_access [modify] [dynamic] %1 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + dealloc_stack %1 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @readIdentifiedBoxArg +// CHECK: [read] Argument index: 0 +sil @readIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }) -> Int { +bb0(%0 : ${ var Int }): + %1 = project_box %0 : ${ var Int }, 0 + %5 = begin_access [read] [dynamic] %1 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @writeIdentifiedBoxArg +// CHECK: [modify] Argument index: 0 +sil @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () { +bb0(%0 : ${ var Int }, %1 : $Int): + %2 = project_box %0 : ${ var Int }, 0 + %3 = begin_access [modify] [dynamic] %2 : $*Int + store %1 to %3 : $*Int + end_access %3 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteIdentifiedBoxArg +// CHECK: [modify] Argument index: 0 +sil @readWriteIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> Int { +bb0(%0 : ${ var Int }, %1 : $Int): + %2 = function_ref @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () + %3 = apply %2(%0, %1) : $@convention(thin) (@guaranteed { var Int }, Int) -> () + %4 = project_box %0 : ${ var Int }, 0 + %5 = begin_access [modify] [dynamic] %4 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @writeIdentifiedBox +// CHECK: [read] Box %1 = alloc_box ${ var Int } +sil @writeIdentifiedBox : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + %1 = alloc_box ${ var Int } + %2 = project_box %1 : ${ var Int }, 0 + %3 = begin_access [read] [dynamic] %2 : $*Int + store %0 to %3 : $*Int + end_access %3 : $*Int + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: @readWriteIdentifiedBox +// CHECK: [modify] Box %1 = alloc_box ${ var Int } +sil @readWriteIdentifiedBox : $@convention(thin) (Int) -> Int { +bb0(%0 : $Int): + %1 = alloc_box ${ var Int } + %2 = function_ref @writeIdentifiedBoxArg : $@convention(thin) (@guaranteed { var Int }, Int) -> () + %3 = apply %2(%1, %0) : $@convention(thin) (@guaranteed { var Int }, Int) -> () + %4 = project_box %1 : ${ var Int }, 0 + %5 = begin_access [modify] [dynamic] %4 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +sil_global @int_global : $Int + +// CHECK-LABEL: @readIdentifiedGlobal +// CHECK: [read] Global // int_global +// CHECK: sil_global @int_global : $Int +sil @readIdentifiedGlobal : $@convention(thin) () -> Int { +bb0: + %1 = global_addr @int_global : $*Int + %2 = begin_access [read] [dynamic] %1 : $*Int + %3 = load %2 : $*Int + end_access %2 : $*Int + return %3 : $Int +} + +// CHECK-LABEL: @writeIdentifiedGlobal +// CHECK: [modify] Global // int_global +// CHECK: sil_global @int_global : $Int +sil @writeIdentifiedGlobal : $@convention(thin) (Int) -> () { +bb0(%0 : $Int): + %1 = global_addr @int_global : $*Int + %2 = begin_access [modify] [dynamic] %1 : $*Int + store %0 to %2 : $*Int + end_access %2 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteIdentifiedGlobal +// CHECK: [modify] Global // int_global +// CHECK: sil_global @int_global : $Int +sil @readWriteIdentifiedGlobal : $@convention(thin) (Int) -> (Int) { +bb0(%0 : $Int): + %1 = function_ref @writeIdentifiedGlobal : $@convention(thin) (Int) -> () + %2 = apply %1(%0) : $@convention(thin) (Int) -> () + %3 = global_addr @int_global : $*Int + %4 = begin_access [read] [dynamic] %3 : $*Int + %5 = load %4 : $*Int + end_access %4 : $*Int + return %5 : $Int +} + +class C { + @sil_stored var property: Int + deinit + init() +} + +// CHECK-LABEL: @readIdentifiedClass +// CHECK: [read] Class %0 = argument of bb0 : $C +// CHECK: Field: @sil_stored var property: Int +sil @readIdentifiedClass : $@convention(thin) (@guaranteed C) -> Int { +bb0(%0 : $C): + %1 = ref_element_addr %0 : $C, #C.property + %2 = begin_access [read] [dynamic] %1 : $*Int + %3 = load %2 : $*Int + end_access %2 : $*Int + return %3 : $Int +} + +// CHECK-LABEL: @writeIdentifiedClass +// CHECK: [modify] Class %0 = argument of bb0 : $C +// CHECK: Field: @sil_stored var property: Int +sil @writeIdentifiedClass : $@convention(thin) (@guaranteed C, Int) -> () { +bb0(%0 : $C, %1 : $Int): + %2 = ref_element_addr %0 : $C, #C.property + %3 = begin_access [modify] [dynamic] %2 : $*Int + store %1 to %3 : $*Int + end_access %3 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteIdentifiedClass +// CHECK: [modify] Class %1 = alloc_ref $C +// CHECK: Field: @sil_stored var property: Int +sil @readWriteIdentifiedClass : $@convention(thin) (Int) -> (Int) { +bb0(%0 : $Int): + %1 = alloc_ref $C + %2 = function_ref @writeIdentifiedClass : $@convention(thin) (@guaranteed C, Int) -> () + %3 = apply %2(%1, %0) : $@convention(thin) (@guaranteed C, Int) -> () + %4 = ref_element_addr %1 : $C, #C.property + %5 = begin_access [read] [dynamic] %4 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +// CHECK-LABEL: @readIdentifiedNestedClass +// CHECK: [read] Class %0 = argument of bb0 : $C +// CHECK: Field: @sil_stored var property: Int +sil @readIdentifiedNestedClass : $@convention(thin) (@guaranteed C) -> Int { +bb0(%0 : $C): + %1 = ref_element_addr %0 : $C, #C.property + %2 = begin_access [read] [dynamic] %1 : $*Int + %3 = begin_access [read] [dynamic] %2 : $*Int + %4 = load %3 : $*Int + end_access %2 : $*Int + end_access %3 : $*Int + return %4 : $Int +} + +// CHECK-LABEL: @writeIdentifiedNestedClass +// CHECK: [modify] Class %0 = argument of bb0 : $C +// CHECK: Field: @sil_stored var property: Int +sil @writeIdentifiedNestedClass : $@convention(thin) (@guaranteed C, Int) -> () { +bb0(%0 : $C, %1 : $Int): + %2 = ref_element_addr %0 : $C, #C.property + %3 = begin_access [read] [dynamic] %2 : $*Int + %4 = begin_access [modify] [dynamic] %3 : $*Int + store %1 to %4 : $*Int + end_access %4 : $*Int + end_access %3 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteIdentifiedNestedClass +// CHECK: [modify] Class %1 = alloc_ref $C +// CHECK: Field: @sil_stored var property: Int +sil @readWriteIdentifiedNestedClass : $@convention(thin) (Int) -> (Int) { +bb0(%0 : $Int): + %1 = alloc_ref $C + %2 = function_ref @writeIdentifiedNestedClass : $@convention(thin) (@guaranteed C, Int) -> () + %3 = apply %2(%1, %0) : $@convention(thin) (@guaranteed C, Int) -> () + %4 = ref_element_addr %1 : $C, #C.property + %5 = begin_access [read] [dynamic] %4 : $*Int + %6 = begin_access [read] [dynamic] %5 : $*Int + %7 = load %6 : $*Int + end_access %5 : $*Int + end_access %6 : $*Int + return %7 : $Int +} + +// CHECK-LABEL: @readUnidentified +// CHECK: unidentified accesses: read +sil @readUnidentified : $@convention(thin) (Builtin.RawPointer) -> Int { +bb0(%0 : $Builtin.RawPointer): + %1 = pointer_to_address %0 : $Builtin.RawPointer to $*Int + %2 = begin_access [read] [dynamic] %1 : $*Int + %4 = load %2 : $*Int + end_access %2 : $*Int + return %4 : $Int +} + +// CHECK-LABEL: @writeUnidentified +// CHECK: unidentified accesses: modify +sil @writeUnidentified : $@convention(thin) (Builtin.RawPointer, Int) -> () { +bb0(%0 : $Builtin.RawPointer, %1 : $Int): + %2 = pointer_to_address %0 : $Builtin.RawPointer to $*Int + %3 = begin_access [modify] [dynamic] %2 : $*Int + store %1 to %3 : $*Int + end_access %3 : $*Int + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: @readWriteUnidentified +// CHECK: unidentified accesses: modify +sil @readWriteUnidentified : $@convention(thin) (Builtin.RawPointer, Int) -> Int { +bb0(%0 : $Builtin.RawPointer, %1 : $Int): + %2 = function_ref @writeUnidentified : $@convention(thin) (Builtin.RawPointer, Int) -> () + %3 = apply %2(%0, %1) : $@convention(thin) (Builtin.RawPointer, Int) -> () + %4 = pointer_to_address %0 : $Builtin.RawPointer to $*Int + %5 = begin_access [read] [dynamic] %4 : $*Int + %6 = load %5 : $*Int + end_access %5 : $*Int + return %6 : $Int +} + +enum TreeB { + case Nil + case Leaf(T) + indirect case Branch(left: TreeB, right: TreeB) +} + +// CHECK-LABEL: @readIndirectEnum +// CHECK: [read] Argument index: 1 +sil @readIndirectEnum : $@convention(thin) (@in TreeB) -> (@out TreeB) { +bb0(%0 : $*TreeB, %1 : $*TreeB): + %enumAddr = unchecked_take_enum_data_addr %1 : $*TreeB, #TreeB.Branch!enumelt.1 + %box = load %enumAddr : $*<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } + %boxAddr = project_box %box : $<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } , 0 + %boxAccess = begin_access [read] [dynamic] %boxAddr : $*(left: TreeB, right: TreeB) + %leftAddr = tuple_element_addr %boxAccess : $*(left: TreeB, right: TreeB), 0 + copy_addr %leftAddr to [initialization] %0 : $*TreeB + end_access %boxAccess : $*(left: TreeB, right: TreeB) + %v = tuple () + return %v : $() +} + +struct SomeError: Error {} + +// CHECK-LABEL: @writeIdentifiedArgReadUnidentifiedVarious +// CHECK: [modify] Argument index: 0 +// CHECK: unidentified accesses: read +sil @writeIdentifiedArgReadUnidentifiedVarious : $@convention(thin) (Int, Error) -> (@out Int) { +bb0(%out : $*Int, %i : $Int, %e : $Error): + %argAccess = begin_access [modify] [dynamic] %out : $*Int + store %i to %argAccess : $*Int + end_access %argAccess : $*Int + + %peb = project_existential_box $SomeError in %e : $Error + %pebAccess = begin_access [read] [dynamic] %peb : $*SomeError + %eload = load %pebAccess : $*SomeError + end_access %pebAccess : $*SomeError + + %oeb = open_existential_box %e : $Error to $*@opened("01234567-89AB-CDEF-0123-333333333333") Error + %oebAccess = begin_access [read] [dynamic] %oeb : $*@opened("01234567-89AB-CDEF-0123-333333333333") Error + end_access %oebAccess : $*@opened("01234567-89AB-CDEF-0123-333333333333") Error + + %v = tuple () + return %v : $() +} + +protocol P {} + +class CP : P { + var property: Int + init() + deinit +} + +// None of these address producers are normally used for formal +// access. i.e. They should not feed into a begin_access +// operand. Eventually, we may want to verify that SIL passes never +// result in such patterns. Until then we gracefully treat them as +// unidentified access. +// CHECK-LABEL: @readUnexpected +// CHECK: unidentified accesses: read +sil @readUnexpected : $@convention(thin) (@inout Int, @inout Int?, @inout Builtin.UnsafeValueBuffer) -> () { +bb0(%inout : $*Int, %oi : $*Optional, %b : $*Builtin.UnsafeValueBuffer): + br bb1(%inout : $*Int) + +bb1(%phi : $*Int): + %phiAccess = begin_access [read] [dynamic] %phi : $*Int + %ld = load %phiAccess : $*Int + end_access %phiAccess : $*Int + + %ea = init_enum_data_addr %oi : $*Optional, #Optional.some!enumelt.1 + %eaAccess = begin_access [read] [dynamic] %ea : $*Int + %eaload = load %eaAccess : $*Int + end_access %eaAccess : $*Int + + %pvb = project_value_buffer $Int in %b : $*Builtin.UnsafeValueBuffer + %pvbAccess = begin_access [read] [dynamic] %pvb : $*Int + %bload = load %pvbAccess : $*Int + end_access %pvbAccess : $*Int + + %p = alloc_stack $P + %iea = init_existential_addr %p : $*P, $CP + %propAccess = begin_access [read] [dynamic] %iea : $*CP + end_access %propAccess : $*CP + dealloc_stack %p : $*P + + %v = tuple () + return %v : $() +} diff --git a/test/SILOptimizer/lslocation_expansion.sil b/test/SILOptimizer/lslocation_expansion.sil index 22ed0838631f0..9dbccfb9bccba 100644 --- a/test/SILOptimizer/lslocation_expansion.sil +++ b/test/SILOptimizer/lslocation_expansion.sil @@ -104,13 +104,13 @@ bb0(%0 : $B): // CHECK: #0 store // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil @store_after_store_struct : $@convention(thin) () -> () { %1 = alloc_stack $S1 %9 = integer_literal $Builtin.Int64, 0 // user: %10 @@ -129,96 +129,96 @@ sil @store_after_store_struct : $@convention(thin) () -> () { // CHECK: #0 store // CHECK-NEXT: alloc_stack $S2 // CHECK-NEXT: Projection Path [$*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S2 // CHECK-NEXT: Projection Path [$*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK: #2 store // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var y: S3 of: $*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var y: S3 in: $*S3 +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var y: S3 of: $*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var y: S3 in: $*S3 +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var y: S3 of: $*S3 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var y: S3 in: $*S3 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var y: S3 of: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var y: S3 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var y: S3 of: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var y: S3 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var x: S3 of: $*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var x: S3 in: $*S3 +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var x: S3 of: $*S3 -// CHECK-NEXT: Field: var c: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var x: S3 in: $*S3 +// CHECK-NEXT: Field: var c: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var x: S3 of: $*S3 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var x: S3 in: $*S3 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var x: S3 of: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var x: S3 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var x: S3 of: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var x: S3 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @many_struct_allocs : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S2, var, name "a" // users: %6, %18 @@ -248,7 +248,7 @@ bb0: // CHECK: #0 store // CHECK-NEXT: alloc_stack $S5, var, name "b"{{.*}} // users: %7, %4 // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var a: SelfLoop of: $*SelfLoop] +// CHECK-NEXT: Field: var a: SelfLoop in: $*SelfLoop] sil hidden @self_loop : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S5, var, name "b" // users: %4, %7 diff --git a/test/SILOptimizer/lslocation_reduction.sil b/test/SILOptimizer/lslocation_reduction.sil index 16a4e0eedad98..eb239ccb5b9bd 100644 --- a/test/SILOptimizer/lslocation_reduction.sil +++ b/test/SILOptimizer/lslocation_reduction.sil @@ -104,11 +104,11 @@ bb0(%0 : $B): // CHECK: #0 store // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int] +// CHECK-NEXT: Field: var a: Int in: $*Int] // CHECK: #1 store // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int] +// CHECK-NEXT: Field: var a: Int in: $*Int] sil @store_after_store_struct : $@convention(thin) () -> () { %1 = alloc_stack $S1 %9 = integer_literal $Builtin.Int64, 0 // user: %10 diff --git a/test/SILOptimizer/lslocation_type_only_expansion.sil b/test/SILOptimizer/lslocation_type_only_expansion.sil index f1f45e75e546f..1d759956a61d5 100644 --- a/test/SILOptimizer/lslocation_type_only_expansion.sil +++ b/test/SILOptimizer/lslocation_type_only_expansion.sil @@ -82,23 +82,23 @@ sil @S6_init : $@convention(thin) (@thin S6.Type) -> S6 // CHECK-LABEL: @test_struct_type_expansion // CHECK: #0 store // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: Projection Path [$*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @test_struct_type_expansion : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S1, var, name "a" // users: %5, %12 @@ -140,23 +140,23 @@ bb0: // CHECK-LABEL: test_struct_and_class_slot // CHECK: #0 store // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var b: C1 of: $*C1] +// CHECK-NEXT: Field: var b: C1 in: $*C1] // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 -// CHECK-NEXT: Field: var c: C1 of: $*C1] +// CHECK-NEXT: Field: var c: C1 in: $*C1] sil hidden @test_struct_and_class_slot : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S3, var, name "a" // users: %4, %7 @@ -176,23 +176,23 @@ bb0: // CHECK-LABEL: test_tuple // CHECK: #0 store // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var c: (Int, Int, S1) of: $*(Int, Int, S1) -// CHECK-NEXT: Index: 2 into: $*S1 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) +// CHECK-NEXT: Index: 2 in: $*S1 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var c: (Int, Int, S1) of: $*(Int, Int, S1) -// CHECK-NEXT: Index: 2 into: $*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) +// CHECK-NEXT: Index: 2 in: $*S1 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var c: (Int, Int, S1) of: $*(Int, Int, S1) -// CHECK-NEXT: Index: 1 into: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) +// CHECK-NEXT: Index: 1 in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 -// CHECK-NEXT: Field: var c: (Int, Int, S1) of: $*(Int, Int, S1) -// CHECK-NEXT: Index: 0 into: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) +// CHECK-NEXT: Index: 0 in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @test_tuple : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S4, var, name "x" // users: %4, %7 @@ -210,41 +210,41 @@ bb0: // CHECK-LABEL: tuple_test_with_reference // CHECK: #0 store // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 2 into: $*S3 -// CHECK-NEXT: Field: var b: C1 of: $*C1] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 2 in: $*S3 +// CHECK-NEXT: Field: var b: C1 in: $*C1] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 2 into: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var b: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 2 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var b: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 2 into: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var b: S1 of: $*S1 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 2 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var b: S1 in: $*S1 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 2 into: $*S3 -// CHECK-NEXT: Field: var a: S2 of: $*S2 -// CHECK-NEXT: Field: var a: Int of: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 2 in: $*S3 +// CHECK-NEXT: Field: var a: S2 in: $*S2 +// CHECK-NEXT: Field: var a: Int in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 2 into: $*S3 -// CHECK-NEXT: Field: var c: C1 of: $*C1] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 2 in: $*S3 +// CHECK-NEXT: Field: var c: C1 in: $*C1] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 1 into: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 1 in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 -// CHECK-NEXT: Field: var c: (Int, Int, S3) of: $*(Int, Int, S3) -// CHECK-NEXT: Index: 0 into: $*Int -// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +// CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) +// CHECK-NEXT: Index: 0 in: $*Int +// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @tuple_test_with_reference : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S5, var, name "x" // users: %4, %7 @@ -262,13 +262,13 @@ bb0: /// CHECK-LABEL: tuple_inside_struct /// CHECK: #0 store /// CHECK-NEXT: Projection Path [$*S6 -/// CHECK-NEXT: Field: var tuple: (Int, Int) of: $*(Int, Int) -/// CHECK-NEXT: Index: 1 into: $*Int -/// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +/// CHECK-NEXT: Field: var tuple: (Int, Int) in: $*(Int, Int) +/// CHECK-NEXT: Index: 1 in: $*Int +/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] /// CHECK-NEXT: Projection Path [$*S6 -/// CHECK-NEXT: Field: var tuple: (Int, Int) of: $*(Int, Int) -/// CHECK-NEXT: Index: 0 into: $*Int -/// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +/// CHECK-NEXT: Field: var tuple: (Int, Int) in: $*(Int, Int) +/// CHECK-NEXT: Index: 0 in: $*Int +/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @tuple_inside_struct : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S6, var, name "x" // users: %4, %7 @@ -292,7 +292,7 @@ bb0: /// CHECK-NOT: Int32 /// CHECK: #1 store /// CHECK-NEXT: Projection Path [$*Int -/// CHECK-NEXT: Field: var value: Int64 of: $*Builtin.Int64] +/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] sil hidden @enum_test : $@convention(thin) () -> () { bb0: %0 = alloc_stack $Example, var, name "ee" // users: %5, %11