Skip to content

[Support] Apply constexpr to getTypeName #127893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 21, 2025
Merged

Conversation

rupprecht
Copy link
Collaborator

This is a followup to 003a721, which we noticed increased binary size a small but noticable amount. The increase seems to be due to code size from each llvm::getTypeName<T> wrapper, which adds up if there are a lot of types.

The original motivation was to improve runtime and avoid getTypeName being recomputed each time. The implementation is simple enough that we can make it fully constexpr, which addresses the size increase, but also further improves runtime: we directly reference the data instead of jumping through a guard variable to see if we've computed it already.

This is a followup to 003a721, which we noticed increased binary size a small but noticable amount. The increase seems to be due to code size from each `llvm::getTypeName<T>` wrapper, which adds up if there are a lot of types.

The original motivation was to improve runtime and avoid `getTypeName` being recomputed each time. The implementation is simple enough that we can make it fully constexpr, which addresses the size increase, but also further improves runtime: we directly reference the data instead of jumping through a guard variable to see if we've computed it already.
@llvmbot
Copy link
Member

llvmbot commented Feb 19, 2025

@llvm/pr-subscribers-llvm-support

Author: Jordan Rupprecht (rupprecht)

Changes

This is a followup to 003a721, which we noticed increased binary size a small but noticable amount. The increase seems to be due to code size from each llvm::getTypeName&lt;T&gt; wrapper, which adds up if there are a lot of types.

The original motivation was to improve runtime and avoid getTypeName being recomputed each time. The implementation is simple enough that we can make it fully constexpr, which addresses the size increase, but also further improves runtime: we directly reference the data instead of jumping through a guard variable to see if we've computed it already.


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

1 Files Affected:

  • (modified) llvm/include/llvm/Support/TypeName.h (+52-38)
diff --git a/llvm/include/llvm/Support/TypeName.h b/llvm/include/llvm/Support/TypeName.h
index baa7a691302e3..c50d67dc38635 100644
--- a/llvm/include/llvm/Support/TypeName.h
+++ b/llvm/include/llvm/Support/TypeName.h
@@ -9,61 +9,75 @@
 #ifndef LLVM_SUPPORT_TYPENAME_H
 #define LLVM_SUPPORT_TYPENAME_H
 
+#include <string_view>
+
 #include "llvm/ADT/StringRef.h"
 
 namespace llvm {
 
-namespace detail {
-template <typename DesiredTypeName> inline StringRef getTypeNameImpl() {
+/// We provide a function which tries to compute the (demangled) name of a type
+/// statically.
+///
+/// This routine may fail on some platforms or for particularly unusual types.
+/// Do not use it for anything other than logging and debugging aids. It isn't
+/// portable or dependendable in any real sense.
+///
+/// The returned StringRef will point into a static storage duration string.
+/// However, it may not be null terminated and may be some strangely aligned
+/// inner substring of a larger string.
+template <typename DesiredTypeName> inline constexpr StringRef getTypeName() {
 #if defined(__clang__) || defined(__GNUC__)
-  StringRef Name = __PRETTY_FUNCTION__;
+  constexpr std::string_view Name = __PRETTY_FUNCTION__;
 
-  StringRef Key = "DesiredTypeName = ";
-  Name = Name.substr(Name.find(Key));
-  assert(!Name.empty() && "Unable to find the template parameter!");
-  Name = Name.drop_front(Key.size());
+  constexpr std::string_view Key = "DesiredTypeName = ";
+  constexpr std::string_view TemplateParamsStart = Name.substr(Name.find(Key));
+  static_assert(!TemplateParamsStart.empty(),
+                "Unable to find the template parameter!");
+  constexpr std::string_view SubstitutionKey =
+      TemplateParamsStart.substr(Key.size());
 
-  assert(Name.ends_with("]") && "Name doesn't end in the substitution key!");
-  return Name.drop_back(1);
+  // ends_with() is only available in c++20
+  static_assert(!SubstitutionKey.empty() && SubstitutionKey.back() == ']',
+                "Name doesn't end in the substitution key!");
+  return SubstitutionKey.substr(0, SubstitutionKey.size() - 1);
 #elif defined(_MSC_VER)
-  StringRef Name = __FUNCSIG__;
+  constexpr std::string_view Name = __FUNCSIG__;
 
-  StringRef Key = "getTypeNameImpl<";
-  Name = Name.substr(Name.find(Key));
-  assert(!Name.empty() && "Unable to find the function name!");
-  Name = Name.drop_front(Key.size());
+  constexpr std::string_view Key = "getTypeName<";
+  constexpr std::string_view GetTypeNameStart = Name.substr(Name.find(Key));
+  static_assert(!GetTypeNameStart.empty(),
+                "Unable to find the template parameter!");
+  constexpr std::string_view SubstitutionKey =
+      GetTypeNameStart.substr(Key.size());
 
-  for (StringRef Prefix : {"class ", "struct ", "union ", "enum "})
-    if (Name.starts_with(Prefix)) {
-      Name = Name.drop_front(Prefix.size());
-      break;
-    }
+  // starts_with() only available in c++20
+  constexpr std::string_view RmPrefixClass =
+      SubstitutionKey.find("class ") == 0
+          ? SubstitutionKey.substr(sizeof("class ") - 1)
+          : SubstitutionKey;
+  constexpr std::string_view RmPrefixStruct =
+      RmPrefixClass.find("struct ") == 0
+          ? RmPrefixClass.substr(sizeof("struct ") - 1)
+          : RmPrefixClass;
+  constexpr std::string_view RmPrefixUnion =
+      RmPrefixStruct.find("union ") == 0
+          ? RmPrefixStruct.substr(sizeof("union ") - 1)
+          : RmPrefixStruct;
+  constexpr std::string_view RmPrefixEnum =
+      RmPrefixUnion.find("enum ") == 0
+          ? RmPrefixUnion.substr(sizeof("enum ") - 1)
+          : RmPrefixUnion;
 
-  auto AnglePos = Name.rfind('>');
-  assert(AnglePos != StringRef::npos && "Unable to find the closing '>'!");
-  return Name.substr(0, AnglePos);
+  constexpr auto AnglePos = RmPrefixEnum.rfind('>');
+  static_assert(AnglePos != std::string_view::npos,
+                "Unable to find the closing '>'!");
+  return RmPrefixEnum.substr(0, AnglePos);
 #else
   // No known technique for statically extracting a type name on this compiler.
   // We return a string that is unlikely to look like any type in LLVM.
   return "UNKNOWN_TYPE";
 #endif
 }
-} // namespace detail
-
-/// We provide a function which tries to compute the (demangled) name of a type
-/// statically.
-///
-/// This routine may fail on some platforms or for particularly unusual types.
-/// Do not use it for anything other than logging and debugging aids. It isn't
-/// portable or dependendable in any real sense.
-///
-/// The returned StringRef will point into a static storage duration string.
-/// However, it may not be null terminated and may be some strangely aligned
-/// inner substring of a larger string.
-template <typename DesiredTypeName> inline StringRef getTypeName() {
-  static StringRef Name = detail::getTypeNameImpl<DesiredTypeName>();
-  return Name;
-}
 
 } // namespace llvm
 

@rupprecht rupprecht merged commit 8a39214 into llvm:main Feb 21, 2025
10 checks passed
@jplehr
Copy link
Contributor

jplehr commented Feb 21, 2025

It seems this broke our sles buildbot.
Can you please take a look? Thanks!

https://lab.llvm.org/buildbot/#/builders/140/builds/17465

@IanWood1
Copy link
Contributor

It looks like gcc doesn't consider __PRETTY_FUNCTION__ constexpr

@rupprecht
Copy link
Collaborator Author

Yes, it looks like older versions of gcc don't declare __PRETTY_FUNCTION__ as constexpr: https://gcc.gnu.org/PR66639

9.1 seems to be the minimum version that's OK with this patch: https://godbolt.org/z/Y6j6hdeov. All supported clang/msvc versions seem to be OK.

rupprecht added a commit that referenced this pull request Feb 21, 2025
…128212)

Prior to gcc version 9, the `__PRETTY_FUNCTION__` macro was not declared
constexpr. In that case, don't declare this as constexpr, and switch the
static asserts to runtime asserts.

Verified this should work on all supported compilers:
https://godbolt.org/z/T77rvPW5z

Followup to #127893 / 8a39214
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants