[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