[libc-commits] [libc] [libc] Move LLVM_LIBC_IS_DEFINED macro to its own header (PR #190081)

Roland McGrath via libc-commits libc-commits at lists.llvm.org
Thu Apr 2 11:57:32 PDT 2026


https://github.com/frobtech updated https://github.com/llvm/llvm-project/pull/190081

>From ebee2c03c2dfb585420d4334847e9461654e97be Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Wed, 1 Apr 2026 16:02:05 -0700
Subject: [PATCH 1/2] [libc] Move LLVM_LIBC_IS_DEFINED macro to its own header

This moves the LLVM_LIBC_IS_DEFINED macro to its own header is
__support/macros.  Its implementation leverages cpp::string_view
instead of rolling its own strcmp; this necessitated fixing
several missing constexpr in the string_view implementation.

The new __support/macros/macro-utils.h is also broken out to hold
the stringification macro and can be used in future for token
pasting shenanigans and other such generic macro machinery.
---
 libc/src/__support/CPP/new.h                  |  3 +-
 libc/src/__support/CPP/string_view.h          | 59 ++++++++++---------
 libc/src/__support/common.h                   | 26 --------
 libc/src/__support/libc_assert.h              | 15 ++---
 libc/src/__support/macros/is_defined.h        | 26 ++++++++
 libc/src/__support/macros/macro-utils.h       | 16 +++++
 libc/src/string/memory_utils/op_aarch64.h     |  1 +
 libc/src/string/memory_utils/op_x86.h         |  1 +
 .../memory_utils/x86_64/inline_memcpy.h       |  5 +-
 .../memory_utils/x86_64/inline_memset.h       |  1 +
 10 files changed, 86 insertions(+), 67 deletions(-)
 create mode 100644 libc/src/__support/macros/is_defined.h
 create mode 100644 libc/src/__support/macros/macro-utils.h

diff --git a/libc/src/__support/CPP/new.h b/libc/src/__support/CPP/new.h
index e529e05d858a4..47a8f1b9b8302 100644
--- a/libc/src/__support/CPP/new.h
+++ b/libc/src/__support/CPP/new.h
@@ -12,6 +12,7 @@
 #include "hdr/func/free.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/macro-utils.h"
 #include "src/__support/macros/properties/compiler.h"
 
 #include <stddef.h> // For size_t
@@ -54,7 +55,7 @@ LIBC_INLINE void *operator new[](size_t, void *p) { return p; }
 
 #ifndef LIBC_COMPILER_IS_MSVC
 #define DELETE_NAME(name)                                                      \
-  __asm__(LIBC_MACRO_TO_STRING(LIBC_NAMESPACE) "_" LIBC_MACRO_TO_STRING(name))
+  __asm__(LLVM_LIBC_STRINGIFY(LIBC_NAMESPACE) "_" LLVM_LIBC_STRINGIFY(name))
 #else
 #define DELETE_NAME(name)
 #endif // LIBC_COMPILER_IS_MSVC
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index aa15814b2e149..6991fd46a4ace 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -28,10 +28,12 @@ class string_view {
   const char *Data;
   size_t Len;
 
-  LIBC_INLINE static size_t min(size_t A, size_t B) { return A <= B ? A : B; }
+  LIBC_INLINE static constexpr size_t min(size_t A, size_t B) {
+    return A <= B ? A : B;
+  }
 
-  LIBC_INLINE static int compareMemory(const char *Lhs, const char *Rhs,
-                                       size_t Length) {
+  LIBC_INLINE static constexpr int
+  compareMemory(const char *Lhs, const char *Rhs, size_t Length) {
     for (size_t i = 0; i < Length; ++i)
       if (int Diff = (int)Lhs[i] - (int)Rhs[i])
         return Diff;
@@ -44,7 +46,7 @@ class string_view {
         return static_cast<size_t>(End - Str);
   }
 
-  LIBC_INLINE bool equals(string_view Other) const {
+  LIBC_INLINE constexpr bool equals(string_view Other) const {
     return (Len == Other.Len &&
             compareMemory(Data, Other.Data, Other.Len) == 0);
   }
@@ -86,11 +88,11 @@ class string_view {
   LIBC_INLINE constexpr bool empty() const { return Len == 0; }
 
   // Returns an iterator to the first character of the view.
-  LIBC_INLINE const char *begin() const { return Data; }
+  LIBC_INLINE constexpr const char *begin() const { return Data; }
 
   // Returns an iterator to the character following the last character of the
   // view.
-  LIBC_INLINE const char *end() const { return Data + Len; }
+  LIBC_INLINE constexpr const char *end() const { return Data + Len; }
 
   // Returns a const reference to the character at specified location pos.
   // No bounds checking is performed: the behavior is undefined if pos >=
@@ -101,7 +103,7 @@ class string_view {
 
   /// compare - Compare two strings; the result is -1, 0, or 1 if this string
   /// is lexicographically less than, equal to, or greater than the \p Other.
-  LIBC_INLINE int compare(string_view Other) const {
+  LIBC_INLINE constexpr int compare(string_view Other) const {
     // Check the prefix for a mismatch.
     if (int Res = compareMemory(Data, Other.Data, min(Len, Other.Len)))
       return Res < 0 ? -1 : 1;
@@ -111,75 +113,78 @@ class string_view {
     return Len < Other.Len ? -1 : 1;
   }
 
-  LIBC_INLINE bool operator==(string_view Other) const { return equals(Other); }
-  LIBC_INLINE bool operator!=(string_view Other) const {
+  LIBC_INLINE constexpr bool operator==(string_view Other) const {
+    return equals(Other);
+  }
+  LIBC_INLINE constexpr bool operator!=(string_view Other) const {
     return !(*this == Other);
   }
-  LIBC_INLINE bool operator<(string_view Other) const {
+  LIBC_INLINE constexpr bool operator<(string_view Other) const {
     return compare(Other) == -1;
   }
-  LIBC_INLINE bool operator<=(string_view Other) const {
+  LIBC_INLINE constexpr bool operator<=(string_view Other) const {
     return compare(Other) != 1;
   }
-  LIBC_INLINE bool operator>(string_view Other) const {
+  LIBC_INLINE constexpr bool operator>(string_view Other) const {
     return compare(Other) == 1;
   }
-  LIBC_INLINE bool operator>=(string_view Other) const {
+  LIBC_INLINE constexpr bool operator>=(string_view Other) const {
     return compare(Other) != -1;
   }
 
   // Moves the start of the view forward by n characters.
   // The behavior is undefined if n > size().
-  LIBC_INLINE void remove_prefix(size_t N) {
+  LIBC_INLINE constexpr void remove_prefix(size_t N) {
     Len -= N;
     Data += N;
   }
 
   // Moves the end of the view back by n characters.
   // The behavior is undefined if n > size().
-  LIBC_INLINE void remove_suffix(size_t N) { Len -= N; }
+  LIBC_INLINE constexpr void remove_suffix(size_t N) { Len -= N; }
 
   // Check if this string starts with the given Prefix.
-  LIBC_INLINE bool starts_with(string_view Prefix) const {
+  LIBC_INLINE constexpr bool starts_with(string_view Prefix) const {
     return Len >= Prefix.Len &&
            compareMemory(Data, Prefix.Data, Prefix.Len) == 0;
   }
 
   // Check if this string starts with the given Prefix.
-  LIBC_INLINE bool starts_with(const char Prefix) const {
+  LIBC_INLINE constexpr bool starts_with(const char Prefix) const {
     return !empty() && front() == Prefix;
   }
 
   // Check if this string ends with the given Prefix.
-  LIBC_INLINE bool ends_with(const char Suffix) const {
+  LIBC_INLINE constexpr bool ends_with(const char Suffix) const {
     return !empty() && back() == Suffix;
   }
 
   // Check if this string ends with the given Suffix.
-  LIBC_INLINE bool ends_with(string_view Suffix) const {
+  LIBC_INLINE constexpr bool ends_with(string_view Suffix) const {
     return Len >= Suffix.Len &&
            compareMemory(end() - Suffix.Len, Suffix.Data, Suffix.Len) == 0;
   }
 
   // Return a reference to the substring from [Start, Start + N).
   //
-  // Start The index of the starting character in the substring; if the index is
-  // npos or greater than the length of the string then the empty substring will
-  // be returned.
+  // Start The index of the starting character in the substring; if the index
+  // is npos or greater than the length of the string then the empty substring
+  // will be returned.
   //
   // N The number of characters to included in the substring. If N exceeds the
   // number of characters remaining in the string, the string suffix (starting
   // with Start) will be returned.
-  LIBC_INLINE string_view substr(size_t Start, size_t N = npos) const {
+  LIBC_INLINE constexpr string_view substr(size_t Start,
+                                           size_t N = npos) const {
     Start = min(Start, Len);
     return string_view(Data + Start, min(N, Len - Start));
   }
 
   // front - Get the first character in the string.
-  LIBC_INLINE char front() const { return Data[0]; }
+  LIBC_INLINE constexpr char front() const { return Data[0]; }
 
   // back - Get the last character in the string.
-  LIBC_INLINE char back() const { return Data[Len - 1]; }
+  LIBC_INLINE constexpr char back() const { return Data[Len - 1]; }
 
   // Finds the first occurence of c in this view, starting at position From.
   LIBC_INLINE constexpr size_t find_first_of(const char c,
@@ -200,8 +205,8 @@ class string_view {
     return npos;
   }
 
-  // Finds the first character not equal to c in this view, starting at position
-  // From.
+  // Finds the first character not equal to c in this view, starting at
+  // position From.
   LIBC_INLINE constexpr size_t find_first_not_of(const char c,
                                                  size_t From = 0) const {
     for (size_t Pos = From; Pos < size(); ++Pos)
diff --git a/libc/src/__support/common.h b/libc/src/__support/common.h
index b3bbb08d4ed2f..d90fe7b8ae98d 100644
--- a/libc/src/__support/common.h
+++ b/libc/src/__support/common.h
@@ -103,30 +103,4 @@
 
 #define LLVM_LIBC_VARIABLE(type, name) LLVM_LIBC_VARIABLE_IMPL(type, name)
 
-namespace LIBC_NAMESPACE_DECL {
-namespace internal {
-LIBC_INLINE constexpr bool same_string(char const *lhs, char const *rhs) {
-  for (; *lhs || *rhs; ++lhs, ++rhs)
-    if (*lhs != *rhs)
-      return false;
-  return true;
-}
-} // namespace internal
-} // namespace LIBC_NAMESPACE_DECL
-
-#define __LIBC_MACRO_TO_STRING(str) #str
-#define LIBC_MACRO_TO_STRING(str) __LIBC_MACRO_TO_STRING(str)
-
-// LLVM_LIBC_IS_DEFINED checks whether a particular macro is defined.
-// Usage: constexpr bool kUseAvx = LLVM_LIBC_IS_DEFINED(__AVX__);
-//
-// This works by comparing the stringified version of the macro with and without
-// evaluation. If FOO is not undefined both stringifications yield "FOO". If FOO
-// is defined, one stringification yields "FOO" while the other yields its
-// stringified value "1".
-#define LLVM_LIBC_IS_DEFINED(macro)                                            \
-  !LIBC_NAMESPACE::internal::same_string(                                      \
-      LLVM_LIBC_IS_DEFINED__EVAL_AND_STRINGIZE(macro), #macro)
-#define LLVM_LIBC_IS_DEFINED__EVAL_AND_STRINGIZE(s) #s
-
 #endif // LLVM_LIBC_SRC___SUPPORT_COMMON_H
diff --git a/libc/src/__support/libc_assert.h b/libc/src/__support/libc_assert.h
index 6e0b5bd3d68b4..26dd0fc2f9562 100644
--- a/libc/src/__support/libc_assert.h
+++ b/libc/src/__support/libc_assert.h
@@ -25,8 +25,9 @@
 #include "src/__support/OSUtil/exit.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/integer_to_string.h"
-#include "src/__support/macros/attributes.h"   // For LIBC_INLINE
+#include "src/__support/macros/attributes.h" // For LIBC_INLINE
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/macro-utils.h"
 #include "src/__support/macros/optimization.h" // For LIBC_UNLIKELY
 
 namespace LIBC_NAMESPACE_DECL {
@@ -65,19 +66,11 @@ LIBC_INLINE void report_assertion_failure(const char *assertion,
   } while (false)
 #else
 
-// Convert __LINE__ to a string using macros. The indirection is necessary
-// because otherwise it will turn "__LINE__" into a string, not its value. The
-// value is evaluated in the indirection step.
-#define __LIBC_MACRO_TO_STR(x) #x
-#define __LIBC_MACRO_TO_STR_INDIR(y) __LIBC_MACRO_TO_STR(y)
-#define __LIBC_LINE_STR__ __LIBC_MACRO_TO_STR_INDIR(__LINE__)
-
 #define LIBC_ASSERT(COND)                                                      \
   do {                                                                         \
     if (LIBC_UNLIKELY(!(COND))) {                                              \
-      LIBC_NAMESPACE::write_to_stderr(__FILE__ ":" __LIBC_LINE_STR__           \
-                                               ": Assertion failed: '" #COND   \
-                                               "' in function: '");            \
+      LIBC_NAMESPACE::write_to_stderr(__FILE__ ":" LLVM_LIBC_STRINGIFY(        \
+          __LINE__) ": Assertion failed: '" #COND "' in function: '");         \
       LIBC_NAMESPACE::write_to_stderr(__PRETTY_FUNCTION__);                    \
       LIBC_NAMESPACE::write_to_stderr("'\n");                                  \
       LIBC_NAMESPACE::internal::exit(0xFF);                                    \
diff --git a/libc/src/__support/macros/is_defined.h b/libc/src/__support/macros/is_defined.h
new file mode 100644
index 0000000000000..38a396ac02708
--- /dev/null
+++ b/libc/src/__support/macros/is_defined.h
@@ -0,0 +1,26 @@
+//===-- LLVM_IS_DEFINED macro ----------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MACROS_IS_DEFINED_H
+#define LLVM_LIBC_SRC___SUPPORT_MACROS_IS_DEFINED_H
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/macros/macro-utils.h"
+
+// LLVM_LIBC_IS_DEFINED checks whether a particular macro is defined.
+// Usage: constexpr bool kUseAvx = LLVM_LIBC_IS_DEFINED(__AVX__);
+//
+// This works by comparing the stringified version of the macro with and
+// without evaluation. If FOO is not undefined both stringifications yield
+// "FOO". If FOO is defined, one stringification yields "FOO" while the other
+// yields its stringified value such as "1".
+#define LLVM_LIBC_IS_DEFINED(macro)                                            \
+  (LIBC_NAMESPACE::cpp::string_view{LLVM_LIBC_STRINGIFY(macro)} !=             \
+   LIBC_NAMESPACE::cpp::string_view{#macro})
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_IS_DEFINED_H
diff --git a/libc/src/__support/macros/macro-utils.h b/libc/src/__support/macros/macro-utils.h
new file mode 100644
index 0000000000000..1831b820825fa
--- /dev/null
+++ b/libc/src/__support/macros/macro-utils.h
@@ -0,0 +1,16 @@
+//===-- Macros used by other macros ----------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MACROS_MACRO_UTILS_H
+#define LLVM_LIBC_SRC___SUPPORT_MACROS_MACRO_UTILS_H
+
+// Stringify the argument after an extra pass of macro expansion.
+#define LLVM_LIBC_STRINGIFY(x) LLVM_LIBC_STRINGIFY_IMPL(x)
+#define LLVM_LIBC_STRINGIFY_IMPL(x) #x
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_MACRO_UTILS_H
diff --git a/libc/src/string/memory_utils/op_aarch64.h b/libc/src/string/memory_utils/op_aarch64.h
index b5c3bb74e7418..4e116bc8a5e3c 100644
--- a/libc/src/string/memory_utils/op_aarch64.h
+++ b/libc/src/string/memory_utils/op_aarch64.h
@@ -15,6 +15,7 @@
 
 #include "src/__support/macros/attributes.h" // LIBC_INLINE
 #include "src/__support/macros/config.h"     // LIBC_NAMESPACE_DECL
+#include "src/__support/macros/is_defined.h"
 #include "src/__support/macros/properties/architectures.h"
 
 #if defined(LIBC_TARGET_ARCH_IS_AARCH64)
diff --git a/libc/src/string/memory_utils/op_x86.h b/libc/src/string/memory_utils/op_x86.h
index 4af2f23da5bc3..3afcbd02bd525 100644
--- a/libc/src/string/memory_utils/op_x86.h
+++ b/libc/src/string/memory_utils/op_x86.h
@@ -14,6 +14,7 @@
 
 #include "src/__support/macros/attributes.h" // LIBC_INLINE
 #include "src/__support/macros/config.h"     // LIBC_NAMESPACE_DECL
+#include "src/__support/macros/is_defined.h"
 #include "src/__support/macros/properties/architectures.h"
 #include "src/__support/macros/properties/compiler.h"
 
diff --git a/libc/src/string/memory_utils/x86_64/inline_memcpy.h b/libc/src/string/memory_utils/x86_64/inline_memcpy.h
index 8c1e1b4157eb7..e569ecceb80cc 100644
--- a/libc/src/string/memory_utils/x86_64/inline_memcpy.h
+++ b/libc/src/string/memory_utils/x86_64/inline_memcpy.h
@@ -8,8 +8,9 @@
 #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_X86_64_INLINE_MEMCPY_H
 #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_X86_64_INLINE_MEMCPY_H
 
-#include "hdr/stdint_proxy.h"                  // SIZE_MAX
-#include "src/__support/macros/attributes.h"   // LIBC_INLINE_VAR
+#include "hdr/stdint_proxy.h"                // SIZE_MAX
+#include "src/__support/macros/attributes.h" // LIBC_INLINE_VAR
+#include "src/__support/macros/is_defined.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 #include "src/string/memory_utils/op_builtin.h"
 #include "src/string/memory_utils/op_x86.h"
diff --git a/libc/src/string/memory_utils/x86_64/inline_memset.h b/libc/src/string/memory_utils/x86_64/inline_memset.h
index 35719a89fa600..04c92aa31b094 100644
--- a/libc/src/string/memory_utils/x86_64/inline_memset.h
+++ b/libc/src/string/memory_utils/x86_64/inline_memset.h
@@ -10,6 +10,7 @@
 
 #include "src/__support/macros/attributes.h" // LIBC_INLINE, LIBC_INLINE_VAR
 #include "src/__support/macros/config.h"     // LIBC_NAMESPACE_DECL
+#include "src/__support/macros/is_defined.h"
 #include "src/string/memory_utils/op_generic.h"
 #include "src/string/memory_utils/op_x86.h"
 #include "src/string/memory_utils/utils.h" // Ptr, CPtr

>From 3e0e6909479a40dd938b745ee780689ce82f81b9 Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Thu, 2 Apr 2026 11:56:24 -0700
Subject: [PATCH 2/2] add cmake rules for new headers

---
 libc/src/__support/macros/CMakeLists.txt    | 15 +++++++++++++++
 libc/src/string/memory_utils/CMakeLists.txt |  1 +
 2 files changed, 16 insertions(+)

diff --git a/libc/src/__support/macros/CMakeLists.txt b/libc/src/__support/macros/CMakeLists.txt
index 8e17642d02fb0..d0fb102911f35 100644
--- a/libc/src/__support/macros/CMakeLists.txt
+++ b/libc/src/__support/macros/CMakeLists.txt
@@ -39,3 +39,18 @@ add_header_library(
     .config
     .optimization
 )
+
+add_header_library(
+  macro_utils
+  HDRS
+    macro-utils.h
+)
+
+add_header_library(
+  is_defined
+  HDRS
+    is_defined.h
+  DEPENDS
+    .macro_utils
+    libc.src.__support.CPP.string_view
+)
diff --git a/libc/src/string/memory_utils/CMakeLists.txt b/libc/src/string/memory_utils/CMakeLists.txt
index 9cabfb9318012..510546d79988a 100644
--- a/libc/src/string/memory_utils/CMakeLists.txt
+++ b/libc/src/string/memory_utils/CMakeLists.txt
@@ -40,6 +40,7 @@ add_header_library(
     libc.src.__support.CPP.cstddef
     libc.src.__support.CPP.type_traits
     libc.src.__support.macros.config
+    libc.src.__support.macros.is_defined
     libc.src.__support.macros.optimization
     libc.src.__support.macros.properties.architectures
     libc.src.__support.macros.properties.compiler



More information about the libc-commits mailing list