1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/off-thread-factory.h"
6
7 #include "src/ast/ast-value-factory.h"
8 #include "src/ast/ast.h"
9 #include "src/base/logging.h"
10 #include "src/common/globals.h"
11 #include "src/execution/isolate.h"
12 #include "src/handles/handles.h"
13 #include "src/heap/spaces-inl.h"
14 #include "src/heap/spaces.h"
15 #include "src/objects/fixed-array.h"
16 #include "src/objects/heap-object.h"
17 #include "src/objects/map-inl.h"
18 #include "src/objects/objects-body-descriptors-inl.h"
19 #include "src/objects/shared-function-info.h"
20 #include "src/objects/string.h"
21 #include "src/objects/visitors.h"
22 #include "src/roots/roots-inl.h"
23 #include "src/roots/roots.h"
24 #include "src/tracing/trace-event.h"
25
26 namespace v8 {
27 namespace internal {
28
OffThreadFactory(Isolate * isolate)29 OffThreadFactory::OffThreadFactory(Isolate* isolate)
30 : roots_(isolate), space_(isolate->heap()), lo_space_(isolate->heap()) {}
31
32 namespace {
33
34 class StringSlotCollectingVisitor : public ObjectVisitor {
35 public:
StringSlotCollectingVisitor(ReadOnlyRoots roots)36 explicit StringSlotCollectingVisitor(ReadOnlyRoots roots) : roots_(roots) {}
37
VisitPointers(HeapObject host,ObjectSlot start,ObjectSlot end)38 void VisitPointers(HeapObject host, ObjectSlot start,
39 ObjectSlot end) override {
40 for (ObjectSlot slot = start; slot != end; ++slot) {
41 Object obj = *slot;
42 if (obj.IsInternalizedString() &&
43 !ReadOnlyHeap::Contains(HeapObject::cast(obj))) {
44 string_slots.emplace_back(host.ptr(), slot.address() - host.ptr());
45 }
46 }
47 }
VisitPointers(HeapObject host,MaybeObjectSlot start,MaybeObjectSlot end)48 void VisitPointers(HeapObject host, MaybeObjectSlot start,
49 MaybeObjectSlot end) override {
50 for (MaybeObjectSlot slot = start; slot != end; ++slot) {
51 MaybeObject maybe_obj = *slot;
52 HeapObject obj;
53 if (maybe_obj.GetHeapObjectIfStrong(&obj)) {
54 if (obj.IsInternalizedString() && !ReadOnlyHeap::Contains(obj)) {
55 string_slots.emplace_back(host.ptr(), slot.address() - host.ptr());
56 }
57 }
58 }
59 }
60
VisitCodeTarget(Code host,RelocInfo * rinfo)61 void VisitCodeTarget(Code host, RelocInfo* rinfo) override { UNREACHABLE(); }
VisitEmbeddedPointer(Code host,RelocInfo * rinfo)62 void VisitEmbeddedPointer(Code host, RelocInfo* rinfo) override {
63 UNREACHABLE();
64 }
65
66 std::vector<RelativeSlot> string_slots;
67
68 private:
69 ReadOnlyRoots roots_;
70 };
71
72 } // namespace
73
FinishOffThread()74 void OffThreadFactory::FinishOffThread() {
75 DCHECK(!is_finished);
76
77 StringSlotCollectingVisitor string_slot_collector(read_only_roots());
78
79 // First iterate all objects in the spaces to find string slots. At this point
80 // all string slots have to point to off-thread strings or read-only strings.
81 {
82 PagedSpaceObjectIterator it(&space_);
83 for (HeapObject obj = it.Next(); !obj.is_null(); obj = it.Next()) {
84 obj.IterateBodyFast(&string_slot_collector);
85 }
86 }
87 {
88 LargeObjectSpaceObjectIterator it(&lo_space_);
89 for (HeapObject obj = it.Next(); !obj.is_null(); obj = it.Next()) {
90 obj.IterateBodyFast(&string_slot_collector);
91 }
92 }
93
94 string_slots_ = std::move(string_slot_collector.string_slots);
95
96 is_finished = true;
97 }
98
Publish(Isolate * isolate)99 void OffThreadFactory::Publish(Isolate* isolate) {
100 DCHECK(is_finished);
101
102 HandleScope handle_scope(isolate);
103
104 // First, handlify all the string slot holder objects, so that we can keep
105 // track of them if they move.
106 //
107 // TODO(leszeks): We might be able to create a HandleScope-compatible
108 // structure off-thread and merge it into the current handle scope all in one
109 // go (DeferredHandles maybe?).
110 std::vector<Handle<HeapObject>> heap_object_handles;
111 std::vector<Handle<Script>> script_handles;
112 {
113 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
114 "V8.OffThreadFinalization.Publish.CollectHandles");
115 heap_object_handles.reserve(string_slots_.size());
116 for (RelativeSlot relative_slot : string_slots_) {
117 // TODO(leszeks): Group slots in the same parent object to avoid creating
118 // multiple duplicate handles.
119 heap_object_handles.push_back(handle(
120 HeapObject::cast(Object(relative_slot.object_address)), isolate));
121
122 // De-internalize the string so that we can re-internalize it later.
123 ObjectSlot slot(relative_slot.object_address + relative_slot.slot_offset);
124 String string = String::cast(slot.Acquire_Load());
125 bool one_byte = string.IsOneByteRepresentation();
126 Map map = one_byte ? read_only_roots().one_byte_string_map()
127 : read_only_roots().string_map();
128 string.set_map_no_write_barrier(map);
129 }
130
131 script_handles.reserve(script_list_.size());
132 for (Script script : script_list_) {
133 script_handles.push_back(handle(script, isolate));
134 }
135 }
136
137 // Then merge the spaces. At this point, we are allowed to point between (no
138 // longer) off-thread pages and main-thread heap pages, and objects in the
139 // previously off-thread page can move.
140 {
141 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
142 "V8.OffThreadFinalization.Publish.Merge");
143 isolate->heap()->old_space()->MergeLocalSpace(&space_);
144 isolate->heap()->lo_space()->MergeOffThreadSpace(&lo_space_);
145 }
146
147 // Iterate the string slots, as an offset from the holders we have handles to.
148 {
149 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
150 "V8.OffThreadFinalization.Publish.UpdateHandles");
151 for (size_t i = 0; i < string_slots_.size(); ++i) {
152 int slot_offset = string_slots_[i].slot_offset;
153
154 // There's currently no cases where the holder object could have been
155 // resized.
156 DCHECK_LT(slot_offset, heap_object_handles[i]->Size());
157
158 ObjectSlot slot(heap_object_handles[i]->ptr() + slot_offset);
159
160 String string = String::cast(slot.Acquire_Load());
161 if (string.IsThinString()) {
162 // We may have already internalized this string via another slot.
163 slot.Release_Store(ThinString::cast(string).GetUnderlying());
164 } else {
165 HandleScope handle_scope(isolate);
166
167 Handle<String> string_handle = handle(string, isolate);
168 Handle<String> internalized_string =
169 isolate->factory()->InternalizeString(string_handle);
170
171 // Recalculate the slot in case there was GC and the holder moved.
172 ObjectSlot slot(heap_object_handles[i]->ptr() + slot_offset);
173
174 DCHECK(string_handle->IsThinString() ||
175 string_handle->IsInternalizedString());
176 if (*string_handle != *internalized_string) {
177 slot.Release_Store(*internalized_string);
178 }
179 }
180 }
181
182 // Merge the recorded scripts into the isolate's script list.
183 // This for loop may seem expensive, but practically there's unlikely to be
184 // more than one script in the OffThreadFactory.
185 Handle<WeakArrayList> scripts = isolate->factory()->script_list();
186 for (Handle<Script> script_handle : script_handles) {
187 scripts = WeakArrayList::Append(isolate, scripts,
188 MaybeObjectHandle::Weak(script_handle));
189 }
190 isolate->heap()->SetRootScriptList(*scripts);
191 }
192 }
193
194 // Hacky method for creating a simple object with a slot pointing to a string.
195 // TODO(leszeks): Remove once we have full FixedArray support.
StringWrapperForTest(Handle<String> string)196 Handle<FixedArray> OffThreadFactory::StringWrapperForTest(
197 Handle<String> string) {
198 HeapObject wrapper =
199 AllocateRaw(FixedArray::SizeFor(1), AllocationType::kOld);
200 wrapper.set_map_after_allocation(read_only_roots().fixed_array_map());
201 FixedArray array = FixedArray::cast(wrapper);
202 array.set_length(1);
203 array.data_start().Relaxed_Store(*string);
204 return handle(array, isolate());
205 }
206
MakeOrFindTwoCharacterString(uint16_t c1,uint16_t c2)207 Handle<String> OffThreadFactory::MakeOrFindTwoCharacterString(uint16_t c1,
208 uint16_t c2) {
209 // TODO(leszeks): Do some real caching here. Also, these strings should be
210 // internalized.
211 if ((c1 | c2) <= unibrow::Latin1::kMaxChar) {
212 Handle<SeqOneByteString> ret =
213 NewRawOneByteString(2, AllocationType::kOld).ToHandleChecked();
214 ret->SeqOneByteStringSet(0, c1);
215 ret->SeqOneByteStringSet(1, c2);
216 return ret;
217 }
218 Handle<SeqTwoByteString> ret =
219 NewRawTwoByteString(2, AllocationType::kOld).ToHandleChecked();
220 ret->SeqTwoByteStringSet(0, c1);
221 ret->SeqTwoByteStringSet(1, c2);
222 return ret;
223 }
224
InternalizeString(const Vector<const uint8_t> & string)225 Handle<String> OffThreadFactory::InternalizeString(
226 const Vector<const uint8_t>& string) {
227 uint32_t hash = StringHasher::HashSequentialString(
228 string.begin(), string.length(), HashSeed(read_only_roots()));
229 return NewOneByteInternalizedString(string, hash);
230 }
231
InternalizeString(const Vector<const uint16_t> & string)232 Handle<String> OffThreadFactory::InternalizeString(
233 const Vector<const uint16_t>& string) {
234 uint32_t hash = StringHasher::HashSequentialString(
235 string.begin(), string.length(), HashSeed(read_only_roots()));
236 return NewTwoByteInternalizedString(string, hash);
237 }
238
AddToScriptList(Handle<Script> shared)239 void OffThreadFactory::AddToScriptList(Handle<Script> shared) {
240 script_list_.push_back(*shared);
241 }
242
AllocateRaw(int size,AllocationType allocation,AllocationAlignment alignment)243 HeapObject OffThreadFactory::AllocateRaw(int size, AllocationType allocation,
244 AllocationAlignment alignment) {
245 DCHECK(!is_finished);
246
247 DCHECK_EQ(allocation, AllocationType::kOld);
248 AllocationResult result;
249 if (size > kMaxRegularHeapObjectSize) {
250 result = lo_space_.AllocateRaw(size);
251 } else {
252 result = space_.AllocateRaw(size, alignment);
253 }
254 return result.ToObjectChecked();
255 }
256
257 } // namespace internal
258 } // namespace v8
259