diff --git a/llvm/include/llvm/Support/Alignment.h b/llvm/include/llvm/Support/Alignment.h --- a/llvm/include/llvm/Support/Alignment.h +++ b/llvm/include/llvm/Support/Alignment.h @@ -22,6 +22,7 @@ #define LLVM_SUPPORT_ALIGNMENT_H_ #include "llvm/ADT/Optional.h" +#include "llvm/Support/BasicTypes.h" #include "llvm/Support/MathExtras.h" #include #ifndef NDEBUG @@ -33,80 +34,6 @@ #define ALIGN_CHECK_ISPOSITIVE(decl) \ assert(decl > 0 && (#decl " should be defined")) -/// This struct is a compact representation of a valid (non-zero power of two) -/// alignment. -/// It is suitable for use as static global constants. -struct Align { -private: - uint8_t ShiftValue = 0; /// The log2 of the required alignment. - /// ShiftValue is less than 64 by construction. - - friend struct MaybeAlign; - friend unsigned Log2(Align); - friend bool operator==(Align Lhs, Align Rhs); - friend bool operator!=(Align Lhs, Align Rhs); - friend bool operator<=(Align Lhs, Align Rhs); - friend bool operator>=(Align Lhs, Align Rhs); - friend bool operator<(Align Lhs, Align Rhs); - friend bool operator>(Align Lhs, Align Rhs); - friend unsigned encode(struct MaybeAlign A); - friend struct MaybeAlign decodeMaybeAlign(unsigned Value); - - /// A trivial type to allow construction of constexpr Align. - /// This is currently needed to workaround a bug in GCC 5.3 which prevents - /// definition of constexpr assign operators. - /// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic - /// FIXME: Remove this, make all assign operators constexpr and introduce user - /// defined literals when we don't have to support GCC 5.3 anymore. - /// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain - struct LogValue { - uint8_t Log; - }; - -public: - /// Default is byte-aligned. - constexpr Align() = default; - /// Do not perform checks in case of copy/move construct/assign, because the - /// checks have been performed when building `Other`. - constexpr Align(const Align &Other) = default; - constexpr Align(Align &&Other) = default; - Align &operator=(const Align &Other) = default; - Align &operator=(Align &&Other) = default; - - explicit Align(uint64_t Value) { - assert(Value > 0 && "Value must not be 0"); - assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2"); - ShiftValue = Log2_64(Value); - assert(ShiftValue < 64 && "Broken invariant"); - } - - /// This is a hole in the type system and should not be abused. - /// Needed to interact with C for instance. - uint64_t value() const { return uint64_t(1) << ShiftValue; } - - // Returns the previous alignment. - Align previous() const { - assert(ShiftValue != 0 && "Undefined operation"); - Align Out; - Out.ShiftValue = ShiftValue - 1; - return Out; - } - - /// Allow constructions of constexpr Align. - template constexpr static LogValue Constant() { - return LogValue{static_cast(CTLog2())}; - } - - /// Allow constructions of constexpr Align from types. - /// Compile time equivalent to Align(alignof(T)). - template constexpr static LogValue Of() { - return Constant::value>(); - } - - /// Constexpr constructor from LogValue type. - constexpr Align(LogValue CA) : ShiftValue(CA.Log) {} -}; - /// Treats the value 0 as a 1, so Align is always at least 1. inline Align assumeAligned(uint64_t Value) { return Value ? Align(Value) : Align(); @@ -144,7 +71,7 @@ /// Checks that SizeInBytes is a multiple of the alignment. inline bool isAligned(Align Lhs, uint64_t SizeInBytes) { - return SizeInBytes % Lhs.value() == 0; + return Bytes(SizeInBytes).isMultipleOf(Lhs.getBytes()); } /// Checks that Addr is a multiple of the alignment. @@ -206,7 +133,7 @@ } /// Returns the log2 of the alignment. -inline unsigned Log2(Align A) { return A.ShiftValue; } +inline unsigned Log2(Align A) { return A.getPowerOf2Bytes().getLogValue(); } /// Returns the alignment that satisfies both alignments. /// Same semantic as MinAlign. @@ -231,15 +158,13 @@ } /// Returns a representation of the alignment that encodes undefined as 0. -inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; } +inline unsigned encode(MaybeAlign A) { return A ? Log2(*A) + 1 : 0; } /// Dual operation of the encode function above. inline MaybeAlign decodeMaybeAlign(unsigned Value) { if (Value == 0) return MaybeAlign(); - Align Out; - Out.ShiftValue = Value - 1; - return Out; + return Align(PowerOf2::fromLogValue(Value - 1)); } /// Returns a representation of the alignment, the encoded value is positive by @@ -249,27 +174,27 @@ /// Comparisons between Align and scalars. Rhs must be positive. inline bool operator==(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() == Rhs; + return Lhs.getBytes() == Bytes(Rhs); } inline bool operator!=(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() != Rhs; + return Lhs.getBytes() != Bytes(Rhs); } inline bool operator<=(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() <= Rhs; + return Lhs.getBytes() <= Bytes(Rhs); } inline bool operator>=(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() >= Rhs; + return Lhs.getBytes() >= Bytes(Rhs); } inline bool operator<(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() < Rhs; + return Lhs.getBytes() < Bytes(Rhs); } inline bool operator>(Align Lhs, uint64_t Rhs) { ALIGN_CHECK_ISPOSITIVE(Rhs); - return Lhs.value() > Rhs; + return Lhs.getBytes() > Bytes(Rhs); } /// Comparisons between MaybeAlign and scalars. @@ -280,26 +205,6 @@ return Lhs ? (*Lhs).value() != Rhs : Rhs != 0; } -/// Comparisons operators between Align. -inline bool operator==(Align Lhs, Align Rhs) { - return Lhs.ShiftValue == Rhs.ShiftValue; -} -inline bool operator!=(Align Lhs, Align Rhs) { - return Lhs.ShiftValue != Rhs.ShiftValue; -} -inline bool operator<=(Align Lhs, Align Rhs) { - return Lhs.ShiftValue <= Rhs.ShiftValue; -} -inline bool operator>=(Align Lhs, Align Rhs) { - return Lhs.ShiftValue >= Rhs.ShiftValue; -} -inline bool operator<(Align Lhs, Align Rhs) { - return Lhs.ShiftValue < Rhs.ShiftValue; -} -inline bool operator>(Align Lhs, Align Rhs) { - return Lhs.ShiftValue > Rhs.ShiftValue; -} - // Don't allow relational comparisons with MaybeAlign. bool operator<=(Align Lhs, MaybeAlign Rhs) = delete; bool operator>=(Align Lhs, MaybeAlign Rhs) = delete; diff --git a/llvm/include/llvm/Support/BasicTypes.h b/llvm/include/llvm/Support/BasicTypes.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/BasicTypes.h @@ -0,0 +1,495 @@ +//===- llvm/Support/BasicTypes.h - low level abstractions ------*- 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 file defines the following set of basic types: +// - Bits : A number of bits (possibly 0 but never negative) +// - Bytes : A number of bytes (possibly 0 but never negative) +// - BitsOffset : A relative quantity of bits (possibly negative) +// - BytesOffset : A relative quantity of bytes (possibly negative) +// - PowerOf2 : A number of bits that is always a power of two +// - PowerOf2 : A number of bytes that is always a power of two +// - Align : An alignment number expressed in Bytes (represented as +// a PowerOf2) +// - Address : An address represented as an uintptr_t +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BASIC_TYPES_H +#define LLVM_SUPPORT_BASIC_TYPES_H + +#include +#include +#include + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/MathExtras.h" + +namespace llvm { + +namespace details { + +struct ByteTag {}; +struct BitTag {}; + +template struct BaseImpl { + static constexpr bool is_unit = true; + using raw_type = ScalarType; + + /// Default constructor initializing to 0. + constexpr explicit BaseImpl() = default; + /// Explicit constructor initializing to provided Quantity. + constexpr explicit BaseImpl(ScalarType Quantity) : Quantity(Quantity) {} + + /// Returns true if value is zero. + bool isZero() const { return Quantity == 0; } + /// Returns true if value is one. + bool isOne() const { return Quantity == 1; } + /// Returns true if value is strictly positive. + bool isPositive() const { return Quantity > 0; } + + // Comparison operators. + bool operator==(BaseT Value) const { return Quantity == Value.Quantity; } + bool operator!=(BaseT Value) const { return Quantity != Value.Quantity; } + + // Relational operators. + bool operator<(BaseT Value) const { return Quantity < Value.Quantity; } + bool operator<=(BaseT Value) const { return Quantity <= Value.Quantity; } + bool operator>(BaseT Value) const { return Quantity > Value.Quantity; } + bool operator>=(BaseT Value) const { return Quantity >= Value.Quantity; } + + // Arithmetic operators. + BaseT operator+(BaseT Value) const { + return BaseT(Quantity + Value.Quantity); + } + BaseT operator*(ScalarType N) const { return BaseT(Quantity * N); } + void operator*=(ScalarType N) { Quantity *= N; } + BaseT operator/(ScalarType N) const { return BaseT(Quantity / N); } + void operator/=(ScalarType N) { Quantity /= N; } + ScalarType operator/(BaseT Value) const { return Quantity / Value.Quantity; } + +protected: + ScalarType Quantity = 0; +}; + +/// A Size is a null or positive value with a unit. +/// It serves as a base class for Bits and Bytes below. +template +struct Size : public BaseImpl, ScalarType> { + static_assert(std::is_unsigned::value, "Unexpected ScalarType"); + + using UP = BaseImpl, ScalarType>; + using UP::UP; // Inherit constructors. + + /// Returns whether the value is a power of two. + bool isPowerOfTwo() const { + return UP::isPositive() && (UP::Quantity & -UP::Quantity) == UP::Quantity; + } + + /// Returns whether this value is a multiple of another value. + bool isMultipleOf(Size Value) const { + assert(!Value.isZero() && "isMultipleOf(0) is undefined"); + return (*this % Value) == 0; + } + + /// Modulo operator with a scalar value, returns the same type. + Size operator%(ScalarType N) const { + assert(N != 0 && "Modulo operator is undefined for 0"); + return Size(UP::Quantity % N); + } + + /// Modulo operator, returns a scalar value. + ScalarType operator%(Size Value) const { + assert(!Value.isZero() && "Modulo operator is undefined for 0"); + return UP::Quantity % Value.Quantity; + } + + // Allow construction from Offset type. + template friend struct Offset; +}; + +/// An Offset is a relative value with a unit. +/// It serves as a base class for BitsOffset and BytesOffset below. +template +struct Offset : public BaseImpl, ScalarType> { + static_assert(std::is_signed::value, "Unexpected ScalarType"); + + using UP = BaseImpl, ScalarType>; + using UP::UP; // Inherit constructors. + + /// Constructor from a Size type of the same unit. + /// Asserts when overflow occurs. + template + constexpr Offset(Size Value) + : Offset(static_cast(Value.Quantity)) { + assert(Value.Quantity <= T(std::numeric_limits::max()) && + "Overflow"); + } + + /// Returns whether this value is stricly negative. + bool isNegative() const { return UP::Quantity < 0; } + + // Arithmetic operators. + Offset operator-(Offset Other) const { + return Offset(UP::Quantity - Other.Quantity); + } + Offset operator-() const { return Offset(-UP::Quantity); } +}; +} // namespace details + +/// Represents a Size (Bits or Bytes) that is a power of 2. +template struct PowerOf2 { + static_assert(UnitT::is_unit, "Unexpected Size"); + static_assert(std::is_unsigned::value, + "Unexpected Size"); + + /// Default constructor initializing to one. + constexpr PowerOf2() {} + + /// Constructor initializing to provided value. + /// Asserts when value is not a power of two. + constexpr PowerOf2(UnitT Value) : PowerOf2(validate(Value)) {} + + // Comparison operators. + bool operator==(PowerOf2 Value) const { return LogValue == Value.LogValue; } + bool operator!=(PowerOf2 Value) const { return LogValue != Value.LogValue; } + + // Relational operators. + bool operator<(PowerOf2 Value) const { return LogValue < Value.LogValue; } + bool operator<=(PowerOf2 Value) const { return LogValue <= Value.LogValue; } + bool operator>(PowerOf2 Value) const { return LogValue > Value.LogValue; } + bool operator>=(PowerOf2 Value) const { return LogValue >= Value.LogValue; } + + /// Returns true if value is one. + bool isOne() const { return LogValue == 0; } + + /// Returns the value. + UnitT getValue() const { return UnitT(1ULL << LogValue); } + + /// Returns the log2 of the value. + /// This is guaranteed to be a number in [0, 63]. + /// e.g. PowerOf2(256).getLogValue() == 8 + auto getLogValue() const { return LogValue; } + + /// Returns the next power of two. + /// Asserts if value becomes greater or equal to 2^64. + auto next() const { return PowerOf2(LogValue + 1); } + + /// Returns the previous power of two. + /// Asserts if current value is one. + auto previous() const { + assert(LogValue != 0 && "Undefined operation"); + return PowerOf2(LogValue - 1); + } + + /// Creates a PowerOf2 object from a log quantity. + /// e.g. PowerOf2::fromLogValue(8) == Bytes(256) + /// Asserts if value is greater or equal to 64. + static constexpr PowerOf2 fromLogValue(uint8_t LogValue) { + return PowerOf2(LogValue); + } + + /// Returns the the largest PowerOf2 not greater than value. + /// e.g. PowerOf2::floor(Bits(2)) == PowerOf2(Bits(2)) + /// e.g. PowerOf2::floor(Bits(3)) == PowerOf2(Bits(2)) + static PowerOf2 floor(UnitT Value) { + assert(Value.isPositive() && "Value must be strictly positive"); + return PowerOf2(llvm::Log2_64(Value.Quantity)); + } + + /// Returns the smallest PowerOf2 not less than value. + /// e.g. PowerOf2::ceil(Bits(2)) == PowerOf2(Bits(2)) + /// e.g. PowerOf2::ceil(Bits(3)) == PowerOf2(Bits(4)) + static PowerOf2 ceil(UnitT Value) { + assert(Value.isPositive() && "Value must be strictly positive"); + return floor(Value + UnitT(1)); + } + +private: + // Makes sure the provided value is a power of two and returns its log2 value. + static uint64_t validate(UnitT Value) { + assert(Value.isPositive() && "Value must be strictly positive"); + assert(Value.isPowerOfTwo() && "Value is not a power of 2"); + return llvm::Log2_64(Value.Quantity); + } + + // Internal construtor to build from a log value. + // Asserts if log value is greater of equals to 64. + constexpr PowerOf2(uint8_t LogValue) : LogValue(LogValue) { + assert(LogValue < 64 && "Exponent too big"); + } + + uint8_t LogValue = 0; // Compact storage. +}; + +/// Represents a positive of null number of bytes. +struct Bytes : public details::Size { + using Size::Size; + + friend struct Bits; + friend struct Address; + friend struct Align; + + friend Bytes operator+(Bytes a, Bytes b); + friend auto getUnsafeRawQuantity(Bytes); + template friend struct PowerOf2; +}; +/// Escape hatch to read the Bytes underlying value. +inline auto getUnsafeRawQuantity(Bytes Value) { return Value.Quantity; } + +/// Represents a positive of null number of bits. +struct Bits : public details::Size { + using Size::Size; + constexpr Bits(Bytes B) : Size(B.Quantity * 8) {} + constexpr Bits(decltype(Quantity) B) : Size(B) {} + + llvm::Optional asBytes() const { + if (isMultipleOf(Bits(8))) + return Bytes(Quantity / 8); + return {}; + } + + Bytes getCoveringBytes() const { return Bytes((Quantity + 7) / 8); } + + friend Bits operator+(Bits a, Bits b); + friend auto getUnsafeRawQuantity(Bits); + template friend struct PowerOf2; +}; +/// Escape hatch to read the Bits underlying value. +inline auto getUnsafeRawQuantity(Bits Value) { return Value.Quantity; } + +struct BytesOffset : public details::Offset { + using Offset::Offset; + friend BytesOffset operator-(BytesOffset a, BytesOffset b); + friend auto getUnsafeRawQuantity(BytesOffset); + friend struct Address; +}; +/// Escape hatch to read the BytesOffset underlying value. +inline auto getUnsafeRawQuantity(BytesOffset Value) { return Value.Quantity; } + +struct BitsOffset : public details::Offset { + using Offset::Offset; + friend BitsOffset operator-(BitsOffset a, BitsOffset b); + friend auto getUnsafeRawQuantity(BitsOffset); +}; +/// Escape hatch to read the BitsOffset underlying value. +inline auto getUnsafeRawQuantity(BitsOffset Value) { return Value.Quantity; } + +/// Bytes addition. +inline Bytes operator+(Bytes a, Bytes b) { + return Bytes(a.Quantity + b.Quantity); +} + +/// Mixed typed addition between Bits and Bytes is handled by leveraging the +/// implicit construction from Bytes to Bits. +inline Bits operator+(Bits a, Bits b) { return Bits(a.Quantity + b.Quantity); } + +/// BytesOffset substraction. +inline BytesOffset operator-(BytesOffset A, BytesOffset B) { + return BytesOffset(A.Quantity - B.Quantity); +} + +/// BitsOffset substraction. +inline BitsOffset operator-(BitsOffset A, BitsOffset B) { + return BitsOffset(A.Quantity - B.Quantity); +} + +/// Align represents an alignment in Bytes, it is always set and always a valid +/// power of two, its minimum value is 1 which means no alignment requirements. +struct Align { + constexpr Align() : Value() {} + constexpr Align(PowerOf2 P) : Value(P) {} + constexpr Align(Bytes Value) : Value(Value) {} + constexpr Align(uint64_t Value) : Align(Bytes(Value)) {} + + /// Returns true if value is one. + bool isOne() const { return Value.isOne(); } + + // Comparison operators. + bool operator==(Align Other) const { return Value == Other.Value; } + bool operator!=(Align Other) const { return Value != Other.Value; } + + // Relational operators. + bool operator<(Align Other) const { return Value < Other.Value; } + bool operator<=(Align Other) const { return Value <= Other.Value; } + bool operator>(Align Other) const { return Value > Other.Value; } + bool operator>=(Align Other) const { return Value >= Other.Value; } + + // Returns the previous alignment. + // i.e. Align(32).previous() == Align(16) + Align previous() const { + assert(Value != PowerOf2() && "Undefined operation"); + return Value.previous(); + } + + // Returns the alignment as bytes. + Bytes getBytes() const { return Value.getValue(); } + + // Returns the alignment as a power of two of bytes. + PowerOf2 getPowerOf2Bytes() const { return Value; } + + /// Allow constructions of constexpr Align. + template constexpr static Align Constant() { + return PowerOf2::fromLogValue(CTLog2()); + } + + /// Allow constructions of constexpr Align from types. + /// Compile time equivalent to Align(alignof(T)). + template constexpr static Align Of() { + return Constant::value>(); + } + + ///////////////////////////////////////////////////////////////////////////// + // Functions to ease transition to new types - Remove ASAP + ///////////////////////////////////////////////////////////////////////////// + + /// This is a hole in the type system and should not be abused. + /// Needed to interact with C for instance. + uint64_t value() const { return getUnsafeRawQuantity(getBytes()); } + +private: + PowerOf2 Value; +}; + +struct Address; // Forward declaration for friendship declaration in Mask. + +namespace details { + +// For now, we keep Mask as an implementation detail but if it deems useful it +// can be exposed. + +/// Represents a bit mask for bitwise operations. +template struct Mask : private Size { + static_assert(std::is_integral::value && + std::is_unsigned::value, + "ScalarType must be an unsigned integral type"); + /// Default constructor initializing mask to all zeros. + constexpr Mask() : Size(0) {} + /// Constructor initializing mask with provided value. + constexpr Mask(ScalarType Quantity) : Size(Quantity) {} + + /// Returns a Mask with all bits set to one. + static Mask allOnes() { return Mask(~0); } + + /// Returns a Mask selecting all bits below the provided power of two. + /// e.g. Mask::allOnes(Bytes(8)), Mask(0b'0000'0111) + static Mask allOnes(PowerOf2 PowValue) { + if (PowValue.isOne()) + return {}; + return allOnes() >> (max_bits - PowValue.getLogValue()); + } + + /// Left shift operation. Asserts if the shifting value is greater than the + /// number of bits of the underlying type. + Mask operator>>(size_t Shift) const { + assert(Shift < max_bits && "Shift operand too large is undefined behavior"); + return Mask(this->Quantity >> Shift); + } + + /// Right shift operation. Asserts if the shifting value is greater than the + /// number of bits of the underlying type. + Mask operator<<(size_t Shift) const { + assert(Shift < max_bits && "Shift operand too large is undefined behavior"); + return Mask(this->Quantity << Shift); + } + + /// Negation operator. + Mask operator~() const { return Mask(~this->Quantity); } + + /// Comparison operators. + bool operator==(Mask M) const { return this->Quantity == M.Quantity; } + bool operator!=(Mask M) const { return this->Quantity != M.Quantity; } + +private: + static constexpr size_t max_bits = sizeof(ScalarType) * 8; + friend struct llvm::Address; +}; + +} // namespace details + +/// A struct representing a host address (underlying type is uintptr_t). +struct Address : private details::Size { + using Mask = details::Mask; + using raw_type = uintptr_t; + + Address() : Size(0) {} + Address(uintptr_t ptr) : Size(ptr) {} + Address(Bytes byte) : Size(byte.Quantity) {} + Address(const void *ptr) : Address(reinterpret_cast(ptr)) {} + Address(void *ptr) : Address(reinterpret_cast(ptr)) {} + + /// Returns whether the address is aligned to the provided alignment. + bool isAligned(Align AL) const { + return isMultipleOf(Address(AL.getBytes())); + } + + Bytes getDistanceFromLastAligned(Align AL) const { + return Bytes(Quantity & Mask::allOnes(AL.getPowerOf2Bytes()).Quantity); + } + + Bytes getDistanceToNextAligned(Align AL) const { + return Bytes(-Quantity & Mask::allOnes(AL.getPowerOf2Bytes()).Quantity); + } + + /// If already aligned returns the same Address, otherwise returns the aligned + /// Address before this one. + Address alignDown(Align AL) const { + return *this & ~Mask::allOnes(AL.getPowerOf2Bytes()); + } + + /// If already aligned returns the same Address, otherwise returns the aligned + /// Address after this one. + Address alignUp(Align AL) const { + return *this + getDistanceToNextAligned(AL); + } + + Address operator+(BytesOffset Value) const { + return Address(Quantity + Value.Quantity); + } + + Address operator-(BytesOffset Value) const { + return Address(Quantity - Value.Quantity); + } + + Address operator+=(BytesOffset Value) { + Quantity += Value.Quantity; + return *this; + } + + Address operator-=(BytesOffset Value) { + Quantity -= Value.Quantity; + return *this; + } + + // Comparison operators. + bool operator==(Address Value) const { return Quantity == Value.Quantity; } + bool operator!=(Address Value) const { return Quantity != Value.Quantity; } + + // Relational operators. + bool operator<(Address Value) const { return Quantity < Value.Quantity; } + bool operator<=(Address Value) const { return Quantity <= Value.Quantity; } + bool operator>(Address Value) const { return Quantity > Value.Quantity; } + bool operator>=(Address Value) const { return Quantity >= Value.Quantity; } + +private: + Address operator&(Mask mask) const { + return Address(Quantity & mask.Quantity); + } + Address operator|(Mask mask) const { + return Address(Quantity | mask.Quantity); + } + friend auto getUnsafeRawQuantity(Address); +}; +/// Escape hatch to read the Address underlying value. +inline auto getUnsafeRawQuantity(Address address) { return address.Quantity; } + +inline Bytes operator""_bytes(unsigned long long v) { return Bytes(v); } +inline Bits operator""_bits(unsigned long long v) { return Bits(v); } + +} // namespace llvm + +#endif /* LLVM_SUPPORT_BASIC_TYPES_H */ diff --git a/llvm/unittests/Support/AlignmentTest.cpp b/llvm/unittests/Support/AlignmentTest.cpp --- a/llvm/unittests/Support/AlignmentTest.cpp +++ b/llvm/unittests/Support/AlignmentTest.cpp @@ -45,14 +45,12 @@ std::vector getValidAlignments() { std::vector Out; - for (size_t Shift = 0; Shift < 64; ++Shift) + for (size_t Shift = 0; Shift < 63; ++Shift) Out.push_back(1ULL << Shift); return Out; } -TEST(AlignmentTest, AlignDefaultCTor) { - EXPECT_EQ(Align().value(), 1ULL); -} +TEST(AlignmentTest, AlignDefaultCTor) { EXPECT_EQ(Align().value(), 1ULL); } TEST(AlignmentTest, MaybeAlignDefaultCTor) { EXPECT_FALSE(MaybeAlign().hasValue()); @@ -94,18 +92,8 @@ } } kTests[] = { // Align - {1, 0, 0}, - {1, 1, 1}, - {1, 5, 5}, - {2, 0, 0}, - {2, 1, 2}, - {2, 2, 2}, - {2, 7, 8}, - {2, 16, 16}, - {4, 0, 0}, - {4, 1, 4}, - {4, 4, 4}, - {4, 6, 8}, + {1, 0, 0}, {1, 1, 1}, {1, 5, 5}, {2, 0, 0}, {2, 1, 2}, {2, 2, 2}, + {2, 7, 8}, {2, 16, 16}, {4, 0, 0}, {4, 1, 4}, {4, 4, 4}, {4, 6, 8}, }; for (const auto &T : kTests) { Align A = Align(T.alignment); @@ -284,9 +272,9 @@ } TEST(AlignmentDeathTest, InvalidCTors) { - EXPECT_DEATH((Align(0)), "Value must not be 0"); + EXPECT_DEATH((Align(0)), "Value must be strictly positive"); for (uint64_t Value : getNonPowerOfTwo()) { - EXPECT_DEATH((Align(Value)), "Alignment is not a power of 2"); + EXPECT_DEATH((Align(Value)), "Value is not a power of 2"); EXPECT_DEATH((MaybeAlign(Value)), "Alignment is neither 0 nor a power of 2"); } diff --git a/llvm/unittests/Support/BasicTypesTest.cpp b/llvm/unittests/Support/BasicTypesTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/BasicTypesTest.cpp @@ -0,0 +1,356 @@ +//===- BasicTypesTest.cpp -------------------------------------------------===// +// +// 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/Support/BasicTypes.h" +#include "gtest/gtest.h" + +namespace llvm { + +using QuantityTypes = + ::testing::Types; +template class BasicTypes_QuantitiesTest : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_QuantitiesTest, QuantityTypes); +TYPED_TEST(BasicTypes_QuantitiesTest, Comparison) { + const auto A = TypeParam(1); + const auto B = TypeParam(2); + + EXPECT_EQ(A, A); + EXPECT_NE(A, B); + + EXPECT_LT(A, B); + EXPECT_GT(B, A); + + EXPECT_LE(A, B); + EXPECT_GE(B, A); + EXPECT_LE(A, A); + EXPECT_GE(A, A); +} + +using EscapableTypes = + ::testing::Types; +template +class BasicTypes_EscapableTypesTest : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_EscapableTypesTest, EscapableTypes); +TYPED_TEST(BasicTypes_EscapableTypesTest, DefaultConstructibleAndEscapeHatch) { + using raw_type = typename TypeParam::raw_type; + const TypeParam DefaultValue; + EXPECT_EQ(getUnsafeRawQuantity(DefaultValue), raw_type(0)); +} + +using SizeTypes = ::testing::Types; +template class BasicTypes_Sizes : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_Sizes, SizeTypes); + +using OffsetTypes = ::testing::Types; +template class BasicTypes_OffsetTypes : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_OffsetTypes, OffsetTypes); + +using AllUnitTypes = ::testing::Types; +template +class BasicTypes_SizesAndOffsets : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_SizesAndOffsets, AllUnitTypes); + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoComparison) { + const auto A = PowerOf2(TypeParam(1)); + const auto B = PowerOf2(TypeParam(2)); + + EXPECT_EQ(A, A); + EXPECT_NE(A, B); + + EXPECT_LT(A, B); + EXPECT_GT(B, A); + + EXPECT_LE(A, B); + EXPECT_GE(B, A); + EXPECT_LE(A, A); + EXPECT_GE(A, A); +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoLogValue) { + for (int64_t I = 0; I < 8; ++I) { + auto Value = TypeParam(1ULL << I); + EXPECT_EQ(PowerOf2(Value).getLogValue(), I); + } +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoValue) { + for (int64_t I = 0; I < 8; ++I) { + auto Value = TypeParam(1ULL << I); + EXPECT_EQ(PowerOf2(Value).getValue(), Value); + } +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoNext) { + for (int64_t I = 0; I < 8; ++I) { + auto Value = TypeParam(1ULL << I); + auto Next = TypeParam(1ULL << (I + 1)); + EXPECT_EQ(PowerOf2(Value).next(), Next); + } +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoPrevious) { + for (int64_t I = 0; I < 8; ++I) { + auto Previous = TypeParam(1ULL << I); + auto Value = TypeParam(1ULL << (I + 1)); + EXPECT_EQ(PowerOf2(Value).previous(), Previous); + } +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoFloor) { + EXPECT_EQ(PowerOf2::floor(TypeParam(2)), + PowerOf2(TypeParam(2))); + EXPECT_EQ(PowerOf2::floor(TypeParam(3)), + PowerOf2(TypeParam(2))); + EXPECT_EQ(PowerOf2::floor(TypeParam(4)), + PowerOf2(TypeParam(4))); +} + +TYPED_TEST(BasicTypes_Sizes, PowerOfTwoCeil) { + EXPECT_EQ(PowerOf2::ceil(TypeParam(2)), + PowerOf2(TypeParam(2))); + EXPECT_EQ(PowerOf2::ceil(TypeParam(3)), + PowerOf2(TypeParam(4))); + EXPECT_EQ(PowerOf2::ceil(TypeParam(4)), + PowerOf2(TypeParam(4))); +} + +TYPED_TEST(BasicTypes_SizesAndOffsets, Arithmetic) { + using raw_type = typename TypeParam::raw_type; + EXPECT_EQ(TypeParam(1) + TypeParam(2), TypeParam(3)); + + EXPECT_EQ(TypeParam(1) * 2, TypeParam(2)); + EXPECT_EQ(TypeParam(2) / TypeParam(2), raw_type(1)); + EXPECT_EQ(TypeParam(2) / 2, TypeParam(1)); + + auto Value = TypeParam(1); + Value *= 6; + EXPECT_EQ(Value, TypeParam(6)); + Value /= 2; + EXPECT_EQ(Value, TypeParam(3)); +} + +TYPED_TEST(BasicTypes_Sizes, Arithmetic) { + EXPECT_EQ(TypeParam(8) % 2, TypeParam(0)); + EXPECT_EQ(TypeParam(8) % TypeParam(2), 0UL); + + // Substracting Sizes returns Offsets that implement the + // isNegative() method. + EXPECT_FALSE((TypeParam(8) - TypeParam(2)).isNegative()); + EXPECT_TRUE((TypeParam(2) - TypeParam(8)).isNegative()); +} + +TYPED_TEST(BasicTypes_OffsetTypes, SignedArithmetic) { + EXPECT_EQ(TypeParam(1) - TypeParam(2), TypeParam(-1)); + EXPECT_EQ(-TypeParam(1), TypeParam(-1)); +} + +// TEST(BasicTypes_OffsetTypes, OffsetToSize) { +// EXPECT_EQ(BytesOffset().asSize(), Bytes()); +// EXPECT_EQ(BitsOffset().asSize(), Bits()); +// } + +TYPED_TEST(BasicTypes_Sizes, Attributes) { + const auto Zero = TypeParam(0); + const auto One = TypeParam(1); + + EXPECT_TRUE(Zero.isZero()); + EXPECT_FALSE(One.isZero()); + + EXPECT_FALSE(Zero.isOne()); + EXPECT_TRUE(One.isOne()); + + EXPECT_TRUE(One.isPositive()); + EXPECT_FALSE(Zero.isPositive()); + + EXPECT_FALSE(Zero.isPowerOfTwo()); + EXPECT_TRUE(One.isPowerOfTwo()); + + EXPECT_TRUE(One.isMultipleOf(One)); + EXPECT_TRUE(Zero.isMultipleOf(One)); + + EXPECT_TRUE(TypeParam(4).isMultipleOf(TypeParam(2))); + EXPECT_FALSE(TypeParam(2).isMultipleOf(TypeParam(4))); +} + +TYPED_TEST(BasicTypes_OffsetTypes, Attributes) { + const auto Zero = TypeParam(0); + const auto One = TypeParam(1); + const auto Negative = TypeParam(-4); + + EXPECT_TRUE(Zero.isZero()); + EXPECT_FALSE(One.isZero()); + + EXPECT_FALSE(Zero.isOne()); + EXPECT_TRUE(One.isOne()); + + EXPECT_TRUE(One.isPositive()); + EXPECT_FALSE(Zero.isPositive()); + EXPECT_FALSE(Negative.isPositive()); + + EXPECT_TRUE(Negative.isNegative()); + EXPECT_FALSE(Zero.isNegative()); + EXPECT_FALSE(One.isNegative()); +} + +TEST(BasicTypes_BitsAndBytes, Literals) { + EXPECT_EQ(2_bits, Bits(2)); + EXPECT_EQ(16_bytes, Bytes(16)); +} + +TEST(BasicTypes_BitsAndBytes, MixedTypeAddition) { + EXPECT_EQ(Bytes(1) + Bits(1), Bits(9)); + EXPECT_EQ(Bits(1) + Bytes(1), Bits(9)); +} + +TEST(BasicTypes_Bits, BitsOperations) { + EXPECT_EQ(Bits(1).asBytes(), llvm::None); + EXPECT_EQ(Bits(8).asBytes(), llvm::Optional(Bytes(1))); + EXPECT_EQ(Bits(1).getCoveringBytes(), Bytes(1)); +} + +template using Mask = details::Mask; + +TEST(BasicTypes_MaskTest, ShiftZero) { + EXPECT_EQ(Mask(0b1) >> 0, Mask(0b1)); + EXPECT_EQ(Mask(0b1) << 0, Mask(0b1)); +} + +TEST(BasicTypes_MaskTest, ShiftOne) { + EXPECT_EQ(Mask(0b10) >> 1, Mask(0b1)); + EXPECT_EQ(Mask(0b10) << 1, Mask(0b100)); +} + +TEST(BasicTypes_MaskTest, Negate) { + EXPECT_EQ(~Mask(0x02), Mask(0xFD)); + EXPECT_EQ(~Mask(0x02), Mask(0xFFFD)); + EXPECT_EQ(~Mask(0x02), Mask(0xFFFFFFFD)); + EXPECT_EQ(~Mask(0x02), Mask(0xFFFFFFFFFFFFFFFD)); +} + +TEST(BasicTypes_MaskTest, FromAlign) { + EXPECT_EQ(Mask::allOnes(Bytes(32)), Mask(0b11111)); + EXPECT_EQ(Mask::allOnes(Bytes(8)), Mask(0b111)); + EXPECT_EQ(Mask::allOnes(Bytes(1)), Mask(0b0)); +} + +TEST(BasicTypes_MaskTest_DEATH, UndefinedShifts) { + EXPECT_DEATH_IF_SUPPORTED(Mask() << 8, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() >> 8, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() << 16, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() >> 16, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() << 32, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() >> 32, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() << 64, + "Shift operand too large is undefined behavior"); + EXPECT_DEATH_IF_SUPPORTED(Mask() >> 64, + "Shift operand too large is undefined behavior"); +} + +TEST(BasicTypes_AddressTest, Initialization) { + EXPECT_EQ(Address(), Address(uintptr_t(0))); + EXPECT_EQ(Address() + 1_bytes, Address(1)); + + const void *ConstVoidPtr = nullptr; + void *VoidPtr = nullptr; + EXPECT_EQ(Address(ConstVoidPtr), Address()); + EXPECT_EQ(Address(VoidPtr), Address()); +} + +TEST(BasicTypes_AddressTest, Arithmetic) { + uintptr_t NullUintPtr = 0; + + EXPECT_EQ(Address(NullUintPtr) + 1_bytes, Address(1)); + EXPECT_EQ(Address(NullUintPtr) - 1_bytes, Address(uintptr_t(~0))); + + EXPECT_EQ(Address(NullUintPtr) += 1_bytes, Address(1)); + EXPECT_EQ(Address(NullUintPtr) -= 1_bytes, Address(uintptr_t(~0))); +} + +TEST(BasicTypes_AddressTest, Alignment) { + const auto Addr0 = Address(uintptr_t(0)); + const auto Addr12 = Address(uintptr_t(12)); + + // Always aligned to 1. + EXPECT_TRUE(Addr12.isAligned(Align())); + + EXPECT_EQ(Addr12.alignDown(Align(32)), Addr0); + EXPECT_EQ(Addr12.alignUp(Align(32)), Address(uintptr_t(32))); + + EXPECT_EQ(Addr12.alignDown(Align(8)), Address(uintptr_t(8))); + EXPECT_EQ(Addr12.alignUp(Align(8)), Address(uintptr_t(16))); + + // Already aligned. + EXPECT_EQ(Addr0.alignDown(Align(8)), Addr0); + EXPECT_EQ(Addr0.alignUp(Align(8)), Addr0); + EXPECT_EQ(Addr12.alignDown(Align(4)), Addr12); + EXPECT_EQ(Addr12.alignUp(Align(4)), Addr12); + + // Align is implictly constructible from Bytes. + EXPECT_TRUE(Addr12.isAligned(Bytes(1))); + EXPECT_FALSE(Addr12.isAligned(Bytes(32))); + + // Align is implictly constructible from integral uint64_t so all of the above + // can be simplified. + EXPECT_TRUE(Addr12.isAligned(1)); + EXPECT_FALSE(Addr12.isAligned(32)); + EXPECT_EQ(Addr12.alignDown(32), Addr0); + EXPECT_EQ(Addr12.alignUp(32), Address(32ULL)); + EXPECT_EQ(Addr12.alignDown(8), Address(8ULL)); + EXPECT_EQ(Addr12.alignUp(8), Address(16ULL)); +} + +TEST(BasicTypes_AddressTest_DEATH, Align) { + EXPECT_DEATH_IF_SUPPORTED(Align().previous(), "Undefined operation"); + EXPECT_DEATH_IF_SUPPORTED(Align(1).previous(), "Undefined operation"); +} + +template +class BasicTypes_TypedTest_DEATH : public testing::Test {}; +TYPED_TEST_SUITE(BasicTypes_TypedTest_DEATH, SizeTypes); + +TYPED_TEST(BasicTypes_TypedTest_DEATH, Units) { + EXPECT_DEATH_IF_SUPPORTED(TypeParam(1).isMultipleOf(TypeParam(0)), + "isMultipleOf\\(0\\) is undefined"); + EXPECT_DEATH_IF_SUPPORTED(TypeParam(1) % 0, + "Modulo operator is undefined for 0"); + EXPECT_DEATH_IF_SUPPORTED(TypeParam(1) % TypeParam(0), + "Modulo operator is undefined for 0"); +} + +TYPED_TEST(BasicTypes_TypedTest_DEATH, PowerOfTwo) { + EXPECT_DEATH_IF_SUPPORTED(PowerOf2(TypeParam(0)), + "Value must be strictly positive"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2(TypeParam(3)), + "Value is not a power of 2"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2::ceil(TypeParam(0)), + "Value must be strictly positive"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2::floor(TypeParam(0)), + "Value must be strictly positive"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2::fromLogValue(64), + "Exponent too big"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2(TypeParam(1)).previous(), + "Undefined operation"); + EXPECT_DEATH_IF_SUPPORTED(PowerOf2::fromLogValue(63).next(), + "Exponent too big"); +} + +TEST(BasicTypes_OffsetType_DEATH, ConstructionOverflows) { + constexpr uint64_t MaxAllowed = std::numeric_limits::max(); + EXPECT_EQ(BytesOffset(Bytes(MaxAllowed)), BytesOffset(MaxAllowed)); + EXPECT_EQ(BitsOffset(Bits(MaxAllowed)), BitsOffset(MaxAllowed)); + EXPECT_DEATH_IF_SUPPORTED(BytesOffset(Bytes(MaxAllowed + 1ULL)), "Overflow"); + EXPECT_DEATH_IF_SUPPORTED(BitsOffset(Bits(MaxAllowed + 1ULL)), "Overflow"); +} + +} // namespace llvm \ No newline at end of file diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -11,6 +11,7 @@ ARMAttributeParser.cpp ArrayRecyclerTest.cpp Base64Test.cpp + BasicTypesTest.cpp BinaryStreamTest.cpp BLAKE3Test.cpp BlockFrequencyTest.cpp