38 changes: 38 additions & 0 deletions libc/src/__support/number_pair.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===-- Utilities for pairs of numbers. -------------------------*- 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_NUMBER_PAIR_H
#define LLVM_LIBC_SRC_SUPPORT_NUMBER_PAIR_H

#include "CPP/type_traits.h"

#include <stdint.h>

namespace __llvm_libc {

template <typename T> struct NumberPair {
T lo;
T hi;
};

using DoubleDouble = NumberPair<double>;

template <typename T>
cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, NumberPair<T>>
split(T a) {
constexpr size_t HALF_BIT_WIDTH = sizeof(T) * 4;
constexpr T LOWER_HALF_MASK = (T(1) << HALF_BIT_WIDTH) - T(1);
NumberPair<T> result;
result.lo = a & LOWER_HALF_MASK;
result.hi = a >> HALF_BIT_WIDTH;
return result;
}

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SUPPORT_NUMBER_PAIR_H
40 changes: 40 additions & 0 deletions libc/test/src/__support/uint128_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// we use a sugar which does not conflict with the UInt128 type which can
// resolve to __uint128_t if the platform has it.
using LL_UInt128 = __llvm_libc::cpp::UInt<128>;
using LL_UInt192 = __llvm_libc::cpp::UInt<192>;
using LL_UInt256 = __llvm_libc::cpp::UInt<256>;

TEST(LlvmLibcUInt128ClassTest, BasicInit) {
LL_UInt128 empty;
Expand Down Expand Up @@ -43,6 +45,24 @@ TEST(LlvmLibcUInt128ClassTest, AdditionTests) {
LL_UInt128 result3({0x12346789bcdf1233, 0xa987765443210fed});
EXPECT_EQ(val5 + val6, result3);
EXPECT_EQ(val5 + val6, val6 + val5);

// Test 192-bit addition
LL_UInt192 val7({0x0123456789abcdef, 0xfedcba9876543210, 0xfedcba9889abcdef});
LL_UInt192 val8({0x1111222233334444, 0xaaaabbbbccccdddd, 0xeeeeffffeeeeffff});
LL_UInt192 result4(
{0x12346789bcdf1233, 0xa987765443210fed, 0xedcbba98789acdef});
EXPECT_EQ(val7 + val8, result4);
EXPECT_EQ(val7 + val8, val8 + val7);

// Test 256-bit addition
LL_UInt256 val9({0x1f1e1d1c1b1a1918, 0xf1f2f3f4f5f6f7f8, 0x0123456789abcdef,
0xfedcba9876543210});
LL_UInt256 val10({0x1111222233334444, 0xaaaabbbbccccdddd, 0x1111222233334444,
0xaaaabbbbccccdddd});
LL_UInt256 result5({0x302f3f3e4e4d5d5c, 0x9c9dafb0c2c3d5d5,
0x12346789bcdf1234, 0xa987765443210fed});
EXPECT_EQ(val9 + val10, result5);
EXPECT_EQ(val9 + val10, val10 + val9);
}

TEST(LlvmLibcUInt128ClassTest, SubtractionTests) {
Expand Down Expand Up @@ -112,6 +132,26 @@ TEST(LlvmLibcUInt128ClassTest, MultiplicationTests) {
LL_UInt128 result5({0x917cf11d1e039c50, 0x3a4f32d17f40d08f});
EXPECT_EQ((val9 * val10), result5);
EXPECT_EQ((val9 * val10), (val10 * val9));

// Test 192-bit multiplication
LL_UInt192 val11(
{0xffffffffffffffff, 0x01D762422C946590, 0x9F4F2726179A2245});
LL_UInt192 val12(
{0xffffffffffffffff, 0x3792F412CB06794D, 0xCDB02555653131B6});

LL_UInt192 result6(
{0x0000000000000001, 0xc695a9ab08652121, 0x5de7faf698d32732});
EXPECT_EQ((val11 * val12), result6);
EXPECT_EQ((val11 * val12), (val12 * val11));

LL_UInt256 val13({0xffffffffffffffff, 0x01D762422C946590, 0x9F4F2726179A2245,
0xffffffffffffffff});
LL_UInt256 val14({0xffffffffffffffff, 0xffffffffffffffff, 0x3792F412CB06794D,
0xCDB02555653131B6});
LL_UInt256 result7({0x0000000000000001, 0xfe289dbdd36b9a6f,
0x291de4c71d5f646c, 0xfd37221cb06d4978});
EXPECT_EQ((val13 * val14), result7);
EXPECT_EQ((val13 * val14), (val14 * val13));
}

TEST(LlvmLibcUInt128ClassTest, DivisionTests) {
Expand Down
64 changes: 35 additions & 29 deletions libc/utils/UnitTest/LibcTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,47 +35,43 @@ class RunContext {

namespace internal {

// When the value is of integral type, just display it as normal.
template <typename ValType>
cpp::enable_if_t<cpp::is_integral_v<ValType>, std::string>
describeValue(ValType Value) {
return std::to_string(Value);
}

std::string describeValue(std::string Value) { return std::string(Value); }
std::string describeValue(cpp::string_view Value) {
return std::string(Value.data(), Value.size());
}

// When the value is UInt128 or __uint128_t, show its hexadecimal digits.
// We cannot just use a UInt128 specialization as that resolves to only
// one type, UInt<128> or __uint128_t. We want both overloads as we want to
// be able to unittest UInt<128> on platforms where UInt128 resolves to
// UInt128.
// TODO(lntue): Investigate why UInt<128> was printed backward, with the lower
// 64-bits first.
template <typename UInt128Type>
std::string describeValue128(UInt128Type Value) {
std::string S(sizeof(UInt128) * 2, '0');

for (auto I = S.rbegin(), End = S.rend(); I != End; ++I, Value >>= 4) {
unsigned char Mod = static_cast<unsigned char>(Value) & 15;
*I = Mod < 10 ? '0' + Mod : 'a' + Mod - 10;
template <typename T>
cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, std::string>
describeValueUInt(T Value) {
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
std::string S(sizeof(T) * 2, '0');

constexpr char HEXADECIMALS[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

for (auto I = S.rbegin(), End = S.rend(); I != End; ++I, Value >>= 8) {
unsigned char Mod = static_cast<unsigned char>(Value) & 0xFF;
*(I++) = HEXADECIMALS[Mod & 0x0F];
*I = HEXADECIMALS[Mod >> 4];
}

return "0x" + S;
}

#ifdef __SIZEOF_INT128__
template <> std::string describeValue<__uint128_t>(__uint128_t Value) {
return describeValue128(Value);
// When the value is of integral type, just display it as normal.
template <typename ValType>
cpp::enable_if_t<cpp::is_integral_v<ValType>, std::string>
describeValue(ValType Value) {
if constexpr (sizeof(ValType) <= sizeof(uint64_t)) {
return std::to_string(Value);
} else {
return describeValueUInt(Value);
}
}
#endif

template <>
std::string
describeValue<__llvm_libc::cpp::UInt<128>>(__llvm_libc::cpp::UInt<128> Value) {
return describeValue128(Value);
std::string describeValue(std::string Value) { return std::string(Value); }
std::string describeValue(cpp::string_view Value) {
return std::string(Value.data(), Value.size());
}

template <typename ValType>
Expand Down Expand Up @@ -282,6 +278,16 @@ template bool test<__llvm_libc::cpp::UInt<128>>(
__llvm_libc::cpp::UInt<128> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);

template bool test<__llvm_libc::cpp::UInt<192>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<192> LHS,
__llvm_libc::cpp::UInt<192> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);

template bool test<__llvm_libc::cpp::UInt<256>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<256> LHS,
__llvm_libc::cpp::UInt<256> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);

template bool test<__llvm_libc::cpp::string_view>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::string_view LHS,
__llvm_libc::cpp::string_view RHS, const char *LHSStr, const char *RHSStr,
Expand Down
22 changes: 22 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@ cc_library(
],
)

cc_library(
name = "__support_number_pair",
hdrs = ["src/__support/number_pair.h"],
deps = [
"__support_cpp_type_traits",
":libc_root",
],
)

cc_library(
name = "__support_integer_utils",
hdrs = ["src/__support/integer_utils.h"],
deps = [
"__support_builtin_wrappers",
"__support_cpp_type_traits",
"__support_number_pair",
":libc_root",
],
)

cc_library(
name = "__support_uint",
hdrs = ["src/__support/UInt.h"],
Expand All @@ -136,6 +156,8 @@ cc_library(
"__support_cpp_type_traits",
"__support_cpp_optional",
"__support_builtin_wrappers",
"__support_number_pair",
"__support_integer_utils",
":libc_root",
],
)
Expand Down