[libc-commits] [libc] [llvm] [libc] Add user defined literals to ease testing (PR #81267)

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Mon Feb 12 02:33:49 PST 2024


================
@@ -0,0 +1,166 @@
+//===-- User literal for unsigned integers ----------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+// This set of user defined literals allows uniform constructions of constants
+// up to 256 bits and also help with unit tests (EXPECT_EQ requires the same
+// type for LHS and RHS).
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
+#define LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
+
+#include "src/__support/UInt128.h"           // UInt128
+#include "src/__support/macros/attributes.h" // LIBC_INLINE
+#include <limits.h>                          // CHAR_BIT
+#include <stddef.h>                          // size_t
+#include <stdint.h>                          // __uintxx_t
+
+namespace LIBC_NAMESPACE {
+
+LIBC_INLINE constexpr __uint8_t operator""_u8(unsigned long long value) {
+  return value;
+}
+
+LIBC_INLINE constexpr __uint16_t operator""_u16(unsigned long long value) {
+  return value;
+}
+
+LIBC_INLINE constexpr __uint32_t operator""_u32(unsigned long long value) {
+  return value;
+}
+
+LIBC_INLINE constexpr __uint64_t operator""_u64(unsigned long long value) {
+  return value;
+}
+
+namespace internal {
+
+// Creates a T by reading digits from an array.
+template <typename T>
+LIBC_INLINE constexpr T accumulate(int base, const uint8_t *digits,
+                                   size_t size) {
+  T value{};
+  for (; size; ++digits, --size) {
+    value *= base;
+    value += *digits;
+  }
+  return value;
+}
+
+// A static buffer to hold the digits for a T.
+template <typename T, int base> struct DigitBuffer {
+  static_assert(base == 2 || base == 10 || base == 16);
+  // Base 2: one char provides exactly one bit.
+  // Base 10: one char provides between three and four bits.
+  // Base 16: one char provides exactly four bits.
+  LIBC_INLINE_VAR static constexpr size_t BITS_PER_DIGIT = base == 2    ? 1
+                                                           : base == 10 ? 3
+                                                           : base == 16 ? 4
+                                                                        : 0;
+  LIBC_INLINE_VAR static constexpr size_t MAX_DIGITS =
+      sizeof(T) * CHAR_BIT / BITS_PER_DIGIT;
+
+  uint8_t digits[MAX_DIGITS] = {};
+  size_t size = 0;
+
+  constexpr DigitBuffer(const char *str) {
+    for (; *str != '\0'; ++str)
+      push(*str);
+  }
+
+  // Returns the digit for a particular character.
+  // Returns 255 if the character is invalid.
+  LIBC_INLINE static constexpr uint8_t get_digit_value(const char c) {
+    const auto to_lower = [](char c) { return c | 32; };
+    const auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+    const auto is_lower = [](char c) { return 'a' <= c && c <= 'z'; };
+    const auto is_upper = [](char c) { return 'A' <= c && c <= 'Z'; };
+    const auto is_alpha = [&](char c) { return is_lower(c) || is_upper(c); };
+    if (is_digit(c))
+      return c - '0';
+    if (base > 10 && is_alpha(c))
+      return to_lower(c) - 'a' + 10;
+    return 255;
+  }
+
+  // Adds a single character to this buffer.
+  LIBC_INLINE constexpr void push(char c) {
+    if (c == '\'')
+      return; // ' is valid but not taken into account.
+    const uint8_t value = get_digit_value(c);
+    if (value == 255 || size >= MAX_DIGITS)
+      __builtin_unreachable(); // invalid or too many characters.
----------------
gchatelet wrote:

These user defined literals are used to build constants so they should run at compile time exclusively. Since LLVM libc is C++17 we don't have ways to enforce it through `consteval` but that's the idea nonetheless. If the function is called in a constant context it will force the compile time evaluation of the function.
The nice property of `__builtin_unreachable` is that it is not executable by the compiler and so the error is pretty clear : https://godbolt.org/z/PT3j1cKc7

If we go with `assert`, we will only catch the issue when compiling in debug mode.

https://github.com/llvm/llvm-project/pull/81267


More information about the libc-commits mailing list