Skip to content

Commit 0323235

Browse files
authored
[libc] Add user defined literals to initialize BigInt and __uint128_t constants (llvm#81267)
Adds user defined literal to construct unsigned integer constants. This is useful when constructing constants for non native C++ types like `__uint128_t` or our custom `BigInt` type.
1 parent 16a0629 commit 0323235

File tree

9 files changed

+523
-213
lines changed

9 files changed

+523
-213
lines changed

libc/src/__support/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ add_header_library(
146146
libc.src.errno.errno
147147
)
148148

149+
add_header_library(
150+
integer_literals
151+
HDRS
152+
integer_literals.h
153+
DEPENDS
154+
.uint128
155+
libc.src.__support.CPP.limits
156+
)
157+
149158
add_header_library(
150159
integer_operations
151160
HDRS

libc/src/__support/integer_literals.h

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
//===-- User literal for unsigned integers ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// This set of user defined literals allows uniform constructions of constants
9+
// up to 256 bits and also help with unit tests (EXPECT_EQ requires the same
10+
// type for LHS and RHS).
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
14+
#define LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
15+
16+
#include "src/__support/CPP/limits.h" // CHAR_BIT
17+
#include "src/__support/UInt128.h" // UInt128
18+
#include "src/__support/macros/attributes.h" // LIBC_INLINE
19+
#include <stddef.h> // size_t
20+
#include <stdint.h> // uintxx_t
21+
22+
namespace LIBC_NAMESPACE {
23+
24+
LIBC_INLINE constexpr uint8_t operator""_u8(unsigned long long value) {
25+
return value;
26+
}
27+
28+
LIBC_INLINE constexpr uint16_t operator""_u16(unsigned long long value) {
29+
return value;
30+
}
31+
32+
LIBC_INLINE constexpr uint32_t operator""_u32(unsigned long long value) {
33+
return value;
34+
}
35+
36+
LIBC_INLINE constexpr uint64_t operator""_u64(unsigned long long value) {
37+
return value;
38+
}
39+
40+
namespace internal {
41+
42+
// Creates a T by reading digits from an array.
43+
template <typename T>
44+
LIBC_INLINE constexpr T accumulate(int base, const uint8_t *digits,
45+
size_t size) {
46+
T value{};
47+
for (; size; ++digits, --size) {
48+
value *= base;
49+
value += *digits;
50+
}
51+
return value;
52+
}
53+
54+
// A static buffer to hold the digits for a T.
55+
template <typename T, int base> struct DigitBuffer {
56+
static_assert(base == 2 || base == 10 || base == 16);
57+
// One character provides log2(base) bits.
58+
// Base 2 and 16 provide exactly one and four bits per character respectively.
59+
// For base 10, a character provides log2(10) ≈ 3.32... which we round to 3
60+
// for the purpose of buffer allocation.
61+
LIBC_INLINE_VAR static constexpr size_t BITS_PER_DIGIT = base == 2 ? 1
62+
: base == 10 ? 3
63+
: base == 16 ? 4
64+
: 0;
65+
LIBC_INLINE_VAR static constexpr size_t MAX_DIGITS =
66+
sizeof(T) * CHAR_BIT / BITS_PER_DIGIT;
67+
68+
uint8_t digits[MAX_DIGITS] = {};
69+
size_t size = 0;
70+
71+
constexpr DigitBuffer(const char *str) {
72+
for (; *str != '\0'; ++str)
73+
push(*str);
74+
}
75+
76+
// Returns the digit for a particular character.
77+
// Returns 255 if the character is invalid.
78+
LIBC_INLINE static constexpr uint8_t get_digit_value(const char c) {
79+
const auto to_lower = [](char c) { return c | 32; };
80+
const auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
81+
const auto is_alpha = [](char c) {
82+
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
83+
};
84+
if (is_digit(c))
85+
return c - '0';
86+
if (base > 10 && is_alpha(c))
87+
return to_lower(c) - 'a' + 10;
88+
return 255;
89+
}
90+
91+
// Adds a single character to this buffer.
92+
LIBC_INLINE constexpr void push(char c) {
93+
if (c == '\'')
94+
return; // ' is valid but not taken into account.
95+
const uint8_t value = get_digit_value(c);
96+
if (value == 255 || size >= MAX_DIGITS) {
97+
// During constant evaluation `__builtin_unreachable` will halt the
98+
// compiler as it is not executable. This is preferable over `assert` that
99+
// will only trigger in debug mode. Also we can't use `static_assert`
100+
// because `value` and `size` are not constant.
101+
__builtin_unreachable(); // invalid or too many characters.
102+
}
103+
digits[size] = value;
104+
++size;
105+
}
106+
};
107+
108+
// Generic implementation for native types (including __uint128_t or ExtInt
109+
// where available).
110+
template <typename T> struct Parser {
111+
template <int base> LIBC_INLINE static constexpr T parse(const char *str) {
112+
const DigitBuffer<T, base> buffer(str);
113+
return accumulate<T>(base, buffer.digits, buffer.size);
114+
}
115+
};
116+
117+
// Specialization for cpp::BigInt<N, false, uint64_t>.
118+
// Because this code runs at compile time we try to make it efficient. For
119+
// binary and hexadecimal formats we read digits by chunks of 64 bits and
120+
// produce the BigInt internal representation direcly. For decimal numbers we
121+
// go the slow path and use slower BigInt arithmetic.
122+
template <size_t N>
123+
struct Parser<LIBC_NAMESPACE::cpp::BigInt<N, false, uint64_t>> {
124+
using UIntT = cpp::BigInt<N, false, uint64_t>;
125+
template <int base> static constexpr UIntT parse(const char *str) {
126+
const DigitBuffer<UIntT, base> buffer(str);
127+
if constexpr (base == 10) {
128+
// Slow path, we sum and multiply BigInt for each digit.
129+
return accumulate<UIntT>(base, buffer.digits, buffer.size);
130+
} else {
131+
// Fast path, we consume blocks of uint64_t and creates the BigInt's
132+
// internal representation directly.
133+
using U64ArrayT = cpp::array<uint64_t, UIntT::WORD_COUNT>;
134+
U64ArrayT array;
135+
size_t size = buffer.size;
136+
const uint8_t *digit_ptr = buffer.digits + size;
137+
for (size_t i = 0; i < array.size(); ++i) {
138+
constexpr size_t U64_DIGITS = DigitBuffer<uint64_t, base>::MAX_DIGITS;
139+
const size_t chunk = size > U64_DIGITS ? U64_DIGITS : size;
140+
digit_ptr -= chunk;
141+
size -= chunk;
142+
array[i] = accumulate<uint64_t>(base, digit_ptr, chunk);
143+
}
144+
return UIntT(array);
145+
}
146+
}
147+
};
148+
149+
// Detects the base of the number and dispatches to the right implementation.
150+
template <typename T>
151+
LIBC_INLINE constexpr T parse_with_prefix(const char *ptr) {
152+
using P = Parser<T>;
153+
if (ptr[0] == '0' && ptr[1] == 'x')
154+
return P::template parse<16>(ptr + 2);
155+
else if (ptr[0] == '0' && ptr[1] == 'b')
156+
return P::template parse<2>(ptr + 2);
157+
else
158+
return P::template parse<10>(ptr);
159+
}
160+
161+
} // namespace internal
162+
163+
LIBC_INLINE constexpr UInt128 operator""_u128(const char *x) {
164+
return internal::parse_with_prefix<UInt128>(x);
165+
}
166+
167+
LIBC_INLINE constexpr auto operator""_u256(const char *x) {
168+
return internal::parse_with_prefix<cpp::UInt<256>>(x);
169+
}
170+
171+
} // namespace LIBC_NAMESPACE
172+
173+
#endif // LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H

libc/test/src/__support/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ add_libc_test(
9797
libc.src.__support.CPP.optional
9898
)
9999

100+
add_libc_test(
101+
integer_literals_test
102+
SUITE
103+
libc-support-tests
104+
SRCS
105+
integer_literals_test.cpp
106+
DEPENDS
107+
libc.src.__support.integer_literals
108+
libc.src.__support.CPP.optional
109+
)
110+
100111
add_libc_test(
101112
fixedvector_test
102113
SUITE

libc/test/src/__support/FPUtil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_libc_test(
2323
DEPENDS
2424
libc.src.__support.FPUtil.fp_bits
2525
libc.src.__support.FPUtil.fpbits_str
26+
libc.src.__support.integer_literals
2627
)
2728

2829
add_fp_unittest(

0 commit comments

Comments
 (0)