https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80290
--- Comment #13 from Richard Biener <rguenth at gcc dot gnu.org> --- typedef std::pair<unsigned, std::pair<const char *, std::pair<const char *, std::pair<const char *, const char *> > > > FruMap; extern const FruMap frus[] = { { 1, { "", { "", {"",""}, }, }, }, }; is twice as fast as typedef std::pair<unsigned, std::pair<const char *, std::pair<const char *, std::pair<const char *, std::pair<const char *, const char *> > > > > FruMap; extern const FruMap frus[] = { { 1, { "", { "", { "", {"",""}, }, } }, }, }; the latter with 70713 implicit_conversion calls while the former with "only" 14331 implicit_conversion calls. Removing another std::pair<> level reduces it to 4389 calls (still 10689 tsubst_copy_and_build calls(!)). typedef std::pair<unsigned, std::pair<const char *, std::pair<const char *, const char *> > > FruMap; extern const FruMap frus[] = { { 1, { "", {"",""}, }, }, }; constexpr evaluation seems to cause quite some bits of it. The constexpr hash is quite big and we arrive with following exprs to evaluate/cache: {} 0 || 0 std::_PCC<false, const char*, const char*>::_ConstructiblePair<const char*, const char*> () !std::_PCC<false, const char*, const char*>::_ImplicitlyConvertiblePair<const char*, const char*> () std::_PCC<false, const char*, const char*>::_ConstructiblePair<const char*, const char*> () && !std::_PCC<false, const char*, const char*>::_ImplicitlyConvertiblePair<const char*, const char*> () and for the && we don't use the cached LHS we computed above because we don't use maybe_constant_value for example in cxx_eval_logical_expression but we recurse into cxx_eval_constant_expression! A simple patch like Index: gcc/cp/constexpr.c =================================================================== --- gcc/cp/constexpr.c (revision 246678) +++ gcc/cp/constexpr.c (working copy) @@ -3860,6 +3860,9 @@ lookup_placeholder (const constexpr_ctx return ob; } +static GTY((deletable)) hash_map<tree, tree> *cv_cache; + + /* Attempt to reduce the expression T to a constant value. On failure, issue diagnostic and return error_mark_node. */ /* FIXME unify with c_fully_fold */ @@ -3874,6 +3877,11 @@ cxx_eval_constant_expression (const cons constexpr_ctx new_ctx; tree r = t; + tree *cached; + if (cv_cache + && (cached = cv_cache->get (t))) + return *cached; + if (jump_target && *jump_target) { /* If we are jumping, ignore all statements/expressions except those @@ -4824,8 +4832,6 @@ fold_simple (tree t) Otherwise, if T does not have TREE_CONSTANT set, returns T. Otherwise, returns a version of T without TREE_CONSTANT. */ -static GTY((deletable)) hash_map<tree, tree> *cv_cache; - tree maybe_constant_value (tree t, tree decl) { should improve this, but while it catches some cases it doesn't result in an overall improvement. The patch is likely incorrect anyway. But I think it shows the issue (and likely the patch doesn't help because "equivalent" calls are not detected as such by the cv_cache map which ends up with pointer equivalence AFAICS). Oh, and there are constexpr evaluations that do not go through the cache anyway (potential_constant_expression, cxx_constant_value)