mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
c8f290ffea
If the caller needs to reponsible for making sure the MaybeAlign has a value, then we should just make the caller convert it to an Align with operator*. I explicitly deleted the relational comparison operators that were being inherited from Optional. It's unclear what the meaning of two MaybeAligns were one is defined and the other isn't should be. So make the caller reponsible for defining the behavior. I left the ==/!= operators from Optional. But now that exposed a weird quirk that ==/!= between Align and MaybeAlign required the MaybeAlign to be defined. But now we use the operator== from Optional that takes an Optional and the Value. Differential Revision: https://reviews.llvm.org/D80455
367 lines
10 KiB
C++
367 lines
10 KiB
C++
//=== - llvm/unittest/Support/Alignment.cpp - Alignment utility 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/Support/Alignment.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <vector>
|
|
|
|
#ifdef _MSC_VER
|
|
// Disable warnings about potential divide by 0.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4723)
|
|
#endif
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
TEST(AlignmentTest, AlignOfConstant) {
|
|
EXPECT_EQ(Align::Of<uint8_t>(), Align(alignof(uint8_t)));
|
|
EXPECT_EQ(Align::Of<uint16_t>(), Align(alignof(uint16_t)));
|
|
EXPECT_EQ(Align::Of<uint32_t>(), Align(alignof(uint32_t)));
|
|
EXPECT_EQ(Align::Of<uint64_t>(), Align(alignof(uint64_t)));
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignConstant) {
|
|
EXPECT_EQ(Align::Constant<1>(), Align(1));
|
|
EXPECT_EQ(Align::Constant<2>(), Align(2));
|
|
EXPECT_EQ(Align::Constant<4>(), Align(4));
|
|
EXPECT_EQ(Align::Constant<8>(), Align(8));
|
|
EXPECT_EQ(Align::Constant<16>(), Align(16));
|
|
EXPECT_EQ(Align::Constant<32>(), Align(32));
|
|
EXPECT_EQ(Align::Constant<64>(), Align(64));
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignConstexprConstant) {
|
|
constexpr Align kConstantAlign = Align::Of<uint64_t>();
|
|
EXPECT_EQ(Align(alignof(uint64_t)), kConstantAlign);
|
|
}
|
|
|
|
std::vector<uint64_t> getValidAlignments() {
|
|
std::vector<uint64_t> Out;
|
|
for (size_t Shift = 0; Shift < 64; ++Shift)
|
|
Out.push_back(1ULL << Shift);
|
|
return Out;
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignDefaultCTor) {
|
|
EXPECT_EQ(Align().value(), 1ULL);
|
|
}
|
|
|
|
TEST(AlignmentTest, MaybeAlignDefaultCTor) {
|
|
EXPECT_FALSE(MaybeAlign().hasValue());
|
|
}
|
|
|
|
TEST(AlignmentTest, ValidCTors) {
|
|
for (uint64_t Value : getValidAlignments()) {
|
|
EXPECT_EQ(Align(Value).value(), Value);
|
|
EXPECT_EQ((*MaybeAlign(Value)).value(), Value);
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, CheckMaybeAlignHasValue) {
|
|
EXPECT_TRUE(MaybeAlign(1));
|
|
EXPECT_TRUE(MaybeAlign(1).hasValue());
|
|
EXPECT_FALSE(MaybeAlign(0));
|
|
EXPECT_FALSE(MaybeAlign(0).hasValue());
|
|
EXPECT_FALSE(MaybeAlign());
|
|
EXPECT_FALSE(MaybeAlign().hasValue());
|
|
}
|
|
|
|
TEST(AlignmentTest, Division) {
|
|
for (uint64_t Value : getValidAlignments()) {
|
|
if (Value > 1) {
|
|
EXPECT_EQ(Align(Value) / 2, Value / 2);
|
|
EXPECT_EQ(MaybeAlign(Value) / 2, Value / 2);
|
|
}
|
|
}
|
|
EXPECT_EQ(MaybeAlign(0) / 2, MaybeAlign(0));
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignTo) {
|
|
struct {
|
|
uint64_t alignment;
|
|
uint64_t offset;
|
|
uint64_t rounded;
|
|
const void *forgedAddr() const {
|
|
// A value of any integral or enumeration type can be converted to a
|
|
// pointer type.
|
|
return reinterpret_cast<const void *>(offset);
|
|
}
|
|
} kTests[] = {
|
|
// MaybeAlign
|
|
{0, 0, 0},
|
|
{0, 1, 1},
|
|
{0, 5, 5},
|
|
// MaybeAlign / 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},
|
|
};
|
|
for (const auto &T : kTests) {
|
|
MaybeAlign A(T.alignment);
|
|
// Test MaybeAlign
|
|
EXPECT_EQ(alignTo(T.offset, A), T.rounded);
|
|
// Test Align
|
|
if (A) {
|
|
EXPECT_EQ(alignTo(T.offset, A.getValue()), T.rounded);
|
|
EXPECT_EQ(alignAddr(T.forgedAddr(), A.getValue()), T.rounded);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignToWithSkew) {
|
|
EXPECT_EQ(alignTo(5, Align(8), 0), alignTo(5, Align(8)));
|
|
EXPECT_EQ(alignTo(5, Align(8), 7), 7U);
|
|
EXPECT_EQ(alignTo(17, Align(8), 1), 17U);
|
|
EXPECT_EQ(alignTo(~0LL, Align(8), 3), 3U);
|
|
}
|
|
|
|
TEST(AlignmentTest, Log2) {
|
|
for (uint64_t Value : getValidAlignments()) {
|
|
EXPECT_EQ(Log2(Align(Value)), Log2_64(Value));
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, MinAlign) {
|
|
struct {
|
|
uint64_t A;
|
|
uint64_t B;
|
|
uint64_t MinAlign;
|
|
} kTests[] = {
|
|
// MaybeAlign
|
|
{0, 0, 0},
|
|
{0, 8, 8},
|
|
{2, 0, 2},
|
|
// MaybeAlign / Align
|
|
{1, 2, 1},
|
|
{8, 4, 4},
|
|
};
|
|
for (const auto &T : kTests) {
|
|
EXPECT_EQ(commonAlignment(MaybeAlign(T.A), MaybeAlign(T.B)), T.MinAlign);
|
|
EXPECT_EQ(MinAlign(T.A, T.B), T.MinAlign);
|
|
if (T.A) {
|
|
EXPECT_EQ(commonAlignment(Align(T.A), MaybeAlign(T.B)), T.MinAlign);
|
|
}
|
|
if (T.B) {
|
|
EXPECT_EQ(commonAlignment(MaybeAlign(T.A), Align(T.B)), T.MinAlign);
|
|
}
|
|
if (T.A && T.B) {
|
|
EXPECT_EQ(commonAlignment(Align(T.A), Align(T.B)), T.MinAlign);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, Encode_Decode) {
|
|
for (uint64_t Value : getValidAlignments()) {
|
|
{
|
|
Align Actual(Value);
|
|
Align Expected = decodeMaybeAlign(encode(Actual)).getValue();
|
|
EXPECT_EQ(Expected, Actual);
|
|
}
|
|
{
|
|
MaybeAlign Actual(Value);
|
|
MaybeAlign Expected = decodeMaybeAlign(encode(Actual));
|
|
EXPECT_EQ(Expected, Actual);
|
|
}
|
|
}
|
|
MaybeAlign Actual(0);
|
|
MaybeAlign Expected = decodeMaybeAlign(encode(Actual));
|
|
EXPECT_EQ(Expected, Actual);
|
|
}
|
|
|
|
TEST(AlignmentTest, isAligned_isAddrAligned) {
|
|
struct {
|
|
uint64_t alignment;
|
|
uint64_t offset;
|
|
bool isAligned;
|
|
const void *forgedAddr() const {
|
|
// A value of any integral or enumeration type can be converted to a
|
|
// pointer type.
|
|
return reinterpret_cast<const void *>(offset);
|
|
}
|
|
} kTests[] = {
|
|
{1, 0, true}, {1, 1, true}, {1, 5, true}, {2, 0, true},
|
|
{2, 1, false}, {2, 2, true}, {2, 7, false}, {2, 16, true},
|
|
{4, 0, true}, {4, 1, false}, {4, 4, true}, {4, 6, false},
|
|
};
|
|
for (const auto &T : kTests) {
|
|
MaybeAlign A(T.alignment);
|
|
// Test Align
|
|
if (A) {
|
|
EXPECT_EQ(isAligned(A.getValue(), T.offset), T.isAligned);
|
|
EXPECT_EQ(isAddrAligned(A.getValue(), T.forgedAddr()), T.isAligned);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, offsetToAlignment) {
|
|
struct {
|
|
uint64_t alignment;
|
|
uint64_t offset;
|
|
uint64_t alignedOffset;
|
|
const void *forgedAddr() const {
|
|
// A value of any integral or enumeration type can be converted to a
|
|
// pointer type.
|
|
return reinterpret_cast<const void *>(offset);
|
|
}
|
|
} kTests[] = {
|
|
{1, 0, 0}, {1, 1, 0}, {1, 5, 0}, {2, 0, 0}, {2, 1, 1}, {2, 2, 0},
|
|
{2, 7, 1}, {2, 16, 0}, {4, 0, 0}, {4, 1, 3}, {4, 4, 0}, {4, 6, 2},
|
|
};
|
|
for (const auto &T : kTests) {
|
|
const Align A(T.alignment);
|
|
EXPECT_EQ(offsetToAlignment(T.offset, A), T.alignedOffset);
|
|
EXPECT_EQ(offsetToAlignedAddr(T.forgedAddr(), A), T.alignedOffset);
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, AlignComparisons) {
|
|
std::vector<uint64_t> ValidAlignments = getValidAlignments();
|
|
std::sort(ValidAlignments.begin(), ValidAlignments.end());
|
|
for (size_t I = 1; I < ValidAlignments.size(); ++I) {
|
|
assert(I >= 1);
|
|
const Align A(ValidAlignments[I - 1]);
|
|
const Align B(ValidAlignments[I]);
|
|
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);
|
|
|
|
EXPECT_EQ(A, A.value());
|
|
EXPECT_NE(A, B.value());
|
|
EXPECT_LT(A, B.value());
|
|
EXPECT_GT(B, A.value());
|
|
EXPECT_LE(A, B.value());
|
|
EXPECT_GE(B, A.value());
|
|
EXPECT_LE(A, A.value());
|
|
EXPECT_GE(A, A.value());
|
|
|
|
EXPECT_EQ(std::max(A, B), B);
|
|
EXPECT_EQ(std::min(A, B), A);
|
|
|
|
const MaybeAlign MA(ValidAlignments[I - 1]);
|
|
const MaybeAlign MB(ValidAlignments[I]);
|
|
EXPECT_EQ(MA, MA);
|
|
EXPECT_NE(MA, MB);
|
|
|
|
EXPECT_EQ(MA, MA ? (*MA).value() : 0);
|
|
EXPECT_NE(MA, MB ? (*MB).value() : 0);
|
|
|
|
EXPECT_EQ(std::max(A, B), B);
|
|
EXPECT_EQ(std::min(A, B), A);
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentTest, Max) {
|
|
// We introduce std::max here to test ADL.
|
|
using std::max;
|
|
|
|
// Uses llvm::max.
|
|
EXPECT_EQ(max(MaybeAlign(), Align(2)), Align(2));
|
|
EXPECT_EQ(max(Align(2), MaybeAlign()), Align(2));
|
|
|
|
EXPECT_EQ(max(MaybeAlign(1), Align(2)), Align(2));
|
|
EXPECT_EQ(max(Align(2), MaybeAlign(1)), Align(2));
|
|
|
|
EXPECT_EQ(max(MaybeAlign(2), Align(2)), Align(2));
|
|
EXPECT_EQ(max(Align(2), MaybeAlign(2)), Align(2));
|
|
|
|
EXPECT_EQ(max(MaybeAlign(4), Align(2)), Align(4));
|
|
EXPECT_EQ(max(Align(2), MaybeAlign(4)), Align(4));
|
|
|
|
// Uses std::max.
|
|
EXPECT_EQ(max(Align(2), Align(4)), Align(4));
|
|
}
|
|
|
|
TEST(AlignmentTest, AssumeAligned) {
|
|
EXPECT_EQ(assumeAligned(0), Align(1));
|
|
EXPECT_EQ(assumeAligned(0), Align());
|
|
EXPECT_EQ(assumeAligned(1), Align(1));
|
|
EXPECT_EQ(assumeAligned(1), Align());
|
|
}
|
|
|
|
// Death tests reply on assert which is disabled in release mode.
|
|
#ifndef NDEBUG
|
|
|
|
// We use a subset of valid alignments for DEATH_TESTs as they are particularly
|
|
// slow.
|
|
std::vector<uint64_t> getValidAlignmentsForDeathTest() {
|
|
return {1, 1ULL << 31, 1ULL << 63};
|
|
}
|
|
|
|
std::vector<uint64_t> getNonPowerOfTwo() { return {3, 10, 15}; }
|
|
|
|
TEST(AlignmentDeathTest, CantConvertUnsetMaybe) {
|
|
EXPECT_DEATH((MaybeAlign(0).getValue()), ".*");
|
|
}
|
|
|
|
TEST(AlignmentDeathTest, Division) {
|
|
EXPECT_DEATH(Align(1) / 2, "Can't halve byte alignment");
|
|
EXPECT_DEATH(MaybeAlign(1) / 2, "Can't halve byte alignment");
|
|
|
|
EXPECT_DEATH(Align(8) / 0, "Divisor must be positive and a power of 2");
|
|
EXPECT_DEATH(Align(8) / 3, "Divisor must be positive and a power of 2");
|
|
}
|
|
|
|
TEST(AlignmentDeathTest, InvalidCTors) {
|
|
EXPECT_DEATH((Align(0)), "Value must not be 0");
|
|
for (uint64_t Value : getNonPowerOfTwo()) {
|
|
EXPECT_DEATH((Align(Value)), "Alignment is not a power of 2");
|
|
EXPECT_DEATH((MaybeAlign(Value)),
|
|
"Alignment is neither 0 nor a power of 2");
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentDeathTest, ComparisonsWithZero) {
|
|
for (uint64_t Value : getValidAlignmentsForDeathTest()) {
|
|
EXPECT_DEATH((void)(Align(Value) == 0), ".* should be defined");
|
|
EXPECT_DEATH((void)(Align(Value) != 0), ".* should be defined");
|
|
EXPECT_DEATH((void)(Align(Value) >= 0), ".* should be defined");
|
|
EXPECT_DEATH((void)(Align(Value) <= 0), ".* should be defined");
|
|
EXPECT_DEATH((void)(Align(Value) > 0), ".* should be defined");
|
|
EXPECT_DEATH((void)(Align(Value) < 0), ".* should be defined");
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentDeathTest, CompareMaybeAlignToZero) {
|
|
for (uint64_t Value : getValidAlignmentsForDeathTest()) {
|
|
// MaybeAlign is allowed to be == or != 0
|
|
(void)(MaybeAlign(Value) == 0);
|
|
(void)(MaybeAlign(Value) != 0);
|
|
}
|
|
}
|
|
|
|
TEST(AlignmentDeathTest, AlignAddr) {
|
|
const void *const unaligned_high_ptr =
|
|
reinterpret_cast<const void *>(std::numeric_limits<uintptr_t>::max() - 1);
|
|
EXPECT_DEATH(alignAddr(unaligned_high_ptr, Align(16)), "Overflow");
|
|
}
|
|
|
|
#endif // NDEBUG
|
|
|
|
} // end anonymous namespace
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|