llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Ilya Biryukov (ilya-biryukov)

<details>
<summary>Changes</summary>

Deduplicating types via standard C++ can be a huge compile-time hog, we have 
observed that on some of the large targets we have internally this can be up to 
25%.

This builtin can be used to improve compilation times in places where the 
template metaprogramming like this cannot be avoided.

The lack of a release note is deliberate, there is a good chance we may want to 
remove this before release and instead go with a builtin that produces packs.

To follow a discussion about a more sophisticated version of this builtin that 
would produce a pack directly and for generalizations about sorting types based 
on mangling, follow #<!-- -->106730 and 
https://discourse.llvm.org/t/rfc-adding-builtin-for-deduplicating-type-lists/80986.

---
Full diff: https://github.com/llvm/llvm-project/pull/139730.diff


3 Files Affected:

- (modified) clang/include/clang/Basic/BuiltinTemplates.td (+5) 
- (modified) clang/lib/Sema/SemaTemplate.cpp (+27) 
- (added) clang/test/SemaTemplate/dedup-types-builtin.cpp (+75) 


``````````diff
diff --git a/clang/include/clang/Basic/BuiltinTemplates.td 
b/clang/include/clang/Basic/BuiltinTemplates.td
index d46ce063d2f7e..49eaadb4e8920 100644
--- a/clang/include/clang/Basic/BuiltinTemplates.td
+++ b/clang/include/clang/Basic/BuiltinTemplates.td
@@ -50,3 +50,8 @@ def __builtin_common_type : BuiltinTemplate<
    Template<[Class<"TypeMember">], "HasTypeMember">,
    Class<"HasNoTypeMember">,
    Class<"Ts", /*is_variadic=*/1>]>;
+
+// template <template<class...> class Template, class ...Args>
+def __builtin_dedup_types
+    : BuiltinTemplate<[Template<[Class<"", /*is_variadic=*/1>], "Template">,
+                       Class<"Args", /*is_variadic=*/1>]>;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 94f4c1c46c1fb..47ea4a6714913 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -17,7 +17,9 @@
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/TemplateBase.h"
 #include "clang/AST/TemplateName.h"
+#include "clang/AST/TypeOrdering.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -3331,6 +3333,31 @@ checkBuiltinTemplateIdType(Sema &SemaRef, 
BuiltinTemplateDecl *BTD,
     QualType HasNoTypeMember = Converted[2].getAsType();
     return HasNoTypeMember;
   }
+  case BTK__builtin_dedup_types: {
+    assert(Converted.size() == 2 &&
+           "__builtin_dedup_types expects 2 arguments");
+    TemplateArgument Tmpl = Converted[0];
+    TemplateArgument Ts = Converted[1];
+    assert(Tmpl.getKind() == clang::TemplateArgument::Template);
+    assert(Ts.getKind() == clang::TemplateArgument::Pack);
+    // Delay the computation until we can compute the final result. We choose
+    // not to remove the duplicates upfront before substitution to keep the 
code
+    // simple.
+    if (Tmpl.isDependent() || Ts.isDependent())
+      return QualType();
+    TemplateArgumentListInfo OutArgs;
+    llvm::SmallDenseSet<QualType> Seen;
+    // Synthesize a new template argument list, removing duplicates.
+    for (auto T : Ts.getPackAsArray()) {
+      assert(T.getKind() == clang::TemplateArgument::Type);
+      if (!Seen.insert(T.getAsType().getCanonicalType()).second)
+        continue;
+      OutArgs.addArgument(TemplateArgumentLoc(
+          T, SemaRef.Context.getTrivialTypeSourceInfo(T.getAsType())));
+    }
+    return SemaRef.CheckTemplateIdType(Tmpl.getAsTemplate(), TemplateLoc,
+                                       OutArgs);
+  }
   }
   llvm_unreachable("unexpected BuiltinTemplateDecl!");
 }
diff --git a/clang/test/SemaTemplate/dedup-types-builtin.cpp 
b/clang/test/SemaTemplate/dedup-types-builtin.cpp
new file mode 100644
index 0000000000000..608c73698fe18
--- /dev/null
+++ b/clang/test/SemaTemplate/dedup-types-builtin.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 %s -verify
+
+template <typename...> struct TypeList;
+
+// FIXME: better error message, note for the location of the builtin.
+static_assert(__is_same(
+  __builtin_dedup_types<TypeList, int, int*, int, double, float>,
+  TypeList<int, int*, double, float>));
+
+template <template<typename ...> typename Templ, typename ...Types>
+struct Dependent {
+  using empty_list = __builtin_dedup_types<Templ>;
+  using same = __builtin_dedup_types<Templ, Types...>;
+  using twice = __builtin_dedup_types<Templ, Types..., Types...>;
+  using dep_only_types = __builtin_dedup_types<TypeList, Types...>;
+  using dep_only_template = __builtin_dedup_types<Templ, int, double, int>;
+}; 
+
+// Check the reverse condition to make sure we see an error and not 
accidentally produced dependent expression.
+static_assert(!__is_same(Dependent<TypeList>::empty_list, TypeList<>)); // 
expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::same, TypeList<>)); // 
expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::twice, TypeList<>)); // 
expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::dep_only_types, TypeList<>)); // 
expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::dep_only_template, TypeList<int, 
double>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::empty_list, 
TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::same, 
TypeList<int*, double*>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::twice, 
TypeList<int*, double*>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, 
int*>::dep_only_types, TypeList<int*, double*>)); // expected-error {{static 
assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, 
int*>::dep_only_template, TypeList<int, double>)); // expected-error {{static 
assertion failed}}
+
+
+template <class ...T>
+using Twice = TypeList<T..., T...>;
+
+// FIXME: move this test into a template, add a test that doing expansions 
outside of templates is an error.
+static_assert(!__is_same(__builtin_dedup_types<Twice, int, double, int>, 
TypeList<int, double, int, double>)); // expected-error {{static assertion 
failed}}
+
+template <int...> struct IntList;
+// Wrong kinds of template arguments.
+// FIXME: make the error message below point at this file.
+__builtin_dedup_types<IntList, int>* wrong_template; // expected-error@* 
{{template argument for non-type template parameter must be an expression}}
+                                                     // expected-note@* 
{{template template argument has different template parameters than its 
corresponding template template parameter}}
+                                                     // expected-note@-5 
{{template parameter is declared here}}
+__builtin_dedup_types<TypeList, 1, 2, 3>* wrong_template_args; // 
expected-error  {{template argument for template type parameter must be a type}}
+                                                               // 
expected-note@* {{template parameter from hidden source}}
+__builtin_dedup_types<> not_enough_args; // expected-error {{too few template 
arguments}}
+                                         // expected-note@* {{template 
declaration from hidden source}}
+__builtin_dedup_types missing_template_args; // expected-error {{use of 
template '__builtin_dedup_types' requires template arguments}}
+                                             // expected-note@* {{template 
declaration from hidden source}}
+
+// Make sure various canonical / non-canonical type representations do not 
affect results
+// of the deduplication and the qualifiers do end up creating different types 
when C++ requires it.
+using Int = int;
+using CInt = const Int;
+using IntArray = Int[10];
+using CIntArray = Int[10];
+using IntPtr = int*;
+using CIntPtr = const int*;
+
+static_assert( 
+  !__is_same( // expected-error {{static assertion failed}}
+    __builtin_dedup_types<TypeList,
+      Int, int,
+      const int, const Int, CInt, const CInt,
+      IntArray, Int[10], int[10],
+      const IntArray, const int[10], CIntArray, const CIntArray,
+      IntPtr, int*,
+      const IntPtr, int* const,
+      CIntPtr, const int*,
+      const IntPtr*, int*const*,
+      CIntPtr*, const int**,
+      const CIntPtr*, const int* const*
+    >,
+    TypeList<int, const int, int[10], const int [10], int*, int* const, const 
int*, int*const *, const int**, const int*const*>),
+  "");

``````````

</details>


https://github.com/llvm/llvm-project/pull/139730
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to