zoecarver created this revision. zoecarver added reviewers: EricWF, eli.friedman, rsmith, craig.topper. Herald added subscribers: libcxx-commits, cfe-commits, jfb, christof. Herald added projects: clang, libc++.
This patch adds builtin type traits to transform reference types. Specifically, it adds `__add_lvalue_reference`, `__add_rvalue_reference`, and `__remove_reference`. The first two builtins speed up builds by around 3x while the last builtin only sees small improvements (we may be able to optimize it more, though). Once added to the standard library, this should make libc++ (and other code) much faster to compile. I tried to generalize as much of the builtin as possible so, the only functional difference between the three builtins is in the file `BuildUnaryTransformType`. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D67052 Files: clang/include/clang/AST/Type.h clang/include/clang/Basic/Specifiers.h clang/include/clang/Basic/TokenKinds.def clang/include/clang/Parse/Parser.h clang/include/clang/Sema/DeclSpec.h clang/lib/Format/FormatToken.cpp clang/lib/Lex/PPMacroExpansion.cpp clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Sema/DeclSpec.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaTemplateVariadic.cpp clang/lib/Sema/SemaType.cpp clang/test/SemaCXX/add_reference.cpp libcxx/test/libcxx/utilities/meta/stress_tests/lit.site.cfg libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_lvalue_reference.sh.cpp libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_rvalue_reference.sh.cpp libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_remove_reference.sh.cpp
Index: libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_remove_reference.sh.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_remove_reference.sh.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a dummy feature that prevents this test from running by default. + +// The table below compares the compile time and object size for each of the +// variants listed in the RUN script. +// +// Impl Compile Time Object Size +// ---------------------------------------------------------- +// new_remove_reference: 22.849 s 121 K +// std::remove_reference: 25.643 s 121 K +// +// RUN: %cxx %flags %compile_flags -c %s -o %S/orig.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 +// RUN: %cxx %flags %compile_flags -c %s -o %S/new.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 -DTEST_NEW + +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "template_cost_testing.h" + +template <int N> struct Arg { enum { value = 1 }; }; + +#ifdef TEST_NEW + +template <class T> +struct new_remove_reference +{ + typedef __remove_reference(T) type; +}; + +#define TEST_CASE_NOP() new_remove_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename new_remove_reference< Arg< __COUNTER__ > >::type, + +#else + +#define TEST_CASE_NOP() std::remove_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename std::remove_reference< Arg< __COUNTER__ > >::type, + +#endif + +int sink(...); + +int x = sink( + REPEAT_10000(TEST_CASE_NOP) + REPEAT_10000(TEST_CASE_NOP) 42 +); + +void Foo( REPEAT_10000(TEST_CASE_TYPE) int) { } + +void escape() { + +sink(&x); +sink(&Foo); +} + + Index: libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_rvalue_reference.sh.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_rvalue_reference.sh.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a dummy feature that prevents this test from running by default. + +// The table below compares the compile time and object size for each of the +// variants listed in the RUN script. +// +// Impl Compile Time Object Size +// ---------------------------------------------------------- +// new_add_rvalue_reference: 56.398 s 171 K +// std::add_rvalue_reference: 114.59 s 271 K +// +// RUN: %cxx %flags %compile_flags -c %s -o %S/orig.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 +// RUN: %cxx %flags %compile_flags -c %s -o %S/new.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 -DTEST_NEW + +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "template_cost_testing.h" + +template <int N> struct Arg { enum { value = 1 }; }; + +#ifdef TEST_NEW + +template <class T> +struct new_add_rvalue_reference +{ + typedef __add_rvalue_reference(T) type; +}; + +#define TEST_CASE_NOP() new_add_rvalue_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename new_add_rvalue_reference< Arg< __COUNTER__ > >::type, + +#else + +#define TEST_CASE_NOP() std::add_rvalue_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename std::add_rvalue_reference< Arg< __COUNTER__ > >::type, + +#endif + +int sink(...); + +int x = sink( + REPEAT_10000(TEST_CASE_NOP) + REPEAT_10000(TEST_CASE_NOP) 42 +); + +void Foo( REPEAT_10000(TEST_CASE_TYPE) int) { } + +void escape() { + +sink(&x); +sink(&Foo); +} + + Index: libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_lvalue_reference.sh.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/meta/stress_tests/stress_test_add_lvalue_reference.sh.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a dummy feature that prevents this test from running by default. + +// The table below compares the compile time and object size for each of the +// variants listed in the RUN script. +// +// Impl Compile Time Object Size +// ---------------------------------------------------------- +// new_add_lvalue_reference: 23,359.202 ms 171 K +// std::add_lvalue_reference: 73,160.138 ms 201 K +// +// RUN: %cxx %flags %compile_flags -c %s -o %S/orig.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 +// RUN: %cxx %flags %compile_flags -c %s -o %S/new.o -ggdb -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17 -DTEST_NEW + +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "template_cost_testing.h" + +template <int N> struct Arg { enum { value = 1 }; }; + +#ifdef TEST_NEW + +template <class T> +struct new_add_lvalue_reference +{ + typedef __add_lvalue_reference(T) type; +}; + +#define TEST_CASE_NOP() new_add_lvalue_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename new_add_lvalue_reference< Arg< __COUNTER__ > >::type, + +#else + +#define TEST_CASE_NOP() std::add_lvalue_reference< Arg< __COUNTER__ > >{}, +#define TEST_CASE_TYPE() typename std::add_lvalue_reference< Arg< __COUNTER__ > >::type, + +#endif + +int sink(...); + +int x = sink( + REPEAT_10000(TEST_CASE_NOP) + REPEAT_10000(TEST_CASE_NOP) 42 +); + +void Foo( REPEAT_10000(TEST_CASE_TYPE) int) { } + +void escape() { + +sink(&x); +sink(&Foo); +} + + Index: libcxx/test/libcxx/utilities/meta/stress_tests/lit.site.cfg =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/meta/stress_tests/lit.site.cfg @@ -0,0 +1,2 @@ +config.cxx_under_test = "/Users/zoe/Developer/llvm-apple/mono-build/bin/clang++" +lit_config.load_config(config, "/Users/zoe/Developer/llvm-apple/llvm-project/libcxx/test/lit.cfg") Index: clang/test/SemaCXX/add_reference.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/add_reference.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -std=c++11 %s + +template<class T> +struct test_lval_trait +{ + typedef __add_lvalue_reference(T) type; +}; + +template<class T> +struct test_lval +{ + static const bool value = __is_same(typename test_lval_trait<T>::type, T&) && + __is_same(typename test_lval_trait<T&>::type, T&) && + __is_same(typename test_lval_trait<T&&>::type, T&); +}; + +template<class T> +struct test_rval_trait +{ + typedef __add_rvalue_reference(T) type; +}; + +template<class T> +struct test_rval +{ + static const bool value = __is_same(typename test_rval_trait<T>::type, T&&) && + __is_same(typename test_rval_trait<T&>::type, T&&) && + __is_same(typename test_rval_trait<T&&>::type, T&&); +}; + +template<class T> +struct test_remove_ref_trait +{ + typedef __remove_reference(T) type; +}; + +template<class T> +struct test_remove_ref +{ + static const bool value = __is_same(typename test_remove_ref_trait<T>::type, T) && + __is_same(typename test_remove_ref_trait<T&>::type, T) && + __is_same(typename test_remove_ref_trait<T&&>::type, T); +}; + +template<class T> +struct test +{ + static const bool value = test_remove_ref<T>::value && + test_rval<T>::value && + test_lval<T>::value; +}; + +struct Foo { }; + +template<class T> +struct Bar { }; + +template<class T> +class Baz { }; + +static_assert(test<int>::value, ""); +static_assert(test<int[]>::value, ""); +static_assert(test<int[8]>::value, ""); +static_assert(test<Foo>::value, ""); +static_assert(test<Bar<int>>::value, ""); +static_assert(test<Baz<int>>::value, ""); Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -1251,6 +1251,22 @@ return OpenCLAccessAttr::Keyword_read_only; } +static UnaryTransformType::UTTKind +TSTToUnaryTransformType(DeclSpec::TST SwitchTST) { + switch (SwitchTST) { + case TST_addLValueReferenceType: + return UnaryTransformType::AddLValueType; + case TST_addRValueReferenceType: + return UnaryTransformType::AddRValueType; + case TST_removeReferenceType: + return UnaryTransformType::RemoveReferenceType; + case TST_underlyingType: + return UnaryTransformType::EnumUnderlyingType; + default: + assert(false && "Cannot map TST to unary transform type"); + } +} + /// Convert the specified declspec to the appropriate type /// object. /// \param state Specifies the declarator containing the declaration specifier @@ -1593,6 +1609,20 @@ declarator.setInvalidType(true); } break; + case DeclSpec::TST_addLValueReferenceType: + case DeclSpec::TST_addRValueReferenceType: + case DeclSpec::TST_removeReferenceType: + Result = S.GetTypeFromParser(DS.getRepAsType()); + assert(!Result.isNull() && + "Reference manipulation type transform may not have received a type."); + Result = S.BuildUnaryTransformType(Result, + TSTToUnaryTransformType(DS.getTypeSpecType()), + DS.getTypeSpecTypeLoc()); + if (Result.isNull()) { + declarator.setInvalidType(true); + } + break; + case DeclSpec::TST_auto: Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); @@ -5456,8 +5486,8 @@ TL.setUnderlyingTInfo(TInfo); } void VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { - // FIXME: This holds only because we only have one unary transform. - assert(DS.getTypeSpecType() == DeclSpec::TST_underlyingType); + // TODO: Should we check something like "IsUnaryTypeTransfrom(DS.getTypeSpecTypeLoc())"? + // assert(DS.getTypeSpecType() == DeclSpec::TST_underlyingType); TL.setKWLoc(DS.getTypeSpecTypeLoc()); TL.setParensRange(DS.getTypeofParensRange()); assert(DS.getRepAsType()); @@ -8409,6 +8439,21 @@ return Context.getUnaryTransformType(BaseType, Underlying, UnaryTransformType::EnumUnderlyingType); } + case UnaryTransformType::AddLValueType: { + QualType Underlying = Context.getLValueReferenceType(BaseType.getCanonicalType()); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::AddLValueType); + } + case UnaryTransformType::AddRValueType: { + QualType Underlying = Context.getRValueReferenceType(BaseType.getCanonicalType()); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::AddRValueType); + } + case UnaryTransformType::RemoveReferenceType: { + QualType Underlying = BaseType.getNonReferenceType(); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::RemoveReferenceType); + } } llvm_unreachable("unknown unary transform type"); } Index: clang/lib/Sema/SemaTemplateVariadic.cpp =================================================================== --- clang/lib/Sema/SemaTemplateVariadic.cpp +++ clang/lib/Sema/SemaTemplateVariadic.cpp @@ -838,6 +838,9 @@ case TST_typename: case TST_typeofType: case TST_underlyingType: + case TST_addLValueReferenceType: + case TST_addRValueReferenceType: + case TST_removeReferenceType: case TST_atomic: { QualType T = DS.getRepAsType().get(); if (!T.isNull() && T->containsUnexpandedParameterPack()) Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -140,6 +140,9 @@ case tok::kw_wchar_t: case tok::kw_bool: case tok::kw___underlying_type: + case tok::kw___add_lvalue_reference: + case tok::kw___add_rvalue_reference: + case tok::kw___remove_reference: case tok::kw___auto_type: return true; @@ -5194,6 +5197,9 @@ case DeclSpec::TST_typename: case DeclSpec::TST_typeofType: case DeclSpec::TST_underlyingType: + case DeclSpec::TST_addLValueReferenceType: + case DeclSpec::TST_addRValueReferenceType: + case DeclSpec::TST_removeReferenceType: case DeclSpec::TST_atomic: { // Grab the type from the parser. TypeSourceInfo *TSI = nullptr; Index: clang/lib/Sema/DeclSpec.cpp =================================================================== --- clang/lib/Sema/DeclSpec.cpp +++ clang/lib/Sema/DeclSpec.cpp @@ -374,6 +374,9 @@ return false; case TST_underlyingType: + case TST_addLValueReferenceType: + case TST_addRValueReferenceType: + case TST_removeReferenceType: case TST_typename: case TST_typeofType: { QualType QT = DS.getRepAsType().get(); @@ -553,6 +556,9 @@ case DeclSpec::TST_decltype: return "(decltype)"; case DeclSpec::TST_decltype_auto: return "decltype(auto)"; case DeclSpec::TST_underlyingType: return "__underlying_type"; + case DeclSpec::TST_addLValueReferenceType: return "__add_lvalue_reference"; + case DeclSpec::TST_addRValueReferenceType: return "__add_rvalue_reference"; + case DeclSpec::TST_removeReferenceType: return "__remove_reference"; case DeclSpec::TST_unknown_anytype: return "__unknown_anytype"; case DeclSpec::TST_atomic: return "_Atomic"; #define GENERIC_IMAGE_TYPE(ImgType, Id) \ Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -1083,6 +1083,48 @@ DS.setTypeofParensRange(T.getRange()); } +DeclSpec::TST Parser::ReferenceTransformTokToDeclSpec() { + switch (Tok.getKind()) { + case tok::kw___add_lvalue_reference: + return DeclSpec::TST_addLValueReferenceType; + case tok::kw___add_rvalue_reference: + return DeclSpec::TST_addRValueReferenceType; + case tok::kw___remove_reference: + return DeclSpec::TST_removeReferenceType; + default: + assert(false && "Not a reference type specifier"); + } +} + +void Parser::ParseAddReferenceTypeSpecifier(DeclSpec &DS) { + DeclSpec::TST ReferenceTransformTST = ReferenceTransformTokToDeclSpec(); + + SourceLocation StartLoc = ConsumeToken(); + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "reference manipulation builtin", + tok::r_paren)) return; + + TypeResult BaseTyResult = ParseTypeName(); + if (BaseTyResult.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + T.consumeClose(); + if (T.getCloseLocation().isInvalid()) return; + + const char *PrevSpec; + unsigned DiagID; + if (DS.SetTypeSpecType(ReferenceTransformTST, + StartLoc, PrevSpec, + DiagID, BaseTyResult.get(), + Actions.getASTContext().getPrintingPolicy())) { + Diag(StartLoc, DiagID) << PrevSpec; + } + DS.setTypeofParensRange(T.getRange()); +} + /// ParseBaseTypeSpecifier - Parse a C++ base-type-specifier which is either a /// class name or decltype-specifier. Note that we only check that the result /// names a type; semantic analysis will need to verify that the type names a Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -3918,6 +3918,12 @@ ParseUnderlyingTypeSpecifier(DS); continue; + case tok::kw___remove_reference: + case tok::kw___add_rvalue_reference: + case tok::kw___add_lvalue_reference: + ParseAddReferenceTypeSpecifier(DS); + continue; + case tok::kw__Atomic: // C11 6.7.2.4/4: // If the _Atomic keyword is immediately followed by a left parenthesis, Index: clang/lib/Lex/PPMacroExpansion.cpp =================================================================== --- clang/lib/Lex/PPMacroExpansion.cpp +++ clang/lib/Lex/PPMacroExpansion.cpp @@ -1636,6 +1636,9 @@ .Case("__array_extent", true) .Case("__reference_binds_to_temporary", true) .Case("__underlying_type", true) + .Case("__add_lvalue_reference", true) + .Case("__add_rvalue_reference", true) + .Case("__remove_reference", true) .Default(false); } else { return llvm::StringSwitch<bool>(II->getName()) Index: clang/lib/Format/FormatToken.cpp =================================================================== --- clang/lib/Format/FormatToken.cpp +++ clang/lib/Format/FormatToken.cpp @@ -55,6 +55,9 @@ case tok::kw_wchar_t: case tok::kw_bool: case tok::kw___underlying_type: + case tok::kw___add_lvalue_reference: + case tok::kw___add_rvalue_reference: + case tok::kw___remove_reference: case tok::annot_typename: case tok::kw_char8_t: case tok::kw_char16_t: Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -300,6 +300,9 @@ static const TST TST_decltype = clang::TST_decltype; static const TST TST_decltype_auto = clang::TST_decltype_auto; static const TST TST_underlyingType = clang::TST_underlyingType; + static const TST TST_addLValueReferenceType = clang::TST_addLValueReferenceType; + static const TST TST_addRValueReferenceType = clang::TST_addRValueReferenceType; + static const TST TST_removeReferenceType = clang::TST_removeReferenceType; static const TST TST_auto = clang::TST_auto; static const TST TST_auto_type = clang::TST_auto_type; static const TST TST_unknown_anytype = clang::TST_unknown_anytype; @@ -408,7 +411,9 @@ static bool isTypeRep(TST T) { return (T == TST_typename || T == TST_typeofType || - T == TST_underlyingType || T == TST_atomic); + T == TST_underlyingType || T == TST_atomic|| + T == TST_addLValueReferenceType || T == TST_addRValueReferenceType || + T == TST_removeReferenceType); } static bool isExprRep(TST T) { return (T == TST_typeofExpr || T == TST_decltype); Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2602,6 +2602,8 @@ SourceLocation StartLoc, SourceLocation EndLoc); void ParseUnderlyingTypeSpecifier(DeclSpec &DS); + DeclSpec::TST ReferenceTransformTokToDeclSpec(); + void ParseAddReferenceTypeSpecifier(DeclSpec &DS); void ParseAtomicSpecifier(DeclSpec &DS); ExprResult ParseAlignArgument(SourceLocation Start, Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -485,7 +485,10 @@ TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX) TYPE_TRAIT_1(__has_unique_object_representations, HasUniqueObjectRepresentations, KEYCXX) -KEYWORD(__underlying_type , KEYCXX) +KEYWORD(__underlying_type , KEYCXX) +KEYWORD(__add_lvalue_reference , KEYCXX) +KEYWORD(__add_rvalue_reference , KEYCXX) +KEYWORD(__remove_reference , KEYCXX) // Clang-only C++ Type Traits TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) Index: clang/include/clang/Basic/Specifiers.h =================================================================== --- clang/include/clang/Basic/Specifiers.h +++ clang/include/clang/Basic/Specifiers.h @@ -87,6 +87,9 @@ TST_typeofExpr, TST_decltype, // C++11 decltype TST_underlyingType, // __underlying_type for C++11 + TST_addLValueReferenceType, // __add_lvalue_reference + TST_addRValueReferenceType, // __add_rvalue_reference + TST_removeReferenceType, // __remove_reference TST_auto, // C++11 auto TST_decltype_auto, // C++1y decltype(auto) TST_auto_type, // __auto_type extension Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -4348,7 +4348,10 @@ class UnaryTransformType : public Type { public: enum UTTKind { - EnumUnderlyingType + EnumUnderlyingType, + RemoveReferenceType, + AddRValueType, + AddLValueType }; private:
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits