blob: e8cd5f4285e526ad870a8429ce2cf965d63d91a2 [file] [log] [blame]
//=== - unittest/Support/OptimizedStructLayoutTest.cpp - Layout 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/OptimizedStructLayout.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
class LayoutTest {
struct Field {
uint64_t Size;
Align Alignment;
uint64_t ForcedOffset;
uint64_t ExpectedOffset;
};
SmallVector<Field, 16> Fields;
bool Verified = false;
public:
LayoutTest() {}
LayoutTest(const LayoutTest &) = delete;
LayoutTest &operator=(const LayoutTest &) = delete;
~LayoutTest() { assert(Verified); }
LayoutTest &flexible(uint64_t Size, uint64_t Alignment,
uint64_t ExpectedOffset) {
Fields.push_back({Size, Align(Alignment),
OptimizedStructLayoutField::FlexibleOffset, ExpectedOffset});
return *this;
}
LayoutTest &fixed(uint64_t Size, uint64_t Alignment, uint64_t Offset) {
Fields.push_back({Size, Align(Alignment), Offset, Offset});
return *this;
}
void verify(uint64_t ExpectedSize, uint64_t ExpectedAlignment) {
SmallVector<OptimizedStructLayoutField, 8> LayoutFields;
LayoutFields.reserve(Fields.size());
for (auto &F : Fields)
LayoutFields.emplace_back(&F, F.Size, F.Alignment, F.ForcedOffset);
auto SizeAndAlign = performOptimizedStructLayout(LayoutFields);
EXPECT_EQ(SizeAndAlign.first, ExpectedSize);
EXPECT_EQ(SizeAndAlign.second, Align(ExpectedAlignment));
for (auto &LF : LayoutFields) {
auto &F = *static_cast<const Field *>(LF.Id);
EXPECT_EQ(LF.Offset, F.ExpectedOffset);
}
Verified = true;
}
};
}
TEST(OptimizedStructLayoutTest, Basic) {
LayoutTest()
.flexible(12, 4, 8)
.flexible(8, 8, 0)
.flexible(4, 4, 20)
.verify(24, 8);
}
TEST(OptimizedStructLayoutTest, OddSize) {
LayoutTest()
.flexible(8, 8, 16)
.flexible(4, 4, 12)
.flexible(1, 1, 10)
.flexible(10, 8, 0)
.verify(24, 8);
}
TEST(OptimizedStructLayoutTest, Gaps) {
LayoutTest()
.fixed(4, 4, 8)
.fixed(4, 4, 16)
.flexible(4, 4, 0)
.flexible(4, 4, 4)
.flexible(4, 4, 12)
.flexible(4, 4, 20)
.verify(24, 4);
}
TEST(OptimizedStructLayoutTest, Greed) {
// The greedy algorithm doesn't find the optimal layout here, which
// would be to put the 5-byte field at the end.
LayoutTest()
.fixed(4, 4, 8)
.flexible(5, 4, 0)
.flexible(4, 4, 12)
.flexible(4, 4, 16)
.flexible(4, 4, 20)
.verify(24, 4);
}
TEST(OptimizedStructLayoutTest, Jagged) {
LayoutTest()
.flexible(1, 2, 18)
.flexible(13, 8, 0)
.flexible(3, 2, 14)
.verify(19, 8);
}
TEST(OptimizedStructLayoutTest, GardenPath) {
// The 4-byte-aligned field is our highest priority, but the less-aligned
// fields keep leaving the end offset mis-aligned.
LayoutTest()
.fixed(7, 4, 0)
.flexible(4, 4, 44)
.flexible(6, 1, 7)
.flexible(5, 1, 13)
.flexible(7, 2, 18)
.flexible(4, 1, 25)
.flexible(4, 1, 29)
.flexible(1, 1, 33)
.flexible(4, 2, 34)
.flexible(4, 2, 38)
.flexible(2, 2, 42)
.flexible(2, 2, 48)
.verify(50, 4);
}
// PR 51131
TEST(OptimizedStructLayoutTest, HighAlignment) {
// Handle the case where a flexible field has such a high alignment
// requirement that aligning LastEnd to it gives an offset past the
// end of the gap before the next fixed-alignment field.
LayoutTest()
.fixed(8, 8, 0)
.fixed(8, 8, 8)
.fixed(64, 64, 64)
.flexible(1, 1, 16)
.flexible(1, 1, 17)
.flexible(4, 128, 128)
.flexible(1, 1, 18)
.flexible(1, 1, 19)
.verify(132, 128);
}