summaryrefslogtreecommitdiff
path: root/chromium/v8/src/objects/compilation-cache-table.h
blob: fdd7fb57f0c1a28d7307a45535b46332270eb3ae (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
// Copyright 2017 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.

#ifndef V8_OBJECTS_COMPILATION_CACHE_TABLE_H_
#define V8_OBJECTS_COMPILATION_CACHE_TABLE_H_

#include "src/objects/feedback-cell.h"
#include "src/objects/hash-table.h"
#include "src/objects/js-regexp.h"
#include "src/objects/shared-function-info.h"
#include "src/roots/roots.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

struct ScriptDetails;

class CompilationCacheShape : public BaseShape<HashTableKey*> {
 public:
  static inline bool IsMatch(HashTableKey* key, Object value) {
    return key->IsMatch(value);
  }

  static inline uint32_t Hash(ReadOnlyRoots roots, HashTableKey* key) {
    return key->Hash();
  }

  static inline uint32_t RegExpHash(String string, Smi flags);

  static inline uint32_t EvalHash(String source, SharedFunctionInfo shared,
                                  LanguageMode language_mode, int position);

  static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);

  static const int kPrefixSize = 0;
  // An 'entry' is essentially a grouped collection of slots. Entries are used
  // in various ways by the different caches; most store the actual key in the
  // first entry slot, but it may also be used differently.
  // Why 3 slots? Because of the eval cache.
  static const int kEntrySize = 3;
  static const bool kMatchNeedsHoleCheck = true;
};

class InfoCellPair {
 public:
  InfoCellPair() = default;
  inline InfoCellPair(Isolate* isolate, SharedFunctionInfo shared,
                      FeedbackCell feedback_cell);

  FeedbackCell feedback_cell() const {
    DCHECK(is_compiled_scope_.is_compiled());
    return feedback_cell_;
  }
  SharedFunctionInfo shared() const {
    DCHECK(is_compiled_scope_.is_compiled());
    return shared_;
  }

  bool has_feedback_cell() const {
    return !feedback_cell_.is_null() && is_compiled_scope_.is_compiled();
  }
  bool has_shared() const {
    // Only return true if SFI is compiled - the bytecode could have been
    // flushed while it's in the compilation cache, and not yet have been
    // removed form the compilation cache.
    return !shared_.is_null() && is_compiled_scope_.is_compiled();
  }

 private:
  IsCompiledScope is_compiled_scope_;
  SharedFunctionInfo shared_;
  FeedbackCell feedback_cell_;
};

// A lookup result from the compilation cache for scripts. There are three
// possible states:
//
// 1. Cache miss: script and toplevel_sfi are both null.
// 2. Cache hit: script and toplevel_sfi are both non-null. toplevel_sfi is
//    guaranteed to be compiled, and to stay compiled while this lookup result
//    instance is alive.
// 3. Partial cache hit: script is non-null, but toplevel_sfi is null. The
//    script may contain an uncompiled toplevel SharedFunctionInfo.
class CompilationCacheScriptLookupResult {
 public:
  MaybeHandle<Script> script() const { return script_; }
  MaybeHandle<SharedFunctionInfo> toplevel_sfi() const { return toplevel_sfi_; }
  IsCompiledScope is_compiled_scope() const { return is_compiled_scope_; }

  using RawObjects = std::pair<Script, SharedFunctionInfo>;

  RawObjects GetRawObjects() const;

  static CompilationCacheScriptLookupResult FromRawObjects(RawObjects raw,
                                                           Isolate* isolate);

 private:
  MaybeHandle<Script> script_;
  MaybeHandle<SharedFunctionInfo> toplevel_sfi_;
  IsCompiledScope is_compiled_scope_;
};

EXTERN_DECLARE_HASH_TABLE(CompilationCacheTable, CompilationCacheShape)

class CompilationCacheTable
    : public HashTable<CompilationCacheTable, CompilationCacheShape> {
 public:
  NEVER_READ_ONLY_SPACE

  // The 'script' cache contains SharedFunctionInfos. Once a root
  // SharedFunctionInfo has become old enough that its bytecode is flushed, the
  // entry is still present and can be used to get the Script.
  static CompilationCacheScriptLookupResult LookupScript(
      Handle<CompilationCacheTable> table, Handle<String> src,
      const ScriptDetails& script_details, Isolate* isolate);
  static Handle<CompilationCacheTable> PutScript(
      Handle<CompilationCacheTable> cache, Handle<String> src,
      Handle<SharedFunctionInfo> value, Isolate* isolate);

  // Eval code only gets cached after a second probe for the
  // code object. To do so, on first "put" only a hash identifying the
  // source is entered into the cache, mapping it to a lifetime count of
  // the hash. On each call to Age all such lifetimes get reduced, and
  // removed once they reach zero. If a second put is called while such
  // a hash is live in the cache, the hash gets replaced by an actual
  // cache entry. Age also removes stale live entries from the cache.
  // Such entries are identified by SharedFunctionInfos pointing to
  // either the recompilation stub, or to "old" code. This avoids memory
  // leaks due to premature caching of eval strings that are
  // never needed later.
  static InfoCellPair LookupEval(Handle<CompilationCacheTable> table,
                                 Handle<String> src,
                                 Handle<SharedFunctionInfo> shared,
                                 Handle<Context> native_context,
                                 LanguageMode language_mode, int position);
  static Handle<CompilationCacheTable> PutEval(
      Handle<CompilationCacheTable> cache, Handle<String> src,
      Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value,
      Handle<Context> native_context, Handle<FeedbackCell> feedback_cell,
      int position);

  // The RegExp cache contains JSRegExp::data fixed arrays.
  Handle<Object> LookupRegExp(Handle<String> source, JSRegExp::Flags flags);
  static Handle<CompilationCacheTable> PutRegExp(
      Isolate* isolate, Handle<CompilationCacheTable> cache, Handle<String> src,
      JSRegExp::Flags flags, Handle<FixedArray> value);

  void Remove(Object value);
  void RemoveEntry(InternalIndex entry);

  inline Object PrimaryValueAt(InternalIndex entry);
  inline void SetPrimaryValueAt(InternalIndex entry, Object value,
                                WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
  inline Object EvalFeedbackValueAt(InternalIndex entry);
  inline void SetEvalFeedbackValueAt(
      InternalIndex entry, Object value,
      WriteBarrierMode mode = UPDATE_WRITE_BARRIER);

  // The initial placeholder insertion of the eval cache survives this many GCs.
  static constexpr int kHashGenerations = 10;

  DECL_CAST(CompilationCacheTable)

 private:
  static Handle<CompilationCacheTable> EnsureScriptTableCapacity(
      Isolate* isolate, Handle<CompilationCacheTable> cache);

  OBJECT_CONSTRUCTORS(CompilationCacheTable,
                      HashTable<CompilationCacheTable, CompilationCacheShape>);
};

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_COMPILATION_CACHE_TABLE_H_