Skip to content

Commit 1197768

Browse files
committed
[NFC] Cache objcImpl requests in ClangImporter
This reduces the memory overhead of objcImpl from one word per Decl to one bit per Decl, at the cost of making cache lookups slightly slower (but it will only be consulted once for non-objcImpl decls, which is by far the most common case).
1 parent 5edd379 commit 1197768

File tree

4 files changed

+59
-48
lines changed

4 files changed

+59
-48
lines changed

include/swift/AST/Decl.h

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
355355
// for the inline bitfields.
356356
union { uint64_t OpaqueBits;
357357

358-
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1,
358+
SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1+1+1,
359359
Kind : bitmax(NumDeclKindBits,8),
360360

361361
/// Whether this declaration is invalid.
@@ -381,7 +381,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
381381
Hoisted : 1,
382382

383383
/// Whether the set of semantic attributes has been computed.
384-
SemanticAttrsComputed : 1
384+
SemanticAttrsComputed : 1,
385+
386+
/// True if \c ObjCInterfaceAndImplementationRequest has been computed
387+
/// and did \em not find anything. This is the fast path where we can bail
388+
/// out without checking other caches or computing anything.
389+
LacksObjCInterfaceOrImplementation : 1
385390
);
386391

387392
SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
@@ -816,13 +821,6 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
816821
private:
817822
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
818823

819-
/// The imported Clang declaration representing the \c @_objcInterface for
820-
/// this declaration (or vice versa), or \c nullptr if there is none.
821-
///
822-
/// If \c this (an otherwise nonsensical value), the value has not yet been
823-
/// computed.
824-
Decl *CachedObjCImplementationDecl;
825-
826824
Decl(const Decl&) = delete;
827825
void operator=(const Decl&) = delete;
828826
SourceLoc getLocFromSource() const;
@@ -840,14 +838,15 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
840838
protected:
841839

842840
Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context)
843-
: Context(context), CachedObjCImplementationDecl(this) {
841+
: Context(context) {
844842
Bits.OpaqueBits = 0;
845843
Bits.Decl.Kind = unsigned(kind);
846844
Bits.Decl.Invalid = false;
847845
Bits.Decl.Implicit = false;
848846
Bits.Decl.FromClang = false;
849847
Bits.Decl.EscapedFromIfConfig = false;
850848
Bits.Decl.Hoisted = false;
849+
Bits.Decl.LacksObjCInterfaceOrImplementation = false;
851850
}
852851

853852
/// Get the Clang node associated with this declaration.
@@ -1179,18 +1178,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
11791178
/// \seeAlso ExtensionDecl::isObjCInterface()
11801179
Decl *getObjCImplementationDecl() const;
11811180

1182-
llvm::Optional<Decl *> getCachedObjCImplementationDecl() const {
1183-
if (CachedObjCImplementationDecl == this)
1184-
return llvm::None;
1185-
return CachedObjCImplementationDecl;
1181+
bool getCachedLacksObjCInterfaceOrImplementation() const {
1182+
return Bits.Decl.LacksObjCInterfaceOrImplementation;
11861183
}
11871184

1188-
void setCachedObjCImplementationDecl(Decl *decl) {
1189-
assert((CachedObjCImplementationDecl == this
1190-
|| CachedObjCImplementationDecl == decl)
1191-
&& "can't change CachedObjCInterfaceDecl once it's computed");
1192-
assert(decl != this && "can't form circular reference");
1193-
CachedObjCImplementationDecl = decl;
1185+
void setCachedLacksObjCInterfaceOrImplementation(bool value) {
1186+
Bits.Decl.LacksObjCInterfaceOrImplementation = value;
11941187
}
11951188

11961189
/// Return the GenericContext if the Decl has one.

include/swift/ClangImporter/ClangImporter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ typedef llvm::PointerUnion<const clang::Decl *, const clang::MacroInfo *,
132132
class ClangImporter final : public ClangModuleLoader {
133133
friend class ClangModuleUnit;
134134

135+
// Make requests in the ClangImporter zone friends so they can access `Impl`.
136+
#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \
137+
friend class Name;
138+
#include "swift/ClangImporter/ClangImporterTypeIDZone.def"
139+
#undef SWIFT_REQUEST
140+
135141
public:
136142
class Implementation;
137143

lib/ClangImporter/ClangImporterRequests.cpp

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/AST/SourceFile.h"
1919
#include "swift/ClangImporter/ClangImporterRequests.h"
2020
#include "swift/Subsystems.h"
21+
#include "ImporterImpl.h"
2122

2223
using namespace swift;
2324

@@ -27,49 +28,56 @@ ObjCInterfaceAndImplementationRequest::getCachedResult() const {
2728
if (!passedDecl)
2829
return {};
2930

30-
auto cachedDecl = passedDecl->getCachedObjCImplementationDecl();
31-
32-
// !cachedDecl means that no decl has been cached and we need to evaluate the
33-
// request.
34-
if (!cachedDecl)
35-
return llvm::None;
36-
37-
// nullptr cachedDecl means that the lack of a decl was cached.
38-
else if (!*cachedDecl)
31+
if (!passedDecl->getCachedLacksObjCInterfaceOrImplementation())
32+
// We've computed this request and found that this is a normal declaration.
3933
return {};
4034

41-
// A decl was cached! Arbitrarily guess that we looked up the implementation
42-
// from the interface.
43-
ObjCInterfaceAndImplementation result{passedDecl, *cachedDecl};
44-
45-
// An imported decl can only be an interface; a native decl can only be an
46-
// implementation. If `implementationDecl` has a Clang node, we must have
47-
// looked up the interface from the implementation.
48-
if (result.implementationDecl->hasClangNode())
49-
std::swap(result.interfaceDecl, result.implementationDecl);
35+
// Either we've computed this request and cached a result in the ImporterImpl,
36+
// or we haven't computed this request. Check the caches.
37+
auto &ctx = passedDecl->getASTContext();
38+
auto importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
39+
auto &impl = importer->Impl;
40+
41+
if (passedDecl->hasClangNode()) {
42+
// `passedDecl` *could* be an interface.
43+
auto iter = impl.ImplementationsByInterface.find(passedDecl);
44+
if (iter != impl.ImplementationsByInterface.end())
45+
return ObjCInterfaceAndImplementation(passedDecl, iter->second);
46+
} else {
47+
// `passedDecl` *could* be an implementation.
48+
auto iter = impl.InterfacesByImplementation.find(passedDecl);
49+
if (iter != impl.InterfacesByImplementation.end())
50+
return ObjCInterfaceAndImplementation(iter->second, passedDecl);
51+
}
5052

51-
return result;
53+
// Nothing in the caches, so we must need to compute this.
54+
return llvm::None;
5255
}
5356

5457
void ObjCInterfaceAndImplementationRequest::
5558
cacheResult(ObjCInterfaceAndImplementation value) const {
56-
Decl *decl = std::get<0>(getStorage());
59+
Decl *passedDecl = std::get<0>(getStorage());
5760

5861
if (!value) {
59-
// `decl` is neither an interface nor an implementation.
60-
decl->setCachedObjCImplementationDecl(nullptr);
62+
// `decl` is neither an interface nor an implementation; remember this.
63+
passedDecl->setCachedLacksObjCInterfaceOrImplementation(true);
6164
return;
6265
}
6366

6467
// Cache computed pointers from implementations to interfaces.
65-
value.interfaceDecl->
66-
setCachedObjCImplementationDecl(value.implementationDecl);
67-
value.implementationDecl->
68-
setCachedObjCImplementationDecl(value.interfaceDecl);
68+
auto &ctx = passedDecl->getASTContext();
69+
auto importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
70+
auto &impl = importer->Impl;
71+
72+
impl.ImplementationsByInterface.insert({ value.interfaceDecl,
73+
value.implementationDecl });
74+
impl.InterfacesByImplementation.insert({ value.implementationDecl,
75+
value.interfaceDecl });
6976

7077
// If this was a duplicate implementation, cache a null so we don't recompute.
71-
if (decl != value.interfaceDecl && decl != value.implementationDecl)
72-
decl->setCachedObjCImplementationDecl(nullptr);
78+
if (!passedDecl->hasClangNode() && passedDecl != value.implementationDecl) {
79+
passedDecl->setCachedLacksObjCInterfaceOrImplementation(true);
80+
}
7381
}
7482

7583
// Define request evaluation functions for each of the name lookup requests.

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
585585
// Mapping from imported types to their raw value types.
586586
llvm::DenseMap<const NominalTypeDecl *, Type> RawTypes;
587587

588+
// Caches used by ObjCInterfaceAndImplementationRequest.
589+
llvm::DenseMap<Decl *, Decl *> ImplementationsByInterface;
590+
llvm::DenseMap<Decl *, Decl *> InterfacesByImplementation;
591+
588592
clang::CompilerInstance *getClangInstance() {
589593
return Instance.get();
590594
}

0 commit comments

Comments
 (0)