[clang] [clang][AST] Hash `AttributedType`'s `Attr` by Content (PR #200961)
Qiongsi Wu via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 4 09:21:07 PDT 2026
https://github.com/qiongsiwu updated https://github.com/llvm/llvm-project/pull/200961
>From b2ffa916c4b4f43183fb90a47548f08e2986882b Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Mon, 1 Jun 2026 14:54:21 -0700
Subject: [PATCH 1/3] Fixing attributed type deduplication.
---
clang/include/clang/AST/Attr.h | 12 ++++++++++++
clang/include/clang/AST/TypeBase.h | 8 +-------
clang/include/clang/Basic/Attr.td | 5 +++++
clang/lib/AST/Type.cpp | 11 +++++++++++
.../AST/attributed-type-dedup-nullability.m | 18 ++++++++++++++++++
.../AST/attributed-type-dedup-swift-attr.m | 15 +++++++++++++++
6 files changed, 62 insertions(+), 7 deletions(-)
create mode 100644 clang/test/AST/attributed-type-dedup-nullability.m
create mode 100644 clang/test/AST/attributed-type-dedup-swift-attr.m
diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index 0f9fc01391c30..df2da85cf6402 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -81,6 +81,8 @@ class Attr : public AttributeCommonInfo {
return ::operator delete(Ptr, C, Alignment);
}
+ virtual ~Attr() = default;
+
protected:
Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed)
@@ -120,6 +122,16 @@ class Attr : public AttributeCommonInfo {
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
static StringRef getDocumentation(attr::Kind);
+
+ // Contribute Attr-specific content to a FoldingSetNodeID for callers like
+ // AttributedType::Profile that dedupe nodes by Attr identity. The default
+ // is empty: AttributedType::Profile already hashes attrKind (and
+ // asserts attr->getKind() == attrKind), so a no-op default makes stateless
+ // Attrs (nullability, ARC ownership, calling conventions, ...) dedupe by
+ // (kind, modified, equivalent) — pointer identity of the Attr is ignored.
+ // Override this in subclasses whose arguments meaningfully distinguish
+ // the wrapped type (e.g. SwiftAttrAttr hashes its string payload).
+ virtual void Profile(llvm::FoldingSetNodeID &ID) const {}
};
class TypeAttr : public Attr {
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c64eee11fd91e..cce7af88181cd 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -6746,13 +6746,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
}
static void Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
- QualType modified, QualType equivalent,
- const Attr *attr) {
- ID.AddInteger(attrKind);
- ID.AddPointer(modified.getAsOpaquePtr());
- ID.AddPointer(equivalent.getAsOpaquePtr());
- ID.AddPointer(attr);
- }
+ QualType modified, QualType equivalent, const Attr *attr);
static bool classof(const Type *T) {
return T->getTypeClass() == Attributed;
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 32f72e8da960e..533f13d2ca879 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3196,6 +3196,11 @@ def SwiftAttr : DeclOrTypeAttr {
let Args = [StringArgument<"Attribute">];
let Documentation = [SwiftAttrDocs];
let PragmaAttributeSupport = 1;
+ let AdditionalMembers = [{
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddString(getAttribute());
+ }
+ }];
}
def SwiftBridge : InheritableAttr {
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 96a398aa21dad..711b216d81de7 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5338,6 +5338,17 @@ NullabilityKindOrNone AttributedType::stripOuterNullability(QualType &T) {
return std::nullopt;
}
+void AttributedType::Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
+ QualType modified, QualType equivalent,
+ const Attr *attr) {
+
+ ID.AddInteger(attrKind);
+ ID.AddPointer(modified.getAsOpaquePtr());
+ ID.AddPointer(equivalent.getAsOpaquePtr());
+ if (attr)
+ attr->Profile(ID);
+}
+
bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
if (!isIntegralType(Ctx) || isEnumeralType())
return false;
diff --git a/clang/test/AST/attributed-type-dedup-nullability.m b/clang/test/AST/attributed-type-dedup-nullability.m
new file mode 100644
index 0000000000000..9904ec00913d0
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-nullability.m
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fobjc-arc -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// All three non-null types below should share a single AttributedType node and
+// serialize as exactly one TYPE_ATTRIBUTED record.
+
+ at class NSString;
+
+#pragma clang assume_nonnull begin
+ at interface T
+- (NSString *)a;
+- (NSString *)b;
+- (NSString *)c;
+ at end
+#pragma clang assume_nonnull end
+
+// CHECK-COUNT-1: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-swift-attr.m b/clang/test/AST/attributed-type-dedup-swift-attr.m
new file mode 100644
index 0000000000000..d39778572eda5
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-swift-attr.m
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fobjc-arc -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// Checks that swift_attr deduplication works correctly.
+// The following test should generate two attributed types, not three.
+
+#define ATTR_A __attribute__((swift_attr("@A")))
+#define ATTR_B __attribute__((swift_attr("@B")))
+
+void f1(int * ATTR_A p);
+void f2(int * ATTR_A p);
+void f3(int * ATTR_B p);
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
>From 90f72abfe49e55b1c93eaa93640e9ba05ebd00f6 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Tue, 2 Jun 2026 10:52:23 -0700
Subject: [PATCH 2/3] Revise to make the fix more focused. Removed the virtual
method Profile from the base Attr class.
---
clang/include/clang/AST/Attr.h | 12 ------------
clang/include/clang/Basic/Attr.td | 2 +-
clang/lib/AST/Type.cpp | 6 +++---
3 files changed, 4 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index df2da85cf6402..0f9fc01391c30 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -81,8 +81,6 @@ class Attr : public AttributeCommonInfo {
return ::operator delete(Ptr, C, Alignment);
}
- virtual ~Attr() = default;
-
protected:
Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed)
@@ -122,16 +120,6 @@ class Attr : public AttributeCommonInfo {
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
static StringRef getDocumentation(attr::Kind);
-
- // Contribute Attr-specific content to a FoldingSetNodeID for callers like
- // AttributedType::Profile that dedupe nodes by Attr identity. The default
- // is empty: AttributedType::Profile already hashes attrKind (and
- // asserts attr->getKind() == attrKind), so a no-op default makes stateless
- // Attrs (nullability, ARC ownership, calling conventions, ...) dedupe by
- // (kind, modified, equivalent) — pointer identity of the Attr is ignored.
- // Override this in subclasses whose arguments meaningfully distinguish
- // the wrapped type (e.g. SwiftAttrAttr hashes its string payload).
- virtual void Profile(llvm::FoldingSetNodeID &ID) const {}
};
class TypeAttr : public Attr {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 533f13d2ca879..44f8ea6d16532 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3197,7 +3197,7 @@ def SwiftAttr : DeclOrTypeAttr {
let Documentation = [SwiftAttrDocs];
let PragmaAttributeSupport = 1;
let AdditionalMembers = [{
- void Profile(llvm::FoldingSetNodeID &ID) const override {
+ void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddString(getAttribute());
}
}];
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 711b216d81de7..c9d038ec308ef 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -14,6 +14,7 @@
#include "Linkage.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -5341,12 +5342,11 @@ NullabilityKindOrNone AttributedType::stripOuterNullability(QualType &T) {
void AttributedType::Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
QualType modified, QualType equivalent,
const Attr *attr) {
-
ID.AddInteger(attrKind);
ID.AddPointer(modified.getAsOpaquePtr());
ID.AddPointer(equivalent.getAsOpaquePtr());
- if (attr)
- attr->Profile(ID);
+ if (const auto *A = dyn_cast_or_null<SwiftAttrAttr>(attr))
+ A->Profile(ID);
}
bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
>From f7da8103e353f2bbcb3c2fab6c374b514b5d6c1d Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Wed, 3 Jun 2026 14:23:57 -0700
Subject: [PATCH 3/3] More general fix - an attribute's argument is taken into
account automatically when computing the profile.
---
clang/include/clang/AST/ASTContext.h | 3 +-
clang/include/clang/AST/Attr.h | 2 +
clang/include/clang/AST/TypeBase.h | 9 +-
clang/include/clang/Basic/Attr.td | 6 +-
clang/lib/AST/ASTContext.cpp | 10 ++-
clang/lib/AST/AttrImpl.cpp | 85 +++++++++++++++++++
clang/lib/AST/Type.cpp | 10 +--
.../AST/attributed-type-dedup-address-space.c | 9 ++
...attributed-type-dedup-annotate-type-args.c | 14 +++
.../AST/attributed-type-dedup-annotate-type.m | 12 +++
.../attributed-type-dedup-objc-ownership.m | 11 +++
clang/test/AST/attributed-type-dedup-pcs.c | 15 ++++
clang/utils/TableGen/ClangAttrEmitter.cpp | 43 ++++++++++
13 files changed, 214 insertions(+), 15 deletions(-)
create mode 100644 clang/test/AST/attributed-type-dedup-address-space.c
create mode 100644 clang/test/AST/attributed-type-dedup-annotate-type-args.c
create mode 100644 clang/test/AST/attributed-type-dedup-annotate-type.m
create mode 100644 clang/test/AST/attributed-type-dedup-objc-ownership.m
create mode 100644 clang/test/AST/attributed-type-dedup-pcs.c
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c952b8d46974c..e58e123cf95af 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -294,7 +294,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<DeducedTemplateSpecializationType>
DeducedTemplateSpecializationTypes;
mutable llvm::FoldingSet<AtomicType> AtomicTypes;
- mutable llvm::FoldingSet<AttributedType> AttributedTypes;
+ mutable llvm::ContextualFoldingSet<AttributedType, ASTContext &>
+ AttributedTypes;
mutable llvm::FoldingSet<PipeType> PipeTypes;
mutable llvm::FoldingSet<BitIntType> BitIntTypes;
mutable llvm::ContextualFoldingSet<DependentBitIntType, ASTContext &>
diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index 0f9fc01391c30..f204bb4faa9b7 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -116,6 +116,8 @@ class Attr : public AttributeCommonInfo {
bool isEquivalent(const Attr &Other,
StructuralEquivalenceContext &Context) const;
+ void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) const;
+
// Pretty print this attribute.
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index cce7af88181cd..e3141a82b54d2 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -6741,12 +6741,13 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
/// \returns the top-level nullability, if present.
static NullabilityKindOrNone stripOuterNullability(QualType &T);
- void Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getAttrKind(), ModifiedType, EquivalentType, Attribute);
+ void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) {
+ Profile(ID, Ctx, getAttrKind(), ModifiedType, EquivalentType, Attribute);
}
- static void Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
- QualType modified, QualType equivalent, const Attr *attr);
+ static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx,
+ Kind attrKind, QualType modified, QualType equivalent,
+ const Attr *attr);
static bool classof(const Type *T) {
return T->getTypeClass() == Attributed;
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 44f8ea6d16532..6ef9786ce5717 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -768,10 +768,13 @@ class Attr {
// Note: Any additional data members will leak and should be constructed
// externally on the ASTContext.
code AdditionalMembers = [{}];
+
+ string comparisonFn = "";
+ string profileFn = "";
+
// Any documentation that should be associated with the attribute. Since an
// attribute may be documented under multiple categories, more than one
// Documentation entry may be listed.
- string comparisonFn = "";
list<Documentation> Documentation;
}
@@ -897,6 +900,7 @@ def Aligned : InheritableAttr {
CustomKeyword<"_Alignas">]>,
Accessor<"isDeclspec",[Declspec<"align">]>];
let comparisonFn = "areAlignedAttrsEqual";
+ let profileFn = "profileAlignedAttr";
let Documentation = [Undocumented];
}
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a401a7471e6fc..f46672b086c12 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -937,9 +937,10 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
FunctionProtoTypes(this_(), FunctionProtoTypesLog2InitSize),
DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()),
DependentPackIndexingTypes(this_()), TemplateSpecializationTypes(this_()),
- DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()),
- DeducedTemplates(this_()), ArrayParameterTypes(this_()),
- CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts),
+ AttributedTypes(this_()), DependentBitIntTypes(this_()),
+ SubstTemplateTemplateParmPacks(this_()), DeducedTemplates(this_()),
+ ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()),
+ SourceMgr(SM), LangOpts(LOpts),
NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)),
XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles,
LangOpts.XRayNeverInstrumentFiles,
@@ -5741,7 +5742,8 @@ QualType ASTContext::getAttributedType(attr::Kind attrKind,
QualType equivalentType,
const Attr *attr) const {
llvm::FoldingSetNodeID id;
- AttributedType::Profile(id, attrKind, modifiedType, equivalentType, attr);
+ AttributedType::Profile(id, *this, attrKind, modifiedType, equivalentType,
+ attr);
void *insertPos = nullptr;
AttributedType *type = AttributedTypes.FindNodeOrInsertPos(id, insertPos);
diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp
index 3a379891016f8..c613f5a6690f8 100644
--- a/clang/lib/AST/AttrImpl.cpp
+++ b/clang/lib/AST/AttrImpl.cpp
@@ -376,4 +376,89 @@ bool areAlignedAttrsEqual(const AlignedAttr &A1, const AlignedAttr &A2,
}
} // namespace
+namespace {
+// Machinery to unique attributes based on the arguments.
+// The construction mirrors the equivalent testing code above.
+// The content of the arguments are added to the FoldingSetNodeID instance,
+// which allows the AttributedTypes to unique the attributes based on
+// the value of the arguments.
+
+#define USE_DEFAULT_PROFILE \
+ (std::is_same_v<T, StringRef> || std::is_same_v<T, VersionTuple> || \
+ std::is_same_v<T, IdentifierInfo *> || \
+ std::is_same_v<T, const IdentifierInfo *> || std::is_enum_v<T> || \
+ std::is_integral_v<T>)
+
+template <class T>
+typename std::enable_if_t<!USE_DEFAULT_PROFILE>
+profileAttrArg(llvm::FoldingSetNodeID &, const ASTContext &, T) {}
+
+template <class T>
+typename std::enable_if_t<USE_DEFAULT_PROFILE>
+profileAttrArg(llvm::FoldingSetNodeID &ID, const ASTContext &, T V) {
+ if constexpr (std::is_same_v<T, StringRef>)
+ ID.AddString(V);
+ else if constexpr (std::is_same_v<T, VersionTuple>) {
+ ID.AddInteger(V.getMajor());
+ ID.AddInteger(V.getMinor().value_or(0));
+ ID.AddInteger(V.getSubminor().value_or(0));
+ ID.AddInteger(V.getBuild().value_or(0));
+ } else if constexpr (std::is_same_v<T, IdentifierInfo *> ||
+ std::is_same_v<T, const IdentifierInfo *>)
+ ID.AddPointer(V);
+ else
+ ID.AddInteger(static_cast<long long>(V));
+}
+
+template <>
+inline void profileAttrArg<ParamIdx>(llvm::FoldingSetNodeID &ID,
+ const ASTContext &, ParamIdx P) {
+ // Mirror equalAttrArgs<ParamIdx>: invalid ParamIdx must agree.
+ ID.AddBoolean(P.isValid());
+ if (P.isValid())
+ ID.AddInteger(P.getASTIndex());
+}
+
+template <class T>
+inline void profileAttrArg(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx,
+ T *Begin, T *End) {
+ ID.AddInteger(End - Begin);
+ for (; Begin != End; ++Begin)
+ profileAttrArg(ID, Ctx, *Begin);
+}
+
+template <>
+inline void profileAttrArg<Attr *>(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Ctx, Attr *A) {
+ if (!A) {
+ ID.AddPointer(nullptr);
+ return;
+ }
+ ID.AddInteger(A->getKind());
+ A->Profile(ID, Ctx);
+}
+
+template <>
+inline void profileAttrArg<Expr *>(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Ctx, Expr *E) {
+ E->Profile(ID, Ctx, /*Canonical=*/true);
+}
+
+template <>
+inline void profileAttrArg<QualType>(llvm::FoldingSetNodeID &ID,
+ const ASTContext &, QualType T) {
+ ID.AddPointer(T.getCanonicalType().getAsOpaquePtr());
+}
+
+void profileAlignedAttr(const AlignedAttr &A, llvm::FoldingSetNodeID &ID,
+ const ASTContext &Ctx) {
+ ID.AddInteger(A.getSpellingListIndex());
+ ID.AddBoolean(A.isAlignmentExpr());
+ if (A.isAlignmentExpr())
+ profileAttrArg(ID, Ctx, A.getAlignmentExpr());
+ else
+ profileAttrArg(ID, Ctx, A.getAlignmentType()->getType());
+}
+} // namespace
+
#include "clang/AST/AttrImpl.inc"
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index c9d038ec308ef..55c3e5c3ead17 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5339,14 +5339,14 @@ NullabilityKindOrNone AttributedType::stripOuterNullability(QualType &T) {
return std::nullopt;
}
-void AttributedType::Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
- QualType modified, QualType equivalent,
- const Attr *attr) {
+void AttributedType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx,
+ Kind attrKind, QualType modified,
+ QualType equivalent, const Attr *attr) {
ID.AddInteger(attrKind);
ID.AddPointer(modified.getAsOpaquePtr());
ID.AddPointer(equivalent.getAsOpaquePtr());
- if (const auto *A = dyn_cast_or_null<SwiftAttrAttr>(attr))
- A->Profile(ID);
+ if (attr)
+ attr->Profile(ID, Ctx);
}
bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
diff --git a/clang/test/AST/attributed-type-dedup-address-space.c b/clang/test/AST/attributed-type-dedup-address-space.c
new file mode 100644
index 0000000000000..8372a21c4eb5a
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-address-space.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+__attribute__((address_space(1))) int *a;
+__attribute__((address_space(1))) int *b;
+__attribute__((address_space(2))) int *c;
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-annotate-type-args.c b/clang/test/AST/attributed-type-dedup-annotate-type-args.c
new file mode 100644
index 0000000000000..060dbfc9bf37e
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-annotate-type-args.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// annotate_type with VariadicExprArgument. The attribued types should
+// dedupe not only based on the string argument, but all of the variadic
+// arguments.
+
+int *[[clang::annotate_type("foo", 1)]] a;
+int *[[clang::annotate_type("foo", 1)]] b;
+int *[[clang::annotate_type("foo", 2)]] c;
+int *[[clang::annotate_type("foo")]] d;
+
+// CHECK-COUNT-3: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-annotate-type.m b/clang/test/AST/attributed-type-dedup-annotate-type.m
new file mode 100644
index 0000000000000..2c4bfff0aefbe
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-annotate-type.m
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// Only two unique string arguments are present. Correspondingly,
+// we should have two TYPE_ATTRIBUTED records.
+
+int *[[clang::annotate_type("foo")]] a;
+int *[[clang::annotate_type("foo")]] b;
+int *[[clang::annotate_type("bar")]] c;
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-objc-ownership.m b/clang/test/AST/attributed-type-dedup-objc-ownership.m
new file mode 100644
index 0000000000000..4d3d69373a1d5
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-objc-ownership.m
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -fobjc-arc -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+ at class NSString;
+
+__attribute__((objc_ownership(strong))) NSString *a;
+__attribute__((objc_ownership(strong))) NSString *b;
+__attribute__((objc_ownership(none))) NSString *c;
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-pcs.c b/clang/test/AST/attributed-type-dedup-pcs.c
new file mode 100644
index 0000000000000..cfd7b7d9f4658
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-pcs.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple arm-none-eabi -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// Pcs uses an EnumArgument and is encoded in the FunctionProtoType's
+// calling convention bits on ARM targets, so the equivalent type
+// discriminates aapcs vs aapcs-vfp. Two same-PCS uses must dedup; a
+// different-PCS use must be distinct. Requires an ARM triple — on x86 /
+// arm64-apple-darwin, pcs is silently ignored and no records are emitted.
+
+void __attribute__((pcs("aapcs"))) f1(void);
+void __attribute__((pcs("aapcs"))) f2(void);
+void __attribute__((pcs("aapcs-vfp"))) f3(void);
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 7df99d3beb11a..1eaec5f07c75e 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -287,6 +287,10 @@ namespace {
std::string S = std::string("get") + std::string(getUpperName()) + "()";
return getArgEqualityFn().str() + "(" + S + ", Other." + S + ", Context)";
}
+
+ virtual std::string emitAttrArgProfileCall() const {
+ return "profileAttrArg(ID, Ctx, get" + getUpperName().str() + "())";
+ }
};
class SimpleArgument : public Argument {
@@ -867,6 +871,11 @@ namespace {
GenIter(false, "end") + ", " + GenIter(true, "begin") + ", " +
GenIter(true, "end") + ", Context)";
}
+
+ std::string emitAttrArgProfileCall() const override {
+ std::string LN = getLowerName().str();
+ return "profileAttrArg(ID, Ctx, " + LN + "_begin(), " + LN + "_end())";
+ }
};
class VariadicOMPInteropInfoArgument : public VariadicArgument {
@@ -3266,6 +3275,22 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS,
OS << "}\n\n";
}
+ std::string ProfileSig = "Profile(llvm::FoldingSetNodeID &ID, "
+ "const ASTContext &Ctx) const";
+ if (Header) {
+ OS << " void " << ProfileSig << ";\n";
+ } else {
+ OS << "void " << R.getName() << "Attr::" << ProfileSig << " {\n";
+ std::string CustomFn = R.getValueAsString("profileFn").str();
+ if (CustomFn.empty()) {
+ for (const auto &ai : Args)
+ OS << " " << ai->emitAttrArgProfileCall() << ";\n";
+ } else {
+ OS << " " << CustomFn << "(*this, ID, Ctx);\n";
+ }
+ OS << "}\n\n";
+ }
+
if (Header) {
if (DelayedArgs) {
DelayedArgs->writeAccessors(OS);
@@ -3338,6 +3363,23 @@ static void emitEquivalenceFunction(const RecordKeeper &Records,
OS << "}\n\n";
}
+static void emitProfileFunction(const RecordKeeper &Records, raw_ostream &OS) {
+ OS << "void Attr::Profile(llvm::FoldingSetNodeID &ID, "
+ "const ASTContext &Ctx) const {\n";
+ OS << " switch (getKind()) {\n";
+ for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) {
+ const Record &R = *Attr;
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
+ OS << " case attr::" << R.getName() << ":\n";
+ OS << " return cast<" << R.getName()
+ << "Attr>(this)->Profile(ID, Ctx);\n";
+ }
+ OS << " }\n";
+ OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n";
+ OS << "}\n\n";
+}
+
// Emits the class method definitions for attributes.
void clang::EmitClangAttrImpl(const RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute classes' member function definitions", OS,
@@ -3374,6 +3416,7 @@ void clang::EmitClangAttrImpl(const RecordKeeper &Records, raw_ostream &OS) {
EmitFunc("printPretty(OS, Policy)");
emitEquivalenceFunction(Records, OS);
+ emitProfileFunction(Records, OS);
}
static void emitAttrList(raw_ostream &OS, StringRef Class,
More information about the cfe-commits
mailing list