summaryrefslogtreecommitdiff
path: root/deps/v8/test/unittests/heap/cppgc/heap-unittest.cc
blob: 694d031ddaeb8b3a4567ff63b70e868397338c7b (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
// Copyright 2020 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 "src/heap/cppgc/heap.h"

#include <algorithm>
#include <iterator>
#include <numeric>

#include "include/cppgc/allocation.h"
#include "src/heap/cppgc/globals.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cppgc {
namespace internal {

namespace {

class GCHeapTest : public testing::TestWithHeap {
 public:
  void ConservativeGC() {
    internal::Heap::From(GetHeap())->CollectGarbage(
        Heap::Config::ConservativeAtomicConfig());
  }
  void PreciseGC() {
    internal::Heap::From(GetHeap())->CollectGarbage(
        Heap::Config::PreciseAtomicConfig());
  }
};

class Foo : public GarbageCollected<Foo> {
 public:
  static size_t destructor_callcount;

  Foo() { destructor_callcount = 0; }
  ~Foo() { destructor_callcount++; }

  void Trace(cppgc::Visitor*) const {}
};

size_t Foo::destructor_callcount;

template <size_t Size>
class GCed : public GarbageCollected<Foo> {
 public:
  void Trace(cppgc::Visitor*) const {}
  char buf[Size];
};

}  // namespace

TEST_F(GCHeapTest, PreciseGCReclaimsObjectOnStack) {
  Foo* volatile do_not_access =
      MakeGarbageCollected<Foo>(GetAllocationHandle());
  USE(do_not_access);
  EXPECT_EQ(0u, Foo::destructor_callcount);
  PreciseGC();
  EXPECT_EQ(1u, Foo::destructor_callcount);
  PreciseGC();
  EXPECT_EQ(1u, Foo::destructor_callcount);
}

namespace {

const void* ConservativeGCReturningObject(cppgc::Heap* heap,
                                          const void* volatile object) {
  internal::Heap::From(heap)->CollectGarbage(
      Heap::Config::ConservativeAtomicConfig());
  return object;
}

}  // namespace

TEST_F(GCHeapTest, ConservativeGCRetainsObjectOnStack) {
  Foo* volatile object = MakeGarbageCollected<Foo>(GetAllocationHandle());
  EXPECT_EQ(0u, Foo::destructor_callcount);
  EXPECT_EQ(object, ConservativeGCReturningObject(GetHeap(), object));
  EXPECT_EQ(0u, Foo::destructor_callcount);
  PreciseGC();
  EXPECT_EQ(1u, Foo::destructor_callcount);
  PreciseGC();
  EXPECT_EQ(1u, Foo::destructor_callcount);
}

TEST_F(GCHeapTest, ObjectPayloadSize) {
  static constexpr size_t kNumberOfObjectsPerArena = 16;
  static constexpr size_t kObjectSizes[] = {1, 32, 64, 128,
                                            2 * kLargeObjectSizeThreshold};

  Heap::From(GetHeap())->CollectGarbage(
      GarbageCollector::Config::ConservativeAtomicConfig());

  Heap::NoGCScope no_gc_scope(*Heap::From(GetHeap()));

  for (size_t k = 0; k < kNumberOfObjectsPerArena; ++k) {
    MakeGarbageCollected<GCed<kObjectSizes[0]>>(GetAllocationHandle());
    MakeGarbageCollected<GCed<kObjectSizes[1]>>(GetAllocationHandle());
    MakeGarbageCollected<GCed<kObjectSizes[2]>>(GetAllocationHandle());
    MakeGarbageCollected<GCed<kObjectSizes[3]>>(GetAllocationHandle());
    MakeGarbageCollected<GCed<kObjectSizes[4]>>(GetAllocationHandle());
  }

  size_t aligned_object_sizes[arraysize(kObjectSizes)];
  std::transform(std::cbegin(kObjectSizes), std::cend(kObjectSizes),
                 std::begin(aligned_object_sizes), [](size_t size) {
                   return RoundUp(size, kAllocationGranularity);
                 });
  const size_t expected_size = std::accumulate(
      std::cbegin(aligned_object_sizes), std::cend(aligned_object_sizes), 0u,
      [](size_t acc, size_t size) {
        return acc + kNumberOfObjectsPerArena * size;
      });
  // TODO(chromium:1056170): Change to EXPECT_EQ when proper sweeping is
  // implemented.
  EXPECT_LE(expected_size, Heap::From(GetHeap())->ObjectPayloadSize());
}

TEST_F(GCHeapTest, AllocateWithAdditionalBytes) {
  static constexpr size_t kBaseSize = sizeof(HeapObjectHeader) + sizeof(Foo);
  static constexpr size_t kAdditionalBytes = 10u * kAllocationGranularity;
  {
    Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle());
    EXPECT_LE(kBaseSize, HeapObjectHeader::FromPayload(object).GetSize());
  }
  {
    Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle(),
                                            AdditionalBytes(kAdditionalBytes));
    EXPECT_LE(kBaseSize + kAdditionalBytes,
              HeapObjectHeader::FromPayload(object).GetSize());
  }
  {
    Foo* object = MakeGarbageCollected<Foo>(
        GetAllocationHandle(),
        AdditionalBytes(kAdditionalBytes * kAdditionalBytes));
    EXPECT_LE(kBaseSize + kAdditionalBytes * kAdditionalBytes,
              HeapObjectHeader::FromPayload(object).GetSize());
  }
}

TEST_F(GCHeapTest, AllocatedSizeDependOnAdditionalBytes) {
  static constexpr size_t kAdditionalBytes = 10u * kAllocationGranularity;
  Foo* object = MakeGarbageCollected<Foo>(GetAllocationHandle());
  Foo* object_with_bytes = MakeGarbageCollected<Foo>(
      GetAllocationHandle(), AdditionalBytes(kAdditionalBytes));
  Foo* object_with_more_bytes = MakeGarbageCollected<Foo>(
      GetAllocationHandle(),
      AdditionalBytes(kAdditionalBytes * kAdditionalBytes));
  EXPECT_LT(HeapObjectHeader::FromPayload(object).GetSize(),
            HeapObjectHeader::FromPayload(object_with_bytes).GetSize());
  EXPECT_LT(HeapObjectHeader::FromPayload(object_with_bytes).GetSize(),
            HeapObjectHeader::FromPayload(object_with_more_bytes).GetSize());
}

}  // namespace internal
}  // namespace cppgc