diff --git a/llvm/include/llvm/ADT/Bitfields.h b/llvm/include/llvm/ADT/Bitfields.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ADT/Bitfields.h @@ -0,0 +1,114 @@ +//===-- llvm/ADT/Bitfield.h - Get and Set bits in an integer ---*- 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 +/// This file implements methods to test, set and extract typed bits from packed +/// unsigned integers. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_BITFIELDS_H +#define LLVM_ADT_BITFIELDS_H + +#include +#include +#include + +/// Helpers to pack / unpack typed bitfields into an unsigned scalar. +/// e.g. +/// +/// uint8_t storage = 0; +/// +/// // Store and retrieve a single bit as bool. +/// using Volatile = Bitfield; +/// setField(storage, true); +/// EXPECT_EQ(storage, 0b00000001); +/// EXPECT_EQ(getField(storage), true); +/// +/// // Store and retrieve a 2 bit typed enum. +/// enum class SuitEnum { CLUBS, DIAMONDS, HEARTS, SPADES }; +/// using Suit = Bitfield; +/// setField(storage, SuitEnum::HEARTS); +/// EXPECT_EQ(storage, 0b00000101); +/// EXPECT_EQ(getField(storage), SuitEnum::HEARTS); +/// +/// // Store and retrieve a 5 bit value as unsigned. +/// using Value = Bitfield; +/// setField(storage, 10); +/// EXPECT_EQ(storage, 0b01010101); +/// EXPECT_EQ(getField(storage), 10U); +/// +/// // Alter storage changes value. +/// storage = 0; +/// EXPECT_EQ(getField(storage), false); +/// EXPECT_EQ(getField(storage), SuitEnum::CLUBS); +/// EXPECT_EQ(getField(storage), 0U); +/// +/// storage = 255; +/// EXPECT_EQ(getField(storage), true); +/// EXPECT_EQ(getField(storage), SuitEnum::SPADES); +/// EXPECT_EQ(getField(storage), 31U); +/// +/// // Ability to efficiently test if a field is non zero. +/// EXPECT_NE(testField(storage), 0U); + +namespace llvm { + +/// A struct to hold the bitfield informations. +/// \param Type, the type of the field once in unpacked form, +/// \param Offset, the position of the first bit, +/// \param Size, the size of the field. +template struct Bitfield { + using type = Type; + static_assert(std::is_unsigned::value || std::is_enum::value, + "Type must be unsigned or enum"); + static constexpr unsigned offset = Offset; + static constexpr unsigned size = Size; + static_assert(size > 0, "Size must be non zero"); + static_assert((offset + size) <= 64, "Data must fit in 64 bits"); + static constexpr uint64_t max_value = (1ULL << size) - 1ULL; + static constexpr uint64_t mask = max_value << offset; +}; + +/// Unpacks the field from the packed value. +template +static typename Bitfield::type getField(T packed) { + static_assert(std::is_unsigned(), "storage must be unsigned"); + static_assert(sizeof(T) <= sizeof(uint64_t), + "T must not exceed uint64_t size"); + const T masked = packed & Bitfield::mask; + const T shifted = masked >> Bitfield::offset; + return static_cast(shifted); +} + +/// Return a non zero value if the field is non zero. +/// It saves a shift operation compared to `getField`. +template static T testField(T packed) { + static_assert(std::is_unsigned(), "storage must be unsigned"); + static_assert(sizeof(T) <= sizeof(uint64_t), + "T must not exceed uint64_t size"); + return packed & Bitfield::mask; +} + +/// Sets the typed value in the provided packed value. +/// The method will asserts if the provided value is too big to fit in the +/// packed field. +template +static void setField(T &packed, typename Bitfield::type value) { + static_assert(std::is_unsigned(), "storage must be unsigned"); + static_assert(sizeof(T) <= sizeof(uint64_t), + "T must not exceed uint64_t size"); + assert(static_cast(value) <= Bitfield::max_value && + "value is too big"); + packed &= ~Bitfield::mask; + packed |= T(value) << Bitfield::offset; +} + +} // namespace llvm + +#endif // LLVM_ADT_BITFIELDS_H \ No newline at end of file diff --git a/llvm/unittests/ADT/BitFieldsTest.cpp b/llvm/unittests/ADT/BitFieldsTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/ADT/BitFieldsTest.cpp @@ -0,0 +1,141 @@ +//===- llvm/unittests/ADT/BitFieldsTest.cpp - BitFields unit tests --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Bitfields.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(BitfieldsTest, Example) { + uint8_t storage = 0; + + // Store and retrieve a single bit as bool. + using Volatile = Bitfield; + setField(storage, true); + EXPECT_EQ(storage, 0b00000001); + EXPECT_EQ(getField(storage), true); + + // Store and retrieve a 2 bit typed enum. + enum class SuitEnum { CLUBS, DIAMONDS, HEARTS, SPADES }; + using Suit = Bitfield; + setField(storage, SuitEnum::HEARTS); + EXPECT_EQ(storage, 0b00000101); + EXPECT_EQ(getField(storage), SuitEnum::HEARTS); + + // Store and retrieve a 5 bit value as unsigned. + using Value = Bitfield; + setField(storage, 10); + EXPECT_EQ(storage, 0b01010101); + EXPECT_EQ(getField(storage), 10U); + + // Alter storage changes value. + storage = 0; + EXPECT_EQ(getField(storage), false); + EXPECT_EQ(getField(storage), SuitEnum::CLUBS); + EXPECT_EQ(getField(storage), 0U); + + storage = 255; + EXPECT_EQ(getField(storage), true); + EXPECT_EQ(getField(storage), SuitEnum::SPADES); + EXPECT_EQ(getField(storage), 31U); + + // Ability to efficiently test if a field is non zero. + EXPECT_NE(testField(storage), 0U); +} + +TEST(BitfieldsTest, FirstBit) { + uint8_t storage = 0; + using FirstBit = Bitfield; + // Set true + setField(storage, true); + EXPECT_EQ(getField(storage), true); + EXPECT_EQ(storage, 0x1ULL); + // Set false + setField(storage, false); + EXPECT_EQ(getField(storage), false); + EXPECT_EQ(storage, 0x0ULL); +} + +TEST(BitfieldsTest, SecondBit) { + uint8_t storage = 0; + using SecondBit = Bitfield; + // Set true + setField(storage, true); + EXPECT_EQ(getField(storage), true); + EXPECT_EQ(storage, 0x2ULL); + // Set false + setField(storage, false); + EXPECT_EQ(getField(storage), false); + EXPECT_EQ(storage, 0x0ULL); +} + +TEST(BitfieldsTest, LastBit) { + uint8_t storage = 0; + using LastBit = Bitfield; + // Set true + setField(storage, true); + EXPECT_EQ(getField(storage), true); + EXPECT_EQ(storage, 0x80ULL); + // Set false + setField(storage, false); + EXPECT_EQ(getField(storage), false); + EXPECT_EQ(storage, 0x0ULL); +} + +TEST(BitfieldsTest, LastBitUint64) { + uint64_t storage = 0; + using LastBit = Bitfield; + // Set true + setField(storage, true); + EXPECT_EQ(getField(storage), true); + EXPECT_EQ(storage, 0x8000000000000000ULL); + // Set false + setField(storage, false); + EXPECT_EQ(getField(storage), false); + EXPECT_EQ(storage, 0x0ULL); +} + +enum class Ordering { + Not = 0, + Unordered = 1, + Monotonic = 2, +}; + +TEST(BitfieldsTest, EnumClass) { + uint8_t storage = 0; + using OrderingField = Bitfield; + EXPECT_EQ(getField(storage), Ordering::Not); + setField(storage, Ordering::Monotonic); + EXPECT_EQ(getField(storage), Ordering::Monotonic); + EXPECT_EQ(storage, 0b00000100); + // value 2 in ^^ +} + +#ifdef EXPECT_DEBUG_DEATH + +TEST(BitfieldsTest, ValueTooBigBool) { + uint64_t storage = 0; + using A = Bitfield; + setField(storage, true); + setField(storage, false); + EXPECT_DEBUG_DEATH(setField(storage, 2), "value is too big"); +} + +TEST(BitfieldsTest, ValueTooBigInt) { + uint64_t storage = 0; + using A = Bitfield; + setField(storage, 3); + EXPECT_DEBUG_DEATH(setField(storage, 4), "value is too big"); + EXPECT_DEBUG_DEATH(setField(storage, -1), "value is too big"); +} + +#endif + +} // namespace \ No newline at end of file diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt --- a/llvm/unittests/ADT/CMakeLists.txt +++ b/llvm/unittests/ADT/CMakeLists.txt @@ -8,6 +8,7 @@ APIntTest.cpp APSIntTest.cpp ArrayRefTest.cpp + BitFieldsTest.cpp BitmaskEnumTest.cpp BitVectorTest.cpp BreadthFirstIteratorTest.cpp