[libc-commits] [libc] [libc] Add internal locale structures and config options (PR #211470)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Tue Sep 29 12:48:24 PDT 2026
https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/211470
>From 5b6f10b8f8081e6b2c32e6d1f53619fe6f95b30b Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 21 Jul 2026 00:00:17 +0100
Subject: [PATCH] [libc] Add internal locale structures and config options
Add internal category data structures and static locale objects for C
and UTF-8 locales. Introduce LIBC_CONF_DEFAULT_LOCALE and
LIBC_CONF_DISABLE_RUNTIME_LOCALE configuration options.
When LIBC_CONF_DISABLE_RUNTIME_LOCALE is set, the locale subsystem is
designed to eliminate all runtime overhead. Indirection through
locale_t objects and thread-local state lookups are bypassed in favour
of direct, compile-time constant lookups, allowing all runtime locale
machinery and dynamic objects to be elided for embedded environments.
Assisted-by: Automated tooling, human reviewed.
---
.../modules/LLVMLibCCompileOptionRules.cmake | 10 ++
libc/config/config.json | 10 ++
libc/include/llvm-libc-macros/locale-macros.h | 4 +-
libc/include/llvm-libc-types/locale_t.h | 8 --
libc/src/locale/CMakeLists.txt | 1 +
libc/src/locale/locale.cpp | 30 +++++-
libc/src/locale/locale.h | 66 ++++++++++--
libc/src/locale/locale_data.h | 100 ++++++++++++++++++
libc/src/locale/newlocale.cpp | 13 ++-
libc/src/locale/uselocale.cpp | 29 +++--
libc/test/src/locale/locale_test.cpp | 16 ++-
11 files changed, 250 insertions(+), 37 deletions(-)
create mode 100644 libc/src/locale/locale_data.h
diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index 79f2d80bcba7a1..27ba8e12bb8ddc 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -169,6 +169,16 @@ function(_get_compile_options_from_config output_var)
libc_add_definition(config_options "LIBC_CONF_WCTYPE_MODE=${LIBC_CONF_WCTYPE_MODE}")
endif()
+ if(LIBC_CONF_DEFAULT_LOCALE)
+ if("${LIBC_CONF_DEFAULT_LOCALE}" MATCHES ".*UTF-8" OR "${LIBC_CONF_DEFAULT_LOCALE}" MATCHES ".*utf8")
+ libc_add_definition(config_options "LIBC_CONF_DEFAULT_LOCALE_IS_UTF8")
+ endif()
+ endif()
+
+ if(LIBC_CONF_DISABLE_RUNTIME_LOCALE)
+ libc_add_definition(config_options "LIBC_CONF_DISABLE_RUNTIME_LOCALE")
+ endif()
+
if(LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT)
libc_add_definition(config_options "LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}")
endif()
diff --git a/libc/config/config.json b/libc/config/config.json
index 604b03c9b5688a..ee0c09b4770b64 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -204,5 +204,15 @@
"value": "LIBC_HARDENING_MODE_NONE",
"doc": "Hardening mode for the library."
}
+ },
+ "locale": {
+ "LIBC_CONF_DEFAULT_LOCALE": {
+ "value": "C",
+ "doc": "The default locale for the C library. Options: 'C', 'C.UTF-8', 'en_US.UTF-8'."
+ },
+ "LIBC_CONF_DISABLE_RUNTIME_LOCALE": {
+ "value": false,
+ "doc": "Bypass runtime locale objects and use hardcoded lookups for embedded builds."
+ }
}
}
diff --git a/libc/include/llvm-libc-macros/locale-macros.h b/libc/include/llvm-libc-macros/locale-macros.h
index 892f8b69f3a777..b95a56462f2e7f 100644
--- a/libc/include/llvm-libc-macros/locale-macros.h
+++ b/libc/include/llvm-libc-macros/locale-macros.h
@@ -27,6 +27,8 @@
#define LC_COLLATE_MASK (1 << LC_COLLATE)
#define LC_MONETARY_MASK (1 << LC_MONETARY)
#define LC_MESSAGES_MASK (1 << LC_MESSAGES)
-#define LC_ALL_MASK 0x7fffffff
+#define LC_ALL_MASK \
+ (LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK | \
+ LC_MONETARY_MASK | LC_MESSAGES_MASK)
#endif // LLVM_LIBC_MACROS_LOCALE_MACROS_H
diff --git a/libc/include/llvm-libc-types/locale_t.h b/libc/include/llvm-libc-types/locale_t.h
index 6d783001acf9f2..1424f70115af7c 100644
--- a/libc/include/llvm-libc-types/locale_t.h
+++ b/libc/include/llvm-libc-types/locale_t.h
@@ -9,14 +9,6 @@
#ifndef LLVM_LIBC_TYPES_LOCALE_T_H
#define LLVM_LIBC_TYPES_LOCALE_T_H
-#define NUM_LOCALE_CATEGORIES 6
-
-struct __locale_data;
-
-struct __locale_t {
- struct __locale_data *data[NUM_LOCALE_CATEGORIES];
-};
-
typedef struct __locale_t *locale_t;
#endif // LLVM_LIBC_TYPES_LOCALE_T_H
diff --git a/libc/src/locale/CMakeLists.txt b/libc/src/locale/CMakeLists.txt
index 9a3abf76a55c5a..84826e7032d38d 100644
--- a/libc/src/locale/CMakeLists.txt
+++ b/libc/src/locale/CMakeLists.txt
@@ -4,6 +4,7 @@ add_object_library(
locale.cpp
HDRS
locale.h
+ locale_data.h
DEPENDS
libc.hdr.types.locale_t
)
diff --git a/libc/src/locale/locale.cpp b/libc/src/locale/locale.cpp
index 2f7e13fb1d6f78..b231dede0decf2 100644
--- a/libc/src/locale/locale.cpp
+++ b/libc/src/locale/locale.cpp
@@ -1,19 +1,43 @@
-//===-- Implementation of locale ------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of static locale instances and locale category data.
+///
+//===----------------------------------------------------------------------===//
#include "src/locale/locale.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/locale/locale_data.h"
namespace LIBC_NAMESPACE_DECL {
-__locale_t c_locale = {nullptr};
+__locale_t c_locale(&C_CTYPE_DATA, &C_NUMERIC_DATA, &C_TIME_DATA, nullptr,
+ &C_MONETARY_DATA, &C_MESSAGES_DATA);
+
+__locale_t utf8_locale(&UTF8_CTYPE_DATA, &C_NUMERIC_DATA, &C_TIME_DATA, nullptr,
+ &C_MONETARY_DATA, &C_MESSAGES_DATA);
+
+locale_t global_locale = DEFAULT_LOCALE_IS_UTF8 ? &utf8_locale : &c_locale;
+
+[[maybe_unused]] static LIBC_THREAD_LOCAL locale_t thread_locale = nullptr;
+
+locale_t get_thread_locale() {
+ if constexpr (DISABLE_RUNTIME_LOCALE)
+ return nullptr;
+ else
+ return thread_locale;
+}
-locale_t locale = nullptr;
+void set_thread_locale(locale_t loc) {
+ if constexpr (!DISABLE_RUNTIME_LOCALE)
+ thread_locale = loc;
+}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/locale/locale.h b/libc/src/locale/locale.h
index 14befa6e7a9c24..09e8006f09db11 100644
--- a/libc/src/locale/locale.h
+++ b/libc/src/locale/locale.h
@@ -1,35 +1,87 @@
-//===-- Implementation header for the locale --------------------*- 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for locale state and structures.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_LOCALE_LOCALECONV_H
#define LLVM_LIBC_SRC_LOCALE_LOCALECONV_H
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
+#include "src/locale/locale_data.h"
+#include "hdr/locale_macros.h"
#include "hdr/types/locale_t.h"
-#include <stddef.h>
+#ifndef LC_GLOBAL_LOCALE
+#define LC_GLOBAL_LOCALE ((locale_t)(-1))
+#endif
namespace LIBC_NAMESPACE_DECL {
-// We only support the "C" locale right now.
-static constexpr size_t MAX_LOCALE_NAME_SIZE = 2;
+template <bool DisableRuntimeLocale> struct LocaleStorage {
+ const LcCtypeData *ctype_ptr;
+ const LcNumericData *numeric_ptr;
+ const LcTimeData *time_ptr;
+ const void *collate_ptr;
+ const LcMonetaryData *monetary_ptr;
+ const LcMessagesData *messages_ptr;
+
+ constexpr LocaleStorage(const LcCtypeData *c, const LcNumericData *num,
+ const LcTimeData *t, const void *col,
+ const LcMonetaryData *mon, const LcMessagesData *msg)
+ : ctype_ptr(c), numeric_ptr(num), time_ptr(t), collate_ptr(col),
+ monetary_ptr(mon), messages_ptr(msg) {}
+
+ constexpr const LcCtypeData *ctype() const { return ctype_ptr; }
+ constexpr const LcNumericData *numeric() const { return numeric_ptr; }
+ constexpr const LcTimeData *time() const { return time_ptr; }
+ constexpr const LcMonetaryData *monetary() const { return monetary_ptr; }
+ constexpr const LcMessagesData *messages() const { return messages_ptr; }
+};
+
+template <> struct LocaleStorage<true> {
+ constexpr LocaleStorage(const LcCtypeData *, const LcNumericData *,
+ const LcTimeData *, const void *,
+ const LcMonetaryData *, const LcMessagesData *) {}
+
+ constexpr const LcCtypeData *ctype() const { return nullptr; }
+ constexpr const LcNumericData *numeric() const { return nullptr; }
+ constexpr const LcTimeData *time() const { return nullptr; }
+ constexpr const LcMonetaryData *monetary() const { return nullptr; }
+ constexpr const LcMessagesData *messages() const { return nullptr; }
+};
+
+} // namespace LIBC_NAMESPACE_DECL
-struct __locale_data {
- char name[MAX_LOCALE_NAME_SIZE];
+struct __locale_t
+ : LIBC_NAMESPACE::LocaleStorage<LIBC_NAMESPACE::DISABLE_RUNTIME_LOCALE> {
+ using LIBC_NAMESPACE::LocaleStorage<
+ LIBC_NAMESPACE::DISABLE_RUNTIME_LOCALE>::LocaleStorage;
};
+namespace LIBC_NAMESPACE_DECL {
+
// The pointer to the default "C" locale.
extern __locale_t c_locale;
+// The pointer to the static UTF-8 locale.
+extern __locale_t utf8_locale;
+
// The global locale instance.
-extern locale_t locale;
+extern locale_t global_locale;
+
+// Thread-local locale accessor functions.
+locale_t get_thread_locale();
+void set_thread_locale(locale_t loc);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/locale/locale_data.h b/libc/src/locale/locale_data.h
new file mode 100644
index 00000000000000..4e01b5a9ecdd65
--- /dev/null
+++ b/libc/src/locale/locale_data.h
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Internal structures for locale category data.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LOCALE_LOCALE_DATA_H
+#define LLVM_LIBC_SRC_LOCALE_LOCALE_DATA_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+#ifdef LIBC_CONF_DEFAULT_LOCALE_IS_UTF8
+inline constexpr bool DEFAULT_LOCALE_IS_UTF8 = true;
+#else
+inline constexpr bool DEFAULT_LOCALE_IS_UTF8 = false;
+#endif
+
+#ifdef LIBC_CONF_DISABLE_RUNTIME_LOCALE
+inline constexpr bool DISABLE_RUNTIME_LOCALE = true;
+#else
+inline constexpr bool DISABLE_RUNTIME_LOCALE = false;
+#endif
+
+struct LcCtypeData {
+ const char *codeset;
+};
+
+struct LcNumericData {
+ const char *radixchar;
+ const char *thousep;
+};
+
+struct LcTimeData {
+ const char *d_t_fmt;
+ const char *d_fmt;
+ const char *t_fmt;
+ const char *t_fmt_ampm;
+ const char *am_str;
+ const char *pm_str;
+ const char *days[7];
+ const char *ab_days[7];
+ const char *months[12];
+ const char *ab_months[12];
+ const char *era;
+ const char *era_d_fmt;
+ const char *era_d_t_fmt;
+ const char *era_t_fmt;
+ const char *alt_digits;
+};
+
+struct LcMonetaryData {
+ const char *crncystr;
+};
+
+struct LcMessagesData {
+ const char *yesexpr;
+ const char *noexpr;
+};
+
+inline constexpr LcCtypeData C_CTYPE_DATA = {"US-ASCII"};
+inline constexpr LcCtypeData UTF8_CTYPE_DATA = {"UTF-8"};
+
+inline constexpr LcNumericData C_NUMERIC_DATA = {".", ""};
+
+inline constexpr LcTimeData C_TIME_DATA = {
+ "%a %b %e %H:%M:%S %Y",
+ "%m/%d/%y",
+ "%H:%M:%S",
+ "%I:%M:%S %p",
+ "AM",
+ "PM",
+ {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
+ "Saturday"},
+ {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
+ {"January", "February", "March", "April", "May", "June", "July", "August",
+ "September", "October", "November", "December"},
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec"},
+ "",
+ "",
+ "",
+ "",
+ ""};
+
+inline constexpr LcMonetaryData C_MONETARY_DATA = {""};
+
+inline constexpr LcMessagesData C_MESSAGES_DATA = {"^[yY]", "^[nN]"};
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LOCALE_LOCALE_DATA_H
diff --git a/libc/src/locale/newlocale.cpp b/libc/src/locale/newlocale.cpp
index 246d31c95cb581..c694cb62a588c8 100644
--- a/libc/src/locale/newlocale.cpp
+++ b/libc/src/locale/newlocale.cpp
@@ -1,10 +1,15 @@
-//===-- Implementation of newlocale ---------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of newlocale.
+///
+//===----------------------------------------------------------------------===//
#include "src/locale/newlocale.h"
#include "hdr/locale_macros.h"
@@ -17,8 +22,12 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(locale_t, newlocale,
(int category_mask, const char *locale_name, locale_t)) {
+ if (!locale_name)
+ return nullptr;
+
cpp::string_view name(locale_name);
- if ((category_mask & ~LC_ALL_MASK) != 0 || (!name.empty() && name != "C"))
+ if ((category_mask & ~LC_ALL_MASK) != 0 ||
+ (!name.empty() && name != "C" && name != "POSIX"))
return nullptr;
return &c_locale;
diff --git a/libc/src/locale/uselocale.cpp b/libc/src/locale/uselocale.cpp
index d6fdad248f12b2..00d1e61d98386d 100644
--- a/libc/src/locale/uselocale.cpp
+++ b/libc/src/locale/uselocale.cpp
@@ -1,23 +1,40 @@
-//===-- Implementation of uselocale ---------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of uselocale.
+///
+//===----------------------------------------------------------------------===//
#include "src/locale/uselocale.h"
-#include "src/locale/locale.h"
-
+#include "hdr/locale_macros.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/locale/locale.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(locale_t, uselocale, (locale_t newloc)) {
- if (!newloc)
- return locale;
- return locale = newloc;
+ if constexpr (DISABLE_RUNTIME_LOCALE) {
+ (void)newloc;
+ return LC_GLOBAL_LOCALE;
+ } else {
+ locale_t oldloc = get_thread_locale();
+
+ if (newloc != nullptr) {
+ if (newloc == LC_GLOBAL_LOCALE)
+ set_thread_locale(nullptr);
+ else
+ set_thread_locale(newloc);
+ }
+
+ return (oldloc == nullptr) ? LC_GLOBAL_LOCALE : oldloc;
+ }
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/locale/locale_test.cpp b/libc/test/src/locale/locale_test.cpp
index d19e2605fca375..e4df93f37a0786 100644
--- a/libc/test/src/locale/locale_test.cpp
+++ b/libc/test/src/locale/locale_test.cpp
@@ -14,34 +14,30 @@
TEST(LlvmLibcLocale, DefaultLocale) {
locale_t new_locale = LIBC_NAMESPACE::newlocale(LC_ALL_MASK, "C", nullptr);
- EXPECT_NE(new_locale, static_cast<locale_t>(nullptr));
+ ASSERT_NE(new_locale, static_cast<locale_t>(nullptr));
locale_t old_locale = LIBC_NAMESPACE::uselocale(new_locale);
- EXPECT_NE(old_locale, static_cast<locale_t>(nullptr));
+ ASSERT_NE(old_locale, static_cast<locale_t>(nullptr));
LIBC_NAMESPACE::freelocale(new_locale);
- LIBC_NAMESPACE::uselocale(old_locale);
+ locale_t restored_locale = LIBC_NAMESPACE::uselocale(old_locale);
+ ASSERT_NE(restored_locale, static_cast<locale_t>(nullptr));
}
TEST(LlvmLibcLocale, NewLocaleValidation) {
- // Choosing masks within LC_*_MASK is OK.
locale_t loc =
LIBC_NAMESPACE::newlocale(LC_CTYPE_MASK | LC_NUMERIC_MASK, "C", nullptr);
- EXPECT_NE(loc, static_cast<locale_t>(nullptr));
+ ASSERT_NE(loc, static_cast<locale_t>(nullptr));
LIBC_NAMESPACE::freelocale(loc);
- // Empty locale name is implementation-defined,
- // defaults to C locale.
loc = LIBC_NAMESPACE::newlocale(LC_ALL_MASK, "", nullptr);
- EXPECT_NE(loc, static_cast<locale_t>(nullptr));
+ ASSERT_NE(loc, static_cast<locale_t>(nullptr));
LIBC_NAMESPACE::freelocale(loc);
- // Masks outside the valid range are rejected.
loc = LIBC_NAMESPACE::newlocale(~0, "C", nullptr);
EXPECT_EQ(loc, static_cast<locale_t>(nullptr));
- // Invalid locale name is rejected.
loc = LIBC_NAMESPACE::newlocale(LC_ALL_MASK, "does-not-exist", nullptr);
EXPECT_EQ(loc, static_cast<locale_t>(nullptr));
}
More information about the libc-commits
mailing list