• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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