1 // Copyright 2019 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/objects/osr-optimized-code-cache.h"
6
7 #include "src/execution/isolate-inl.h"
8 #include "src/objects/code.h"
9 #include "src/objects/maybe-object.h"
10 #include "src/objects/shared-function-info.h"
11
12 namespace v8 {
13 namespace internal {
14
15 // static
Empty(Isolate * isolate)16 Handle<OSROptimizedCodeCache> OSROptimizedCodeCache::Empty(Isolate* isolate) {
17 return Handle<OSROptimizedCodeCache>::cast(
18 isolate->factory()->empty_weak_fixed_array());
19 }
20
21 // static
Insert(Isolate * isolate,Handle<NativeContext> native_context,Handle<SharedFunctionInfo> shared,Handle<CodeT> code,BytecodeOffset osr_offset)22 void OSROptimizedCodeCache::Insert(Isolate* isolate,
23 Handle<NativeContext> native_context,
24 Handle<SharedFunctionInfo> shared,
25 Handle<CodeT> code,
26 BytecodeOffset osr_offset) {
27 DCHECK(!osr_offset.IsNone());
28 DCHECK(!isolate->serializer_enabled());
29 DCHECK(CodeKindIsOptimizedJSFunction(code->kind()));
30
31 Handle<OSROptimizedCodeCache> osr_cache(native_context->osr_code_cache(),
32 isolate);
33
34 if (shared->osr_code_cache_state() == kNotCached) {
35 DCHECK_EQ(osr_cache->FindEntry(*shared, osr_offset), -1);
36 } else if (osr_cache->FindEntry(*shared, osr_offset) != -1) {
37 return; // Already cached for a different JSFunction.
38 }
39
40 STATIC_ASSERT(kEntryLength == 3);
41 int entry = -1;
42 for (int index = 0; index < osr_cache->length(); index += kEntryLength) {
43 if (osr_cache->Get(index + kSharedOffset)->IsCleared() ||
44 osr_cache->Get(index + kCachedCodeOffset)->IsCleared()) {
45 entry = index;
46 break;
47 }
48 }
49
50 if (entry == -1) {
51 if (osr_cache->length() + kEntryLength <= kMaxLength) {
52 entry = GrowOSRCache(isolate, native_context, &osr_cache);
53 } else {
54 // We reached max capacity and cannot grow further. Reuse an existing
55 // entry.
56 // TODO(mythria): We could use better mechanisms (like lru) to replace
57 // existing entries. Though we don't expect this to be a common case, so
58 // for now choosing to replace the first entry.
59 osr_cache->ClearEntry(0, isolate);
60 entry = 0;
61 }
62 }
63
64 osr_cache->InitializeEntry(entry, *shared, *code, osr_offset);
65 }
66
Clear(Isolate * isolate,NativeContext native_context)67 void OSROptimizedCodeCache::Clear(Isolate* isolate,
68 NativeContext native_context) {
69 native_context.set_osr_code_cache(*OSROptimizedCodeCache::Empty(isolate));
70 }
71
Compact(Isolate * isolate,Handle<NativeContext> native_context)72 void OSROptimizedCodeCache::Compact(Isolate* isolate,
73 Handle<NativeContext> native_context) {
74 Handle<OSROptimizedCodeCache> osr_cache(native_context->osr_code_cache(),
75 isolate);
76
77 // Re-adjust the cache so all the valid entries are on one side. This will
78 // enable us to compress the cache if needed.
79 int curr_valid_index = 0;
80 for (int curr_index = 0; curr_index < osr_cache->length();
81 curr_index += kEntryLength) {
82 if (osr_cache->Get(curr_index + kSharedOffset)->IsCleared() ||
83 osr_cache->Get(curr_index + kCachedCodeOffset)->IsCleared()) {
84 continue;
85 }
86 if (curr_valid_index != curr_index) {
87 osr_cache->MoveEntry(curr_index, curr_valid_index, isolate);
88 }
89 curr_valid_index += kEntryLength;
90 }
91
92 if (!NeedsTrimming(curr_valid_index, osr_cache->length())) return;
93
94 Handle<OSROptimizedCodeCache> new_osr_cache =
95 Handle<OSROptimizedCodeCache>::cast(isolate->factory()->NewWeakFixedArray(
96 CapacityForLength(curr_valid_index), AllocationType::kOld));
97 DCHECK_LT(new_osr_cache->length(), osr_cache->length());
98 {
99 DisallowGarbageCollection no_gc;
100 new_osr_cache->CopyElements(isolate, 0, *osr_cache, 0,
101 new_osr_cache->length(),
102 new_osr_cache->GetWriteBarrierMode(no_gc));
103 }
104 native_context->set_osr_code_cache(*new_osr_cache);
105 }
106
TryGet(SharedFunctionInfo shared,BytecodeOffset osr_offset,Isolate * isolate)107 CodeT OSROptimizedCodeCache::TryGet(SharedFunctionInfo shared,
108 BytecodeOffset osr_offset,
109 Isolate* isolate) {
110 DisallowGarbageCollection no_gc;
111 int index = FindEntry(shared, osr_offset);
112 if (index == -1) return {};
113
114 CodeT code = GetCodeFromEntry(index);
115 if (code.is_null()) {
116 ClearEntry(index, isolate);
117 return {};
118 }
119
120 DCHECK(code.is_optimized_code() && !code.marked_for_deoptimization());
121 return code;
122 }
123
EvictDeoptimizedCode(Isolate * isolate)124 void OSROptimizedCodeCache::EvictDeoptimizedCode(Isolate* isolate) {
125 // This is called from DeoptimizeMarkedCodeForContext that uses raw pointers
126 // and hence the DisallowGarbageCollection scope here.
127 DisallowGarbageCollection no_gc;
128 for (int index = 0; index < length(); index += kEntryLength) {
129 MaybeObject code_entry = Get(index + kCachedCodeOffset);
130 HeapObject heap_object;
131 if (!code_entry->GetHeapObject(&heap_object)) continue;
132
133 CodeT code = CodeT::cast(heap_object);
134 DCHECK(code.is_optimized_code());
135 if (!code.marked_for_deoptimization()) continue;
136
137 ClearEntry(index, isolate);
138 }
139 }
140
OsrOffsetsFor(SharedFunctionInfo shared)141 std::vector<BytecodeOffset> OSROptimizedCodeCache::OsrOffsetsFor(
142 SharedFunctionInfo shared) {
143 DisallowGarbageCollection gc;
144
145 const OSRCodeCacheStateOfSFI state = shared.osr_code_cache_state();
146 if (state == kNotCached) return {};
147
148 std::vector<BytecodeOffset> offsets;
149 for (int index = 0; index < length(); index += kEntryLength) {
150 if (GetSFIFromEntry(index) != shared) continue;
151 offsets.emplace_back(GetBytecodeOffsetFromEntry(index));
152 if (state == kCachedOnce) return offsets;
153 }
154
155 return offsets;
156 }
157
FirstOsrOffsetFor(SharedFunctionInfo shared)158 base::Optional<BytecodeOffset> OSROptimizedCodeCache::FirstOsrOffsetFor(
159 SharedFunctionInfo shared) {
160 DisallowGarbageCollection gc;
161
162 const OSRCodeCacheStateOfSFI state = shared.osr_code_cache_state();
163 if (state == kNotCached) return {};
164
165 for (int index = 0; index < length(); index += kEntryLength) {
166 if (GetSFIFromEntry(index) != shared) continue;
167 return GetBytecodeOffsetFromEntry(index);
168 }
169
170 return {};
171 }
172
GrowOSRCache(Isolate * isolate,Handle<NativeContext> native_context,Handle<OSROptimizedCodeCache> * osr_cache)173 int OSROptimizedCodeCache::GrowOSRCache(
174 Isolate* isolate, Handle<NativeContext> native_context,
175 Handle<OSROptimizedCodeCache>* osr_cache) {
176 int old_length = (*osr_cache)->length();
177 int grow_by = CapacityForLength(old_length) - old_length;
178 DCHECK_GT(grow_by, kEntryLength);
179 *osr_cache = Handle<OSROptimizedCodeCache>::cast(
180 isolate->factory()->CopyWeakFixedArrayAndGrow(*osr_cache, grow_by));
181 for (int i = old_length; i < (*osr_cache)->length(); i++) {
182 (*osr_cache)->Set(i, HeapObjectReference::ClearedValue(isolate));
183 }
184 native_context->set_osr_code_cache(**osr_cache);
185
186 return old_length;
187 }
188
GetCodeFromEntry(int index)189 CodeT OSROptimizedCodeCache::GetCodeFromEntry(int index) {
190 DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
191 DCHECK_EQ(index % kEntryLength, 0);
192 HeapObject code_entry;
193 Get(index + OSRCodeCacheConstants::kCachedCodeOffset)
194 ->GetHeapObject(&code_entry);
195 if (code_entry.is_null()) return CodeT();
196 return CodeT::cast(code_entry);
197 }
198
GetSFIFromEntry(int index)199 SharedFunctionInfo OSROptimizedCodeCache::GetSFIFromEntry(int index) {
200 DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
201 DCHECK_EQ(index % kEntryLength, 0);
202 HeapObject sfi_entry;
203 Get(index + OSRCodeCacheConstants::kSharedOffset)->GetHeapObject(&sfi_entry);
204 return sfi_entry.is_null() ? SharedFunctionInfo()
205 : SharedFunctionInfo::cast(sfi_entry);
206 }
207
GetBytecodeOffsetFromEntry(int index)208 BytecodeOffset OSROptimizedCodeCache::GetBytecodeOffsetFromEntry(int index) {
209 DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
210 DCHECK_EQ(index % kEntryLength, 0);
211 Smi osr_offset_entry;
212 Get(index + kOsrIdOffset)->ToSmi(&osr_offset_entry);
213 return BytecodeOffset(osr_offset_entry.value());
214 }
215
FindEntry(SharedFunctionInfo shared,BytecodeOffset osr_offset)216 int OSROptimizedCodeCache::FindEntry(SharedFunctionInfo shared,
217 BytecodeOffset osr_offset) {
218 DisallowGarbageCollection no_gc;
219 DCHECK(!osr_offset.IsNone());
220 for (int index = 0; index < length(); index += kEntryLength) {
221 if (GetSFIFromEntry(index) != shared) continue;
222 if (GetBytecodeOffsetFromEntry(index) != osr_offset) continue;
223 return index;
224 }
225 return -1;
226 }
227
ClearEntry(int index,Isolate * isolate)228 void OSROptimizedCodeCache::ClearEntry(int index, Isolate* isolate) {
229 SharedFunctionInfo shared = GetSFIFromEntry(index);
230 DCHECK_GT(shared.osr_code_cache_state(), kNotCached);
231 if (V8_LIKELY(shared.osr_code_cache_state() == kCachedOnce)) {
232 shared.set_osr_code_cache_state(kNotCached);
233 } else if (shared.osr_code_cache_state() == kCachedMultiple) {
234 int osr_code_cache_count = 0;
235 for (int index = 0; index < length(); index += kEntryLength) {
236 if (GetSFIFromEntry(index) == shared) {
237 osr_code_cache_count++;
238 }
239 }
240 if (osr_code_cache_count == 2) {
241 shared.set_osr_code_cache_state(kCachedOnce);
242 }
243 }
244 HeapObjectReference cleared_value =
245 HeapObjectReference::ClearedValue(isolate);
246 Set(index + OSRCodeCacheConstants::kSharedOffset, cleared_value);
247 Set(index + OSRCodeCacheConstants::kCachedCodeOffset, cleared_value);
248 Set(index + OSRCodeCacheConstants::kOsrIdOffset, cleared_value);
249 }
250
InitializeEntry(int entry,SharedFunctionInfo shared,CodeT code,BytecodeOffset osr_offset)251 void OSROptimizedCodeCache::InitializeEntry(int entry,
252 SharedFunctionInfo shared,
253 CodeT code,
254 BytecodeOffset osr_offset) {
255 Set(entry + OSRCodeCacheConstants::kSharedOffset,
256 HeapObjectReference::Weak(shared));
257 HeapObjectReference weak_code_entry = HeapObjectReference::Weak(code);
258 Set(entry + OSRCodeCacheConstants::kCachedCodeOffset, weak_code_entry);
259 Set(entry + OSRCodeCacheConstants::kOsrIdOffset,
260 MaybeObject::FromSmi(Smi::FromInt(osr_offset.ToInt())));
261 if (V8_LIKELY(shared.osr_code_cache_state() == kNotCached)) {
262 shared.set_osr_code_cache_state(kCachedOnce);
263 } else if (shared.osr_code_cache_state() == kCachedOnce) {
264 shared.set_osr_code_cache_state(kCachedMultiple);
265 }
266 }
267
MoveEntry(int src,int dst,Isolate * isolate)268 void OSROptimizedCodeCache::MoveEntry(int src, int dst, Isolate* isolate) {
269 Set(dst + OSRCodeCacheConstants::kSharedOffset,
270 Get(src + OSRCodeCacheConstants::kSharedOffset));
271 Set(dst + OSRCodeCacheConstants::kCachedCodeOffset,
272 Get(src + OSRCodeCacheConstants::kCachedCodeOffset));
273 Set(dst + OSRCodeCacheConstants::kOsrIdOffset, Get(src + kOsrIdOffset));
274 HeapObjectReference cleared_value =
275 HeapObjectReference::ClearedValue(isolate);
276 Set(src + OSRCodeCacheConstants::kSharedOffset, cleared_value);
277 Set(src + OSRCodeCacheConstants::kCachedCodeOffset, cleared_value);
278 Set(src + OSRCodeCacheConstants::kOsrIdOffset, cleared_value);
279 }
280
CapacityForLength(int curr_length)281 int OSROptimizedCodeCache::CapacityForLength(int curr_length) {
282 // TODO(mythria): This is a randomly chosen heuristic and is not based on any
283 // data. We may have to tune this later.
284 if (curr_length == 0) return kInitialLength;
285 if (curr_length * 2 > kMaxLength) return kMaxLength;
286 return curr_length * 2;
287 }
288
NeedsTrimming(int num_valid_entries,int curr_length)289 bool OSROptimizedCodeCache::NeedsTrimming(int num_valid_entries,
290 int curr_length) {
291 return curr_length > kInitialLength && curr_length > num_valid_entries * 3;
292 }
293
RawGetForTesting(int index) const294 MaybeObject OSROptimizedCodeCache::RawGetForTesting(int index) const {
295 return WeakFixedArray::Get(index);
296 }
297
RawSetForTesting(int index,MaybeObject value)298 void OSROptimizedCodeCache::RawSetForTesting(int index, MaybeObject value) {
299 WeakFixedArray::Set(index, value);
300 }
301
302 } // namespace internal
303 } // namespace v8
304