Speed up compilation of ASTImporter

Avoid recursively instantiating importSeq. Use initializer list
expansion to stamp out a single instantiation of std::tuple of the
deduced sequence of types, and thread the error around that tuple type.
Avoids needlessly instantiating std::tuple N-1 times.

new time to compile: 0m25.985s
old time to compile: 0m35.563s

new obj size: 10,000kb
old obj size: 12,332kb

I found the slow TU by looking at ClangBuildAnalyzer results, and looked
at -ftime-trace for the file in chrome://tracing to find this.

Tested with: clang-cl, MSVC, and GCC.

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D73667
This commit is contained in:
Reid Kleckner 2020-01-29 14:29:34 -08:00
parent b0d25fff9b
commit af3e884956
1 changed files with 19 additions and 18 deletions

View File

@ -186,32 +186,33 @@ namespace clang {
return import(*From); return import(*From);
} }
template <class T> // Helper for error management in importSeq.
Expected<std::tuple<T>> template <typename T> T checkImport(Error &Err, const T &From) {
importSeq(const T &From) { // Don't attempt to import nodes if we hit an error earlier.
Expected<T> ToOrErr = import(From); if (Err)
if (!ToOrErr) return T{};
return ToOrErr.takeError(); Expected<T> MaybeVal = import(From);
return std::make_tuple<T>(std::move(*ToOrErr)); if (!MaybeVal) {
Err = MaybeVal.takeError();
return T{};
}
return *MaybeVal;
} }
// Import multiple objects with a single function call. // Import multiple objects with a single function call.
// This should work for every type for which a variant of `import` exists. // This should work for every type for which a variant of `import` exists.
// The arguments are processed from left to right and import is stopped on // The arguments are processed from left to right and import is stopped on
// first error. // first error.
template <class THead, class... TTail> template <class... Args>
Expected<std::tuple<THead, TTail...>> Expected<std::tuple<Args...>> importSeq(const Args &... args) {
importSeq(const THead &FromHead, const TTail &...FromTail) { Error E = Error::success();
Expected<std::tuple<THead>> ToHeadOrErr = importSeq(FromHead); std::tuple<Args...> Res{checkImport(E, args)...};
if (!ToHeadOrErr) if (E)
return ToHeadOrErr.takeError(); return std::move(E);
Expected<std::tuple<TTail...>> ToTailOrErr = importSeq(FromTail...); return std::move(Res);
if (!ToTailOrErr)
return ToTailOrErr.takeError();
return std::tuple_cat(*ToHeadOrErr, *ToTailOrErr);
} }
// Wrapper for an overload set. // Wrapper for an overload set.
template <typename ToDeclT> struct CallOverloadedCreateFun { template <typename ToDeclT> struct CallOverloadedCreateFun {
template <typename... Args> template <typename... Args>
auto operator()(Args &&... args) auto operator()(Args &&... args)