Skip to content

Request evaluator split caching #75015

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 12 commits into from
Jul 7, 2024
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
106 changes: 101 additions & 5 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
// for the inline bitfields.
union { uint64_t OpaqueBits;

SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1+1+1+1+1,
Kind : bitmax(NumDeclKindBits,8),

/// Whether this declaration is invalid.
Expand Down Expand Up @@ -388,7 +388,23 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// True if \c ObjCInterfaceAndImplementationRequest has been computed
/// and did \em not find anything. This is the fast path where we can bail
/// out without checking other caches or computing anything.
LacksObjCInterfaceOrImplementation : 1
LacksObjCInterfaceOrImplementation : 1,

/// True if we're in the common case where the ExpandMemberAttributeMacros
/// request returned an empty array.
NoMemberAttributeMacros : 1,

/// True if we're in the common case where the ExpandPeerMacroRequest
/// request returned an empty array.
NoPeerMacros : 1,

/// True if we're in the common case where the GlobalActorAttributeRequest
/// request returned a pair of null pointers.
NoGlobalActorAttribute : 1,

/// True if we're in the common case where the SPIGroupsRequest
/// request returned an empty array of identifiers.
NoSPIGroups : 1
);

SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
Expand Down Expand Up @@ -436,7 +452,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
IsStatic : 1
);

SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1+1+1,
/// Encodes whether this is a 'let' binding.
Introducer : 2,

Expand All @@ -454,7 +470,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
IsPropertyWrapperBackingProperty : 1,

/// Whether this is a lazily top-level global variable from the main file.
IsTopLevelGlobal : 1
IsTopLevelGlobal : 1,

/// Whether this variable has no attached property wrappers.
NoAttachedPropertyWrappers : 1,

/// Whether this variable has no property wrapper auxiliary variables.
NoPropertyWrapperAuxiliaryVariables : 1
);

SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits,
Expand Down Expand Up @@ -504,7 +526,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {

/// Whether this function is a distributed thunk for a distributed
/// function or computed property.
DistributedThunk: 1
DistributedThunk : 1
);

SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl,
Expand Down Expand Up @@ -827,6 +849,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
friend class MemberLookupTable;
friend class DeclDeserializer;
friend class RawCommentRequest;
friend class ExpandMemberAttributeMacros;
friend class ExpandPeerMacroRequest;
friend class GlobalActorAttributeRequest;
friend class SPIGroupsRequest;

private:
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
Expand All @@ -845,6 +871,38 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// Directly set the invalid bit
void setInvalidBit();

bool hasNoMemberAttributeMacros() const {
return Bits.Decl.NoMemberAttributeMacros;
}

void setHasNoMemberAttributeMacros() {
Bits.Decl.NoMemberAttributeMacros = true;
}

bool hasNoPeerMacros() const {
return Bits.Decl.NoPeerMacros;
}

void setHasNoPeerMacros() {
Bits.Decl.NoPeerMacros = true;
}

bool hasNoGlobalActorAttribute() const {
return Bits.Decl.NoGlobalActorAttribute;
}

void setHasNoGlobalActorAttribute() {
Bits.Decl.NoGlobalActorAttribute = true;
}

bool hasNoSPIGroups() const {
return Bits.Decl.NoSPIGroups;
}

void setHasNoSPIGroups() {
Bits.Decl.NoSPIGroups = true;
}

protected:

Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context)
Expand All @@ -857,6 +915,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
Bits.Decl.EscapedFromIfConfig = false;
Bits.Decl.Hoisted = false;
Bits.Decl.LacksObjCInterfaceOrImplementation = false;
Bits.Decl.NoMemberAttributeMacros = false;
Bits.Decl.NoGlobalActorAttribute = false;
Bits.Decl.NoSPIGroups = false;
}

/// Get the Clang node associated with this declaration.
Expand Down Expand Up @@ -2749,6 +2810,10 @@ class ValueDecl : public Decl {
/// allows the entity to be replaced at runtime.
unsigned isDynamic : 1;

/// Whether the DynamicallyReplacedDeclRequest request was evaluated and
/// output a null pointer.
unsigned noDynamicallyReplacedDecl : 1;

/// Whether the "isFinal" bit has been computed yet.
unsigned isFinalComputed : 1;

Expand All @@ -2762,6 +2827,13 @@ class ValueDecl : public Decl {
/// Whether this declaration produces an implicitly unwrapped
/// optional result.
unsigned isIUO : 1;

/// Whether we're in the common case where the ActorIsolationRequest
/// request returned ActorIsolation::forUnspecified().
unsigned noActorIsolation : 1;

/// Whether we've evaluated the ApplyAccessNoteRequest.
unsigned accessNoteApplied : 1;
} LazySemanticInfo = { };

friend class DynamicallyReplacedDeclRequest;
Expand All @@ -2772,6 +2844,9 @@ class ValueDecl : public Decl {
friend class IsImplicitlyUnwrappedOptionalRequest;
friend class InterfaceTypeRequest;
friend class CheckRedeclarationRequest;
friend class ActorIsolationRequest;
friend class DynamicallyReplacedDeclRequest;
friend class ApplyAccessNoteRequest;
friend class Decl;
SourceLoc getLocFromSource() const { return NameLoc; }
protected:
Expand Down Expand Up @@ -6090,8 +6165,27 @@ enum class PropertyWrapperSynthesizedPropertyKind {
/// VarDecl - 'var' and 'let' declarations.
class VarDecl : public AbstractStorageDecl {
friend class NamingPatternRequest;
friend class AttachedPropertyWrappersRequest;
friend class PropertyWrapperAuxiliaryVariablesRequest;

NamedPattern *NamingPattern = nullptr;

bool hasNoAttachedPropertyWrappers() const {
return Bits.VarDecl.NoAttachedPropertyWrappers;
}

void setHasNoAttachedPropertyWrappers() {
Bits.VarDecl.NoAttachedPropertyWrappers = true;
}

bool hasNoPropertyWrapperAuxiliaryVariables() const {
return Bits.VarDecl.NoPropertyWrapperAuxiliaryVariables;
}

void setHasNoPropertyWrapperAuxiliaryVariables() {
Bits.VarDecl.NoPropertyWrapperAuxiliaryVariables = true;
}

public:
enum class Introducer : uint8_t {
Let = 0,
Expand Down Expand Up @@ -7416,6 +7510,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
friend class ParseAbstractFunctionBodyRequest;
friend class TypeCheckFunctionBodyRequest;
friend class IsFunctionBodySkippedRequest;
friend class LifetimeDependenceInfoRequest;

CaptureInfo Captures;

Expand All @@ -7431,6 +7526,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
struct {
unsigned NeedsNewVTableEntryComputed : 1;
unsigned NeedsNewVTableEntry : 1;
unsigned NoLifetimeDependenceInfo : 1;
} LazySemanticInfo = { };

AbstractFunctionDecl(DeclKind Kind, DeclContext *Parent, DeclName Name,
Expand Down
27 changes: 27 additions & 0 deletions include/swift/AST/Evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,31 @@ class Evaluator {
recorder.clearRequest<Request>(request);
}

/// Store a value in the request evaluator's cache for a split-cached request.
/// The request chooses to do this itself for storing some suitable definition of
/// "non-empty" value.
template<typename Request,
typename std::enable_if<Request::hasSplitCache>::type* = nullptr>
void cacheNonEmptyOutput(const Request &request,
typename Request::OutputType &&output) {
bool inserted = cache.insert<Request>(request, std::move(output));
assert(inserted && "Request result was already cached");
(void) inserted;
}

/// Consults the request evaluator's cache for a split-cached request.
/// The request should perform this check after consulting it's own optimized
/// representation for storing an empty value.
template<typename Request,
typename std::enable_if<Request::hasSplitCache>::type* = nullptr>
std::optional<typename Request::OutputType>
getCachedNonEmptyOutput(const Request &request) {
auto found = cache.find_as(request);
if (found == cache.end<Request>())
return std::nullopt;
return found->second;
}

/// Clear the cache stored within this evaluator.
///
/// Note that this does not clear the caches of requests that use external
Expand All @@ -283,6 +308,8 @@ class Evaluator {
return activeRequests.count(ActiveRequest(request));
}

void dump(llvm::raw_ostream &out) { cache.dump(out); }

private:
/// Diagnose a cycle detected in the evaluation of the given
/// request.
Expand Down
19 changes: 17 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
Kind : 2
);

SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1,
/// True if closure parameters were synthesized from anonymous closure
/// variables.
HasAnonymousClosureVars : 1,
Expand All @@ -284,7 +284,11 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
IsolatedByPreconcurrency : 1,

/// True if this is a closure literal that is passed to a sending parameter.
IsPassedToSendingParameter : 1
IsPassedToSendingParameter : 1,

/// True if we're in the common case where the GlobalActorAttributeRequest
/// request returned a pair of null pointers.
NoGlobalActorAttribute : 1
);

SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
Expand Down Expand Up @@ -4124,6 +4128,16 @@ class ClosureExpr : public AbstractClosureExpr {
/// The body of the closure.
BraceStmt *Body;

friend class GlobalActorAttributeRequest;

bool hasNoGlobalActorAttribute() const {
return Bits.ClosureExpr.NoGlobalActorAttribute;
}

void setHasNoGlobalActorAttribute() {
Bits.ClosureExpr.NoGlobalActorAttribute = true;
}

public:
ClosureExpr(const DeclAttributes &attributes,
SourceRange bracketRange, VarDecl *capturedSelfDecl,
Expand All @@ -4143,6 +4157,7 @@ class ClosureExpr : public AbstractClosureExpr {
Bits.ClosureExpr.ImplicitSelfCapture = false;
Bits.ClosureExpr.InheritActorContext = false;
Bits.ClosureExpr.IsPassedToSendingParameter = false;
Bits.ClosureExpr.NoGlobalActorAttribute = false;
}

SourceRange getSourceRange() const;
Expand Down
55 changes: 50 additions & 5 deletions include/swift/AST/RequestCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "swift/AST/DependencyCollector.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

#ifndef SWIFT_AST_REQUEST_CACHE_H
#define SWIFT_AST_REQUEST_CACHE_H
Expand Down Expand Up @@ -162,14 +163,22 @@ class RequestKey<Request, typename detail::TupleHasDenseMapInfo<
class PerRequestCache {
void *Storage;
std::function<void(void *)> Deleter;
std::function<void(llvm::raw_ostream &out, void *)> Dumper;

PerRequestCache(void *storage, std::function<void(void *)> deleter)
: Storage(storage), Deleter(deleter) {}
PerRequestCache(void *storage,
std::function<void(void *)> deleter,
std::function<void(llvm::raw_ostream &out, void *)> dumper)
: Storage(storage), Deleter(deleter), Dumper(dumper) {}

public:
PerRequestCache() : Storage(nullptr), Deleter([](void *) {}) {}
PerRequestCache()
: Storage(nullptr),
Deleter([](void *) {}),
Dumper([](llvm::raw_ostream &, void *) {}) {}
PerRequestCache(PerRequestCache &&other)
: Storage(other.Storage), Deleter(std::move(other.Deleter)) {
: Storage(other.Storage),
Deleter(std::move(other.Deleter)),
Dumper(std::move(other.Dumper)) {
other.Storage = nullptr;
}

Expand All @@ -190,7 +199,17 @@ class PerRequestCache {
llvm::DenseMap<RequestKey<Request>,
typename Request::OutputType>;
return PerRequestCache(new Map(),
[](void *ptr) { delete static_cast<Map *>(ptr); });
[](void *ptr) { delete static_cast<Map *>(ptr); },
[](llvm::raw_ostream &out, void *storage) {
out << TypeID<Request>::getName() << "\t";
if (auto *map = static_cast<Map *>(storage)) {
out << map->size() << "\t"
<< llvm::capacity_in_bytes(*map);
} else {
out << "0\t0";
}
out << "\n";
});
}

template <typename Request>
Expand All @@ -204,11 +223,28 @@ class PerRequestCache {
return static_cast<Map *>(Storage);
}

template <typename Request>
std::pair<size_t, size_t>
size() const {
using Map =
llvm::DenseMap<RequestKey<Request>,
typename Request::OutputType>;
if (!Storage)
return std::make_pair(0, 0);

auto map = static_cast<Map *>(Storage);
return std::make_pair(map.size(), llvm::capacity_in_bytes(map));
}

bool isNull() const { return !Storage; }
~PerRequestCache() {
if (Storage)
Deleter(Storage);
}

void dump(llvm::raw_ostream &out) {
Dumper(out, Storage);
}
};

/// Data structure for caching results of requests. Sharded by the type ID
Expand Down Expand Up @@ -275,6 +311,15 @@ class RequestCache {
void clear() {
#define SWIFT_TYPEID_ZONE(Name, Id) Name##ZoneCache.clear();
#include "swift/Basic/TypeIDZones.def"
#undef SWIFT_TYPEID_ZONE
}

void dump(llvm::raw_ostream &out) {
#define SWIFT_TYPEID_ZONE(Name, Id) \
for (auto &entry : Name##ZoneCache) { \
entry.dump(out); \
}
#include "swift/Basic/TypeIDZones.def"
#undef SWIFT_TYPEID_ZONE
}
};
Expand Down
Loading