summaryrefslogtreecommitdiff
path: root/deps/v8/test/unittests/heap/cppgc/allocation-unittest.cc
blob: 9a18c49a2c74034ab5c53717af4277d5e9f7874a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "include/cppgc/allocation.h"

#include "include/cppgc/visitor.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-object-header.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cppgc {
namespace internal {

namespace {

class CppgcAllocationTest : public testing::TestWithHeap {};

struct GCed final : GarbageCollected<GCed> {
  void Trace(cppgc::Visitor*) const {}
};

class HeapAllocatedArray final : public GarbageCollected<HeapAllocatedArray> {
 public:
  HeapAllocatedArray() {
    for (int i = 0; i < kArraySize; ++i) {
      array_[i] = i % 128;
    }
  }

  int8_t at(size_t i) { return array_[i]; }
  void Trace(Visitor* visitor) const {}

 private:
  static const int kArraySize = 1000;
  int8_t array_[kArraySize];
};

}  // namespace

TEST_F(CppgcAllocationTest, MakeGarbageCollectedPreservesPayload) {
  // Allocate an object in the heap.
  HeapAllocatedArray* array =
      MakeGarbageCollected<HeapAllocatedArray>(GetAllocationHandle());

  // Sanity check of the contents in the heap.
  EXPECT_EQ(0, array->at(0));
  EXPECT_EQ(42, array->at(42));
  EXPECT_EQ(0, array->at(128));
  EXPECT_EQ(999 % 128, array->at(999));
}

TEST_F(CppgcAllocationTest, ReuseMemoryFromFreelist) {
  // Allocate 3 objects so that the address we look for below is not at the
  // start of the page.
  MakeGarbageCollected<GCed>(GetAllocationHandle());
  MakeGarbageCollected<GCed>(GetAllocationHandle());
  GCed* p1 = MakeGarbageCollected<GCed>(GetAllocationHandle());
  // GC reclaims all objects. LABs are reset during the GC.
  PreciseGC();
  // Now the freed memory in the first GC should be reused. Allocating 3
  // objects again should suffice but allocating 5 to give the test some slack.
  bool reused_memory_found = false;
  for (int i = 0; i < 5; i++) {
    GCed* p2 = MakeGarbageCollected<GCed>(GetAllocationHandle());
    if (p1 == p2) {
      reused_memory_found = true;
      break;
    }
  }
  EXPECT_TRUE(reused_memory_found);
}

namespace {
class CallbackInCtor final : public GarbageCollected<CallbackInCtor> {
 public:
  template <typename Callback>
  explicit CallbackInCtor(Callback callback) {
    callback();
  }

  void Trace(Visitor*) const {}
};
}  // namespace

TEST_F(CppgcAllocationTest,
       ConservativeGCDuringAllocationDoesNotReclaimObject) {
  CallbackInCtor* obj = MakeGarbageCollected<CallbackInCtor>(
      GetAllocationHandle(), [this]() { ConservativeGC(); });
  EXPECT_FALSE(HeapObjectHeader::FromObject(obj).IsFree());
}

namespace {
class LargeObject : public GarbageCollected<LargeObject> {
 public:
  static constexpr size_t kDataSize = kLargeObjectSizeThreshold + 1;
  static size_t destructor_calls;

  explicit LargeObject(bool check) {
    if (!check) return;
    for (size_t i = 0; i < LargeObject::kDataSize; ++i) {
      EXPECT_EQ(0, data[i]);
    }
  }
  ~LargeObject() { ++destructor_calls; }
  void Trace(Visitor*) const {}

  char data[kDataSize];
};
size_t LargeObject::destructor_calls = 0u;
}  // namespace

TEST_F(CppgcAllocationTest, LargePagesAreZeroedOut) {
  static constexpr size_t kNumObjects = 1u;
  LargeObject::destructor_calls = 0u;
  std::vector<void*> pages;
  for (size_t i = 0; i < kNumObjects; ++i) {
    auto* obj = MakeGarbageCollected<LargeObject>(GetAllocationHandle(), false);
    pages.push_back(obj);
    memset(obj->data, 0xff, LargeObject::kDataSize);
  }
  PreciseGC();
  EXPECT_EQ(kNumObjects, LargeObject::destructor_calls);
  bool reused_page = false;
  for (size_t i = 0; i < kNumObjects; ++i) {
    auto* obj = MakeGarbageCollected<LargeObject>(GetAllocationHandle(), true);
    if (std::find(pages.begin(), pages.end(), obj) != pages.end())
      reused_page = true;
  }
  EXPECT_TRUE(reused_page);
}

namespace {

constexpr size_t kDoubleWord = 2 * sizeof(void*);
constexpr size_t kWord = sizeof(void*);

class alignas(kDoubleWord) DoubleWordAligned final
    : public GarbageCollected<DoubleWordAligned> {
 public:
  void Trace(Visitor*) const {}
};

class alignas(kDoubleWord) LargeDoubleWordAligned
    : public GarbageCollected<LargeDoubleWordAligned> {
 public:
  virtual void Trace(cppgc::Visitor*) const {}
  char array[kLargeObjectSizeThreshold];
};

template <size_t Size>
class CustomPadding final : public GarbageCollected<CustomPadding<Size>> {
 public:
  void Trace(cppgc::Visitor* visitor) const {}
  char base_size[128];  // Gets allocated in using RegularSpaceType::kNormal4.
  char padding[Size];
};

template <size_t Size>
class alignas(kDoubleWord) AlignedCustomPadding final
    : public GarbageCollected<AlignedCustomPadding<Size>> {
 public:
  void Trace(cppgc::Visitor* visitor) const {}
  char base_size[128];  // Gets allocated in using RegularSpaceType::kNormal4.
  char padding[Size];
};

}  // namespace

TEST_F(CppgcAllocationTest, DoubleWordAlignedAllocation) {
  static constexpr size_t kAlignmentMask = kDoubleWord - 1;
  auto* gced = MakeGarbageCollected<DoubleWordAligned>(GetAllocationHandle());
  EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(gced) & kAlignmentMask);
}

TEST_F(CppgcAllocationTest, LargeDoubleWordAlignedAllocation) {
  static constexpr size_t kAlignmentMask = kDoubleWord - 1;
  auto* gced =
      MakeGarbageCollected<LargeDoubleWordAligned>(GetAllocationHandle());
  EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(gced) & kAlignmentMask);
}

TEST_F(CppgcAllocationTest, AlignToDoubleWordFromUnaligned) {
  static constexpr size_t kAlignmentMask = kDoubleWord - 1;
  auto* padding_object =
      MakeGarbageCollected<CustomPadding<kWord>>(GetAllocationHandle());
  // The address from which the next object can be allocated, i.e. the end of
  // |padding_object|, should not be properly aligned.
  ASSERT_EQ(kWord, (reinterpret_cast<uintptr_t>(padding_object) +
                    sizeof(*padding_object)) &
                       kAlignmentMask);
  auto* aligned_object =
      MakeGarbageCollected<AlignedCustomPadding<16>>(GetAllocationHandle());
  EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(aligned_object) & kAlignmentMask);
  // Test only yielded a reliable result if objects are adjacent to each other.
  ASSERT_EQ(reinterpret_cast<uintptr_t>(padding_object) +
                sizeof(*padding_object) + sizeof(HeapObjectHeader),
            reinterpret_cast<uintptr_t>(aligned_object));
}

TEST_F(CppgcAllocationTest, AlignToDoubleWordFromAligned) {
  static constexpr size_t kAlignmentMask = kDoubleWord - 1;
  auto* padding_object =
      MakeGarbageCollected<CustomPadding<16>>(GetAllocationHandle());
  // The address from which the next object can be allocated, i.e. the end of
  // |padding_object|, should be properly aligned.
  ASSERT_EQ(0u, (reinterpret_cast<uintptr_t>(padding_object) +
                 sizeof(*padding_object)) &
                    kAlignmentMask);
  auto* aligned_object =
      MakeGarbageCollected<AlignedCustomPadding<16>>(GetAllocationHandle());
  EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(aligned_object) & kAlignmentMask);
  // Test only yielded a reliable result if objects are adjacent to each other.
  ASSERT_EQ(reinterpret_cast<uintptr_t>(padding_object) +
                sizeof(*padding_object) + 2 * sizeof(HeapObjectHeader),
            reinterpret_cast<uintptr_t>(aligned_object));
}

}  // namespace internal
}  // namespace cppgc