Skip to content

Commit c7fd48a

Browse files
committed
Rework storage for conformance isolation
Switch over to split caching for the conformance isolation request, which optimizes for the common case where the conformance is nonisolated. Also put the explicit global actor TypeExpr* in an ASTContext side table, so we don't take a pointer's worth of storage in every conformance. For that side table, introduce a new "ASTContext::GlobalCache" that's there only for side tables, so we don't have to go add get/set operations to ASTContext and recompile the world every time we want to add a side table like this. Thanks, Slava!
1 parent 836fcbe commit c7fd48a

9 files changed

+125
-41
lines changed

include/swift/AST/ASTContext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ class ASTContext final {
279279
struct Implementation;
280280
Implementation &getImpl() const;
281281

282+
struct GlobalCache;
283+
284+
/// Retrieve a reference to the global cache within this ASTContext,
285+
/// which is a place where we can stash side tables without having to
286+
/// recompile the world every time we add a side table. See
287+
/// "swift/AST/ASTContextGlobalCache.h"
288+
GlobalCache &getGlobalCache() const;
289+
282290
friend ConstraintCheckerArenaRAII;
283291

284292
void operator delete(void *Data) throw();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- ASTContextGlobalCache.h - AST Context Cache ------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the ASTContext::GlobalCache type. DO NOT include this
14+
// header from any other header: it should only be included in those .cpp
15+
// files that need to access the side tables. There are no include guards to
16+
// force the issue.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "swift/AST/ASTContext.h"
21+
22+
namespace swift {
23+
24+
/// A collection of side tables associated with the ASTContext itself, meant
25+
// as
26+
struct ASTContext::GlobalCache {
27+
/// Mapping from normal protocol conformances to the explicitly-specified
28+
/// global actor isolations, e.g., when the conformance was spelled
29+
/// `@MainActor P` or similar.
30+
llvm::DenseMap<const NormalProtocolConformance *, TypeExpr *>
31+
conformanceExplicitGlobalActorIsolation;
32+
};
33+
34+
} // end namespace

include/swift/AST/ProtocolConformance.h

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
147147
SWIFT_INLINE_BITFIELD_EMPTY(RootProtocolConformance, ProtocolConformance);
148148

149149
SWIFT_INLINE_BITFIELD_FULL(NormalProtocolConformance, RootProtocolConformance,
150-
1+1+1+
150+
1+1+1+1+1+
151151
bitmax(NumProtocolConformanceOptions,8)+
152152
bitmax(NumProtocolConformanceStateBits,8)+
153153
bitmax(NumConformanceEntryKindBits,8),
@@ -161,6 +161,12 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
161161
/// this conformance.
162162
IsPreconcurrencyEffectful : 1,
163163

164+
/// Whether the computed actor isolation is nonisolated.
165+
IsComputedNonisolated : 1,
166+
167+
/// Whether there is an explicit global actor specified for this
168+
/// conformance.
169+
HasExplicitGlobalActor : 1,
164170
: NumPadBits,
165171

166172
/// Options.
@@ -555,9 +561,6 @@ class NormalProtocolConformance : public RootProtocolConformance,
555561
/// NominalTypeDecl that declared the conformance.
556562
DeclContext *Context;
557563

558-
/// The global actor isolation for this conformance, if there is one.
559-
TypeExpr *globalActorIsolation = nullptr;
560-
561564
NormalProtocolConformance *ImplyingConformance = nullptr;
562565

563566
/// The mapping of individual requirements in the protocol over to
@@ -582,8 +585,19 @@ class NormalProtocolConformance : public RootProtocolConformance,
582585

583586
void resolveLazyInfo() const;
584587

585-
/// Set up global actor isolation for this conformance.
586-
void setGlobalActorIsolation(Type globalActorType);
588+
/// Retrieve the explicitly-specified global actor isolation.
589+
TypeExpr *getExplicitGlobalActorIsolation() const;
590+
591+
// Record the explicitly-specified global actor isolation.
592+
void setExplicitGlobalActorIsolation(TypeExpr *typeExpr);
593+
594+
bool isComputedNonisolated() const {
595+
return Bits.NormalProtocolConformance.IsComputedNonisolated;
596+
}
597+
598+
void setComputedNonnisolated(bool value = true) {
599+
Bits.NormalProtocolConformance.IsComputedNonisolated = value;
600+
}
587601

588602
public:
589603
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
@@ -608,7 +622,9 @@ class NormalProtocolConformance : public RootProtocolConformance,
608622
Bits.NormalProtocolConformance.HasComputedAssociatedConformances = false;
609623
Bits.NormalProtocolConformance.SourceKind =
610624
unsigned(ConformanceEntryKind::Explicit);
611-
globalActorIsolation = options.getGlobalActorIsolationType();
625+
Bits.NormalProtocolConformance.IsComputedNonisolated = false;
626+
Bits.NormalProtocolConformance.HasExplicitGlobalActor = false;
627+
setExplicitGlobalActorIsolation(options.getGlobalActorIsolationType());
612628
}
613629

614630
/// Get the protocol being conformed to.
@@ -651,7 +667,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
651667

652668
ProtocolConformanceOptions getOptions() const {
653669
return ProtocolConformanceOptions(Bits.NormalProtocolConformance.Options,
654-
globalActorIsolation);
670+
getExplicitGlobalActorIsolation());
655671
}
656672

657673
/// Whether this is an "unchecked" conformance.

include/swift/AST/TypeCheckRequests.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,8 @@ class ConformanceHasEffectRequest :
478478
class ConformanceIsolationRequest :
479479
public SimpleRequest<ConformanceIsolationRequest,
480480
ActorIsolation(ProtocolConformance *),
481-
RequestFlags::Cached> {
481+
RequestFlags::SeparatelyCached |
482+
RequestFlags::SplitCached> {
482483
public:
483484
using SimpleRequest::SimpleRequest;
484485

@@ -490,8 +491,10 @@ class ConformanceIsolationRequest :
490491
evaluate(Evaluator &evaluator, ProtocolConformance *conformance) const;
491492

492493
public:
493-
// Caching.
494-
bool isCached() const;
494+
// Separate caching.
495+
bool isCached() const { return true; }
496+
std::optional<ActorIsolation> getCachedResult() const;
497+
void cacheResult(ActorIsolation result) const;
495498
};
496499

497500
/// Determine whether the given declaration is 'final'.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,8 @@ SWIFT_REQUEST(TypeChecker, ConformanceHasEffectRequest,
408408
bool(EffectKind, ProtocolConformanceRef),
409409
Cached, NoLocationInfo)
410410
SWIFT_REQUEST(TypeChecker, ConformanceIsolationRequest,
411-
ActorIsolation(ProtocolConformance *), Cached,
411+
ActorIsolation(ProtocolConformance *),
412+
SeparatelyCached | SplitCached,
412413
NoLocationInfo)
413414
SWIFT_REQUEST(TypeChecker, ResolveTypeRequest,
414415
Type (const TypeResolution *, TypeRepr *, GenericParamList *),

lib/AST/ASTContext.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ClangTypeConverter.h"
1919
#include "ForeignRepresentationInfo.h"
2020
#include "SubstitutionMapStorage.h"
21+
#include "swift/AST/ASTContextGlobalCache.h"
2122
#include "swift/ABI/MetadataValues.h"
2223
#include "swift/AST/AvailabilityContextStorage.h"
2324
#include "swift/AST/ClangModuleLoader.h"
@@ -270,6 +271,9 @@ struct ASTContext::Implementation {
270271

271272
llvm::BumpPtrAllocator Allocator; // used in later initializations
272273

274+
/// The global cache of side tables for random things.
275+
GlobalCache globalCache;
276+
273277
/// The set of cleanups to be called when the ASTContext is destroyed.
274278
std::vector<std::function<void(void)>> Cleanups;
275279

@@ -747,6 +751,10 @@ inline ASTContext::Implementation &ASTContext::getImpl() const {
747751
return *reinterpret_cast<Implementation*>(pointer + offset);
748752
}
749753

754+
ASTContext::GlobalCache &ASTContext::getGlobalCache() const {
755+
return getImpl().globalCache;
756+
}
757+
750758
void ASTContext::operator delete(void *Data) throw() {
751759
AlignedFree(Data);
752760
}

lib/AST/ProtocolConformance.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/ProtocolConformance.h"
1818
#include "ConformanceLookupTable.h"
1919
#include "swift/AST/ASTContext.h"
20+
#include "swift/AST/ASTContextGlobalCache.h"
2021
#include "swift/AST/ConformanceLookup.h"
2122
#include "swift/AST/Decl.h"
2223
#include "swift/AST/DistributedDecl.h"
@@ -474,24 +475,33 @@ void NormalProtocolConformance::resolveLazyInfo() const {
474475
loader->finishNormalConformance(mutableThis, LoaderContextData);
475476
}
476477

477-
void NormalProtocolConformance::setGlobalActorIsolation(Type globalActorType) {
478-
ASTContext &ctx = getDeclContext()->getASTContext();
479-
if (globalActorIsolation)
480-
globalActorIsolation->setType(MetatypeType::get(globalActorType));
481-
else
482-
globalActorIsolation = TypeExpr::createImplicit(globalActorType, ctx);
483-
484-
Bits.NormalProtocolConformance.Options |=
485-
static_cast<unsigned>(ProtocolConformanceFlags::GlobalActorIsolated);
486-
}
487-
488478
void NormalProtocolConformance::setLazyLoader(LazyConformanceLoader *loader,
489479
uint64_t contextData) {
490480
assert(!Loader && "already has a loader");
491481
Loader = loader;
492482
LoaderContextData = contextData;
493483
}
494484

485+
TypeExpr *NormalProtocolConformance::getExplicitGlobalActorIsolation() const {
486+
if (!Bits.NormalProtocolConformance.HasExplicitGlobalActor)
487+
return nullptr;
488+
489+
ASTContext &ctx = getDeclContext()->getASTContext();
490+
return ctx.getGlobalCache().conformanceExplicitGlobalActorIsolation[this];
491+
}
492+
493+
void
494+
NormalProtocolConformance::setExplicitGlobalActorIsolation(TypeExpr *typeExpr) {
495+
if (!typeExpr) {
496+
Bits.NormalProtocolConformance.HasExplicitGlobalActor = false;
497+
return;
498+
}
499+
500+
Bits.NormalProtocolConformance.HasExplicitGlobalActor = true;
501+
ASTContext &ctx = getDeclContext()->getASTContext();
502+
ctx.getGlobalCache().conformanceExplicitGlobalActorIsolation[this] = typeExpr;
503+
}
504+
495505
namespace {
496506
class PrettyStackTraceRequirement : public llvm::PrettyStackTraceEntry {
497507
const char *Action;

lib/AST/TypeCheckRequests.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,33 +1371,38 @@ void AssociatedConformanceRequest::cacheResult(
13711371
//----------------------------------------------------------------------------//
13721372
// ConformanceIsolationRequest computation.
13731373
//----------------------------------------------------------------------------//
1374-
bool ConformanceIsolationRequest::isCached() const {
1374+
std::optional<ActorIsolation>
1375+
ConformanceIsolationRequest::getCachedResult() const {
13751376
// We only want to cache for global-actor-isolated conformances. For
13761377
// everything else, which is nearly every conformance, this request quickly
13771378
// returns "nonisolated" so there is no point in caching it.
13781379
auto conformance = std::get<0>(getStorage());
13791380
auto rootNormal =
13801381
dyn_cast<NormalProtocolConformance>(conformance->getRootConformance());
13811382
if (!rootNormal)
1382-
return false;
1383+
return ActorIsolation::forNonisolated(false);
13831384

1384-
// We know this is nonisolated.
1385-
if (rootNormal->getOptions().contains(ProtocolConformanceFlags::Nonisolated))
1386-
return false;
1385+
// Was actor isolation non-isolated?
1386+
if (rootNormal->isComputedNonisolated())
1387+
return ActorIsolation::forNonisolated(false);
13871388

1388-
// Explicit global actor isolation needs to be checked.
1389-
if (rootNormal->globalActorIsolation)
1390-
return true;
1389+
ASTContext &ctx = rootNormal->getDeclContext()->getASTContext();
1390+
return ctx.evaluator.getCachedNonEmptyOutput(*this);
1391+
}
13911392

1392-
// If we might infer conformance isolation, then we should cache the result.
1393-
auto dc = rootNormal->getDeclContext();
1394-
auto nominal = dc->getSelfNominalTypeDecl();
1395-
if (nominal &&
1396-
((isa<ClassDecl>(nominal) && cast<ClassDecl>(nominal)->isActor()) ||
1397-
nominal->getSemanticAttrs().hasAttribute<NonisolatedAttr>()))
1398-
return false;
1393+
void ConformanceIsolationRequest::cacheResult(ActorIsolation result) const {
1394+
auto conformance = std::get<0>(getStorage());
1395+
auto rootNormal =
1396+
cast<NormalProtocolConformance>(conformance->getRootConformance());
1397+
1398+
// Common case: conformance is nonisolated.
1399+
if (result.isNonisolated()) {
1400+
rootNormal->setComputedNonnisolated();
1401+
return;
1402+
}
13991403

1400-
return true;
1404+
ASTContext &ctx = rootNormal->getDeclContext()->getASTContext();
1405+
ctx.evaluator.cacheNonEmptyOutput(*this, std::move(result));
14011406
}
14021407

14031408
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7890,7 +7890,7 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance
78907890

78917891
// If there is an explicitly-specified global actor on the isolation,
78927892
// resolve it and report it.
7893-
if (auto globalActorTypeExpr = rootNormal->globalActorIsolation) {
7893+
if (auto globalActorTypeExpr = rootNormal->getExplicitGlobalActorIsolation()) {
78947894
// If we don't already have a resolved global actor type, resolve it now.
78957895
Type globalActorType = globalActorTypeExpr->getInstanceType();
78967896
if (!globalActorType) {
@@ -7925,7 +7925,6 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance
79257925
nominal) {
79267926
auto nominalIsolation = getActorIsolation(nominal);
79277927
if (nominalIsolation.isMainActor()) {
7928-
rootNormal->setGlobalActorIsolation(nominalIsolation.getGlobalActor());
79297928
return nominalIsolation;
79307929
}
79317930
}

0 commit comments

Comments
 (0)