https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/223027
>From e2a1f096f5c76d71d363aeeeed6fbedab80fd019 Mon Sep 17 00:00:00 2001 From: Mehdi Amini <[email protected]> Date: Thu, 10 Sep 2026 11:16:40 -0700 Subject: [PATCH] Speed up common template argument checks Avoid setting up pack and constraint machinery for ordinary unconstrained template argument lists. Reuse canonical type work, keep common converted argument lists inline, and skip default-substitution comparisons when the parameter has no default. CTMark O0 (3 samples, CPU 6): 29.439800 s -> 29.315900 s (-0.421%). Impact on significant TUs in MLIR build time: - `mlir/lib/RegisterAllDialects.cpp`: 0.7009% fewer retired instructions. - `mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp`: 0.6378% fewer retired instructions. Assisted-by: Codex --- clang/include/clang/Sema/Sema.h | 7 +- clang/lib/Sema/SemaTemplate.cpp | 88 +++++++++++++++----- clang/unittests/Support/TimeProfilerTest.cpp | 3 - 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0864337a9374c6..de39fe063c70a0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12081,7 +12081,7 @@ class Sema final : public SemaBase { /// The checked, converted argument will be added to the /// end of these vectors. - SmallVector<TemplateArgument, 4> SugaredConverted, CanonicalConverted; + SmallVector<TemplateArgument, 8> SugaredConverted, CanonicalConverted; /// The check is being performed in the context of partial ordering. bool PartialOrdering; @@ -12186,7 +12186,10 @@ class Sema final : public SemaBase { /// /// This routine implements the semantics of C++ [temp.arg.type]. It /// returns true if an error occurred, and false otherwise. - bool CheckTemplateArgument(TypeSourceInfo *Arg); + /// + /// \param CanonicalArg If non-null, receives the canonical type of \p Arg. + bool CheckTemplateArgument(TypeSourceInfo *Arg, + QualType *CanonicalArg = nullptr); /// Check a template argument against its corresponding /// non-type template parameter. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b8b0c71894daac..07394b80a5c4e6 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5285,7 +5285,8 @@ bool Sema::CheckTemplateTypeArgument( } } - if (CheckTemplateArgument(TSI)) + QualType CanonArgType; + if (CheckTemplateArgument(TSI, &CanonArgType)) return true; // Objective-C ARC: @@ -5297,11 +5298,11 @@ bool Sema::CheckTemplateTypeArgument( Qualifiers Qs; Qs.setObjCLifetime(Qualifiers::OCL_Strong); ArgType = Context.getQualifiedType(ArgType, Qs); + CanonArgType = Context.getCanonicalType(ArgType); } SugaredConverted.push_back(TemplateArgument(ArgType)); - CanonicalConverted.push_back( - TemplateArgument(Context.getCanonicalType(ArgType))); + CanonicalConverted.push_back(TemplateArgument(CanonArgType)); return false; } @@ -5855,6 +5856,19 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, return true; } +/// Return whether \p Param has a default template argument. +/// +/// \p Param must be one of the three template parameter declaration kinds. +static bool hasDefaultTemplateArgument(const NamedDecl *Param) { + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + return TTP->hasDefaultArgument(); + if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param)) + return TTP->hasDefaultArgument(); + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) + return NTTP->hasDefaultArgument(); + llvm_unreachable("expected a template parameter declaration"); +} + /// Check that the given template argument list is well-formed /// for specializing the given template. bool Sema::CheckTemplateArgumentList( @@ -5919,10 +5933,13 @@ bool Sema::CheckTemplateArgumentList( } } + bool IsParameterPack = (*Param)->isTemplateParameterPack(); + UnsignedOrNone ExpandedPackSize = getExpandedPackSize(*Param); + // If we have an expanded parameter pack, make sure we don't have too // many arguments. - if (UnsignedOrNone Expansions = getExpandedPackSize(*Param)) { - if (*Expansions == SugaredArgumentPack.size()) { + if (ExpandedPackSize) { + if (*ExpandedPackSize == SugaredArgumentPack.size()) { // We're done with this parameter pack. Pack up its arguments and add // them to the list. CTAI.SugaredConverted.push_back( @@ -5972,8 +5989,25 @@ bool Sema::CheckTemplateArgumentList( if (ArgIdx < NumArgs) { TemplateArgumentLoc &ArgLoc = NewArgs[ArgIdx]; - bool NonPackParameter = - !(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param); + // Fast path for the common one-to-one case. + if (!IsParameterPack && !ArgLoc.getArgument().isPackExpansion()) { + SaveAndRestore _1(CTAI.PartialOrdering, false); + if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc, + RAngleLoc, 0, CTAI, CTAK_Specified)) + return true; + if (hasDefaultTemplateArgument(*Param)) + CTAI.CanonicalConverted.back().setIsDefaulted( + clang::isSubstitutedDefaultArgument( + Context, ArgLoc.getArgument(), *Param, + CTAI.CanonicalConverted, Params->getDepth())); + else if (CTAI.CanonicalConverted.back().getIsDefaulted()) + CTAI.CanonicalConverted.back().setIsDefaulted(false); + ++ArgIdx; + ++Param; + continue; + } + + bool NonPackParameter = !IsParameterPack || ExpandedPackSize; bool ArgIsExpansion = ArgLoc.getArgument().isPackExpansion(); if (ArgIsExpansion && CTAI.MatchingTTP) { @@ -5993,10 +6027,13 @@ bool Sema::CheckTemplateArgumentList( CTAK_Specified)) return true; Arg = NewArgLoc.getArgument(); - CTAI.CanonicalConverted.back().setIsDefaulted( - clang::isSubstitutedDefaultArgument(Context, Arg, *Param, - CTAI.CanonicalConverted, - Params->getDepth())); + if (hasDefaultTemplateArgument(*Param)) + CTAI.CanonicalConverted.back().setIsDefaulted( + clang::isSubstitutedDefaultArgument(Context, Arg, *Param, + CTAI.CanonicalConverted, + Params->getDepth())); + else if (CTAI.CanonicalConverted.back().getIsDefaulted()) + CTAI.CanonicalConverted.back().setIsDefaulted(false); } ArgLoc = TemplateArgumentLoc( TemplateArgument::CreatePackCopy(Context, Args), @@ -6007,10 +6044,13 @@ bool Sema::CheckTemplateArgumentList( RAngleLoc, SugaredArgumentPack.size(), CTAI, CTAK_Specified)) return true; - CTAI.CanonicalConverted.back().setIsDefaulted( - clang::isSubstitutedDefaultArgument(Context, ArgLoc.getArgument(), - *Param, CTAI.CanonicalConverted, - Params->getDepth())); + if (hasDefaultTemplateArgument(*Param)) + CTAI.CanonicalConverted.back().setIsDefaulted( + clang::isSubstitutedDefaultArgument( + Context, ArgLoc.getArgument(), *Param, + CTAI.CanonicalConverted, Params->getDepth())); + else if (CTAI.CanonicalConverted.back().getIsDefaulted()) + CTAI.CanonicalConverted.back().setIsDefaulted(false); if (ArgIsExpansion && NonPackParameter) { // CWG1430/CWG2686: we have a pack expansion as an argument to an // alias template, builtin template, or concept, and it's not part of @@ -6061,7 +6101,7 @@ bool Sema::CheckTemplateArgumentList( return false; } - if ((*Param)->isTemplateParameterPack()) { + if (IsParameterPack) { // The template parameter was a template parameter pack, so take the // deduced argument and place it on the argument pack. Note that we // stay on the same template parameter so that we can deduce more @@ -6088,9 +6128,8 @@ bool Sema::CheckTemplateArgumentList( // If we have a template parameter pack with no more corresponding // arguments, just break out now and we'll fill in the argument pack below. - if ((*Param)->isTemplateParameterPack()) { - assert(!getExpandedPackSize(*Param) && - "Should have dealt with this already"); + if (IsParameterPack) { + assert(!ExpandedPackSize && "Should have dealt with this already"); // A non-expanded parameter pack before the end of the parameter list // only occurs for an ill-formed template parameter list, unless we've @@ -6207,7 +6246,8 @@ bool Sema::CheckTemplateArgumentList( if (UpdateArgsWithConversions) TemplateArgs = std::move(NewArgs); - if (!PartialTemplateArgs) { + if (!PartialTemplateArgs && !isa<ConceptDecl>(Template) && + Template->hasAssociatedConstraints()) { // Setup the context/ThisScope for the case where we are needing to // re-instantiate constraints outside of normal instantiation. DeclContext *NewContext = Template->getDeclContext(); @@ -6231,8 +6271,7 @@ bool Sema::CheckTemplateArgumentList( /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConceptInstantiation=*/true); - if (!isa<ConceptDecl>(Template) && - EnsureTemplateArgumentListConstraints( + if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { if (ConstraintsNotSatisfied) @@ -6548,11 +6587,14 @@ bool UnnamedLocalNoLinkageFinder::VisitHLSLInlineSpirvType( return false; } -bool Sema::CheckTemplateArgument(TypeSourceInfo *ArgInfo) { +bool Sema::CheckTemplateArgument(TypeSourceInfo *ArgInfo, + QualType *CanonicalArg) { assert(ArgInfo && "invalid TypeSourceInfo"); QualType Arg = ArgInfo->getType(); SourceRange SR = ArgInfo->getTypeLoc().getSourceRange(); QualType CanonArg = Context.getCanonicalType(Arg); + if (CanonicalArg) + *CanonicalArg = CanonArg; if (CanonArg->isVariablyModifiedType()) { return Diag(SR.getBegin(), diag::err_variably_modified_template_arg) << Arg; diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp index ae6b16a7377d63..32a9dd390b81ba 100644 --- a/clang/unittests/Support/TimeProfilerTest.cpp +++ b/clang/unittests/Support/TimeProfilerTest.cpp @@ -349,14 +349,11 @@ TEST(TimeProfilerTest, ClassTemplateInstantiations) { ExecuteCompiler | Frontend (test.cc) | | ParseClass (S) -| | CheckConstraintSatisfaction (<test.cc:9:21, col:29>) | | InstantiateClass (S<double>, test.cc:9) | | InstantiateFunction (S<double>::foo, test.cc:5) | | ParseDeclarationOrFunctionDefinition (test.cc:11:5) | | | ParseFunctionDefinition (user) -| | | | CheckConstraintSatisfaction (<test.cc:12:7, col:12>) | | | | InstantiateClass (S<int>, test.cc:3) -| | | | CheckConstraintSatisfaction (<test.cc:13:7, col:14>) | | | | InstantiateClass (S<float>, test.cc:3) | | | | DeferInstantiation (S<float>::foo) | PerformPendingInstantiations _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
