1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef jit_BaselineIC_h
8 #define jit_BaselineIC_h
9
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12
13 #include <stddef.h>
14 #include <stdint.h>
15
16 #include "jit/ICState.h"
17 #include "jit/JitCode.h"
18 #include "jit/shared/Assembler-shared.h"
19 #include "jit/TypeData.h"
20 #include "js/TypeDecls.h"
21
22 class JS_PUBLIC_API JSTracer;
23
24 enum class JSOp : uint8_t;
25
26 namespace js {
27
28 MOZ_COLD void ReportOutOfMemory(JSContext* cx);
29
30 namespace jit {
31
32 class BaselineFrame;
33 class CacheIRStubInfo;
34 class ICScript;
35
36 enum class TailCallVMFunctionId;
37 enum class VMFunctionId;
38
39 // [SMDOC] JIT Inline Caches (ICs)
40 //
41 // Baseline Inline Caches are polymorphic caches that aggressively
42 // share their stub code.
43 //
44 // Every polymorphic site contains a linked list of stubs which are
45 // specific to that site. These stubs are composed of a |StubData|
46 // structure that stores parametrization information (e.g.
47 // the shape pointer for a shape-check-and-property-get stub), any
48 // dynamic information (e.g. warm-up counters), a pointer to the stub code,
49 // and a pointer to the next stub state in the linked list.
50 //
51 // Every BaselineScript keeps an table of |CacheDescriptor| data
52 // structures, which store the following:
53 // A pointer to the first StubData in the cache.
54 // The bytecode PC of the relevant IC.
55 // The machine-code PC where the call to the stubcode returns.
56 //
57 // A diagram:
58 //
59 // Control flow Pointers
60 // =======# ----. .---->
61 // # | |
62 // #======> \-----/
63 //
64 //
65 // .---------------------------------------.
66 // | .-------------------------. |
67 // | | .----. | |
68 // Baseline | | | | | |
69 // JIT Code 0 ^ 1 ^ 2 ^ | | |
70 // +--------------+ .-->+-----+ +-----+ +-----+ | | |
71 // | | #=|==>| |==>| |==>| FB | | | |
72 // | | # | +-----+ +-----+ +-----+ | | |
73 // | | # | # # # | | |
74 // |==============|==# | # # # | | |
75 // |=== IC =======| | # # # | | |
76 // .->|==============|<===|======#=========#=========# | | |
77 // | | | | | | |
78 // | | | | | | |
79 // | | | | | | |
80 // | | | | v | |
81 // | | | | +---------+ | |
82 // | | | | | Fallback| | |
83 // | | | | | Stub | | |
84 // | | | | | Code | | |
85 // | | | | +---------+ | |
86 // | +--------------+ | | |
87 // | |_______ | +---------+ | |
88 // | | | | Stub |<---/ |
89 // | IC | \--. | Code | |
90 // | Descriptor | | +---------+ |
91 // | Table v | |
92 // | +-----------------+ | +---------+ |
93 // \--| Ins | PC | Stub |----/ | Stub |<-------/
94 // +-----------------+ | Code |
95 // | ... | +---------+
96 // +-----------------+
97 // Shared
98 // Stub Code
99 //
100
101 class ICStub;
102 class ICCacheIRStub;
103 class ICFallbackStub;
104
105 #ifdef JS_JITSPEW
106 void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...)
107 MOZ_FORMAT_PRINTF(3, 4);
108 #else
109 # define FallbackICSpew(...)
110 #endif
111
112 // An entry in the ICScript IC table. There's one ICEntry per IC.
113 class ICEntry {
114 // A pointer to the first IC stub for this instruction.
115 ICStub* firstStub_;
116
117 public:
ICEntry(ICStub * firstStub)118 explicit ICEntry(ICStub* firstStub) : firstStub_(firstStub) {}
119
firstStub()120 ICStub* firstStub() const {
121 MOZ_ASSERT(firstStub_);
122 return firstStub_;
123 }
124
setFirstStub(ICStub * stub)125 void setFirstStub(ICStub* stub) { firstStub_ = stub; }
126
offsetOfFirstStub()127 static constexpr size_t offsetOfFirstStub() {
128 return offsetof(ICEntry, firstStub_);
129 }
130
131 void trace(JSTracer* trc);
132 };
133
134 //
135 // Base class for all IC stubs.
136 //
137 class ICStub {
138 friend class ICFallbackStub;
139
140 protected:
141 // The raw jitcode to call for this stub.
142 uint8_t* stubCode_;
143
144 // Counts the number of times the stub was entered
145 //
146 // See Bug 1494473 comment 6 for a mechanism to handle overflow if overflow
147 // becomes a concern.
148 uint32_t enteredCount_ = 0;
149
150 // Tracks input types for some CacheIR stubs, to help optimize
151 // polymorphic cases. Stored in the base class to make use of
152 // padding bytes.
153 TypeData typeData_;
154
155 // Whether this is an ICFallbackStub or an ICCacheIRStub.
156 bool isFallback_;
157
ICStub(uint8_t * stubCode,bool isFallback)158 ICStub(uint8_t* stubCode, bool isFallback)
159 : stubCode_(stubCode), isFallback_(isFallback) {
160 MOZ_ASSERT(stubCode != nullptr);
161 }
162
163 public:
isFallback()164 inline bool isFallback() const { return isFallback_; }
165
166 inline ICStub* maybeNext() const;
167
toFallbackStub()168 inline const ICFallbackStub* toFallbackStub() const {
169 MOZ_ASSERT(isFallback());
170 return reinterpret_cast<const ICFallbackStub*>(this);
171 }
172
toFallbackStub()173 inline ICFallbackStub* toFallbackStub() {
174 MOZ_ASSERT(isFallback());
175 return reinterpret_cast<ICFallbackStub*>(this);
176 }
177
toCacheIRStub()178 ICCacheIRStub* toCacheIRStub() {
179 MOZ_ASSERT(!isFallback());
180 return reinterpret_cast<ICCacheIRStub*>(this);
181 }
toCacheIRStub()182 const ICCacheIRStub* toCacheIRStub() const {
183 MOZ_ASSERT(!isFallback());
184 return reinterpret_cast<const ICCacheIRStub*>(this);
185 }
186
usesTrampolineCode()187 bool usesTrampolineCode() const {
188 // All fallback code is stored in a single JitCode instance, so we can't
189 // call JitCode::FromExecutable on the raw pointer.
190 return isFallback();
191 }
jitCode()192 JitCode* jitCode() {
193 MOZ_ASSERT(!usesTrampolineCode());
194 return JitCode::FromExecutable(stubCode_);
195 }
196
enteredCount()197 uint32_t enteredCount() const { return enteredCount_; }
incrementEnteredCount()198 inline void incrementEnteredCount() { enteredCount_++; }
resetEnteredCount()199 void resetEnteredCount() { enteredCount_ = 0; }
200
offsetOfStubCode()201 static constexpr size_t offsetOfStubCode() {
202 return offsetof(ICStub, stubCode_);
203 }
offsetOfEnteredCount()204 static constexpr size_t offsetOfEnteredCount() {
205 return offsetof(ICStub, enteredCount_);
206 }
207 };
208
209 class ICFallbackStub final : public ICStub {
210 friend class ICStubConstIterator;
211
212 protected:
213 // The PC offset of this IC's bytecode op within the JSScript.
214 uint32_t pcOffset_;
215
216 // The state of this IC.
217 ICState state_{};
218
219 public:
ICFallbackStub(uint32_t pcOffset,TrampolinePtr stubCode)220 explicit ICFallbackStub(uint32_t pcOffset, TrampolinePtr stubCode)
221 : ICStub(stubCode.value, /* isFallback = */ true), pcOffset_(pcOffset) {}
222
numOptimizedStubs()223 inline size_t numOptimizedStubs() const { return state_.numOptimizedStubs(); }
224
newStubIsFirstStub()225 bool newStubIsFirstStub() const { return state_.newStubIsFirstStub(); }
226
state()227 ICState& state() { return state_; }
228
pcOffset()229 uint32_t pcOffset() const { return pcOffset_; }
230
231 // Add a new stub to the IC chain terminated by this fallback stub.
232 inline void addNewStub(ICEntry* icEntry, ICCacheIRStub* stub);
233
234 void discardStubs(JSContext* cx, ICEntry* icEntry);
235
clearUsedByTranspiler()236 void clearUsedByTranspiler() { state_.clearUsedByTranspiler(); }
setUsedByTranspiler()237 void setUsedByTranspiler() { state_.setUsedByTranspiler(); }
238
trialInliningState()239 TrialInliningState trialInliningState() const {
240 return state_.trialInliningState();
241 }
setTrialInliningState(TrialInliningState state)242 void setTrialInliningState(TrialInliningState state) {
243 state_.setTrialInliningState(state);
244 }
245
246 void trackNotAttached();
247
248 void unlinkStub(Zone* zone, ICEntry* icEntry, ICCacheIRStub* prev,
249 ICCacheIRStub* stub);
250 };
251
252 class ICCacheIRStub final : public ICStub {
253 // Pointer to next IC stub.
254 ICStub* next_ = nullptr;
255
256 const CacheIRStubInfo* stubInfo_;
257
258 #ifndef JS_64BIT
259 // Ensure stub data is 8-byte aligned on 32-bit.
260 uintptr_t padding_ = 0;
261 #endif
262
263 public:
ICCacheIRStub(JitCode * stubCode,const CacheIRStubInfo * stubInfo)264 ICCacheIRStub(JitCode* stubCode, const CacheIRStubInfo* stubInfo)
265 : ICStub(stubCode->raw(), /* isFallback = */ false),
266 stubInfo_(stubInfo) {}
267
next()268 ICStub* next() const { return next_; }
setNext(ICStub * stub)269 void setNext(ICStub* stub) { next_ = stub; }
270
stubInfo()271 const CacheIRStubInfo* stubInfo() const { return stubInfo_; }
272 uint8_t* stubDataStart();
273
274 void trace(JSTracer* trc);
275
276 // Optimized stubs get purged on GC. But some stubs can be active on the
277 // stack during GC - specifically the ones that can make calls. To ensure
278 // that these do not get purged, all stubs that can make calls are allocated
279 // in the fallback stub space.
280 bool makesGCCalls() const;
allocatedInFallbackSpace()281 bool allocatedInFallbackSpace() const { return makesGCCalls(); }
282
offsetOfNext()283 static constexpr size_t offsetOfNext() {
284 return offsetof(ICCacheIRStub, next_);
285 }
286
setTypeData(TypeData data)287 void setTypeData(TypeData data) { typeData_ = data; }
typeData()288 TypeData typeData() const { return typeData_; }
289 };
290
291 // Assert stub size is what we expect to catch regressions.
292 #ifdef JS_64BIT
293 static_assert(sizeof(ICFallbackStub) == 3 * sizeof(uintptr_t));
294 static_assert(sizeof(ICCacheIRStub) == 4 * sizeof(uintptr_t));
295 #else
296 static_assert(sizeof(ICFallbackStub) == 5 * sizeof(uintptr_t));
297 static_assert(sizeof(ICCacheIRStub) == 6 * sizeof(uintptr_t));
298 #endif
299
maybeNext()300 inline ICStub* ICStub::maybeNext() const {
301 return isFallback() ? nullptr : toCacheIRStub()->next();
302 }
303
addNewStub(ICEntry * icEntry,ICCacheIRStub * stub)304 inline void ICFallbackStub::addNewStub(ICEntry* icEntry, ICCacheIRStub* stub) {
305 MOZ_ASSERT(stub->next() == nullptr);
306 stub->setNext(icEntry->firstStub());
307 icEntry->setFirstStub(stub);
308 state_.trackAttached();
309 }
310
311 AllocatableGeneralRegisterSet BaselineICAvailableGeneralRegs(size_t numInputs);
312
313 bool ICSupportsPolymorphicTypeData(JSOp op);
314
315 struct IonOsrTempData;
316
317 extern bool DoCallFallback(JSContext* cx, BaselineFrame* frame,
318 ICFallbackStub* stub, uint32_t argc, Value* vp,
319 MutableHandleValue res);
320
321 extern bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame,
322 ICFallbackStub* stub, Value* vp,
323 MutableHandleValue res);
324
325 extern bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame,
326 ICFallbackStub* stub, HandleValue arg,
327 MutableHandleValue ret);
328
329 extern bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame,
330 ICFallbackStub* stub, HandleValue lhs,
331 HandleValue rhs, HandleValue receiver,
332 MutableHandleValue res);
333
334 extern bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
335 ICFallbackStub* stub, HandleValue lhs,
336 HandleValue rhs, MutableHandleValue res);
337
338 extern bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame,
339 ICFallbackStub* stub, Value* stack,
340 HandleValue objv, HandleValue index,
341 HandleValue rhs);
342
343 extern bool DoInFallback(JSContext* cx, BaselineFrame* frame,
344 ICFallbackStub* stub, HandleValue key,
345 HandleValue objValue, MutableHandleValue res);
346
347 extern bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame,
348 ICFallbackStub* stub, HandleValue keyValue,
349 HandleValue objValue, MutableHandleValue res);
350
351 extern bool DoCheckPrivateFieldFallback(JSContext* cx, BaselineFrame* frame,
352 ICFallbackStub* stub,
353 HandleValue objValue,
354 HandleValue keyValue,
355 MutableHandleValue res);
356
357 extern bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame,
358 ICFallbackStub* stub, HandleObject envChain,
359 MutableHandleValue res);
360
361 extern bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame,
362 ICFallbackStub* stub, HandleObject envChain,
363 MutableHandleValue res);
364
365 extern bool DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame,
366 ICFallbackStub* stub,
367 MutableHandleValue res);
368
369 extern bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame,
370 ICFallbackStub* stub, MutableHandleValue val,
371 MutableHandleValue res);
372
373 extern bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame,
374 ICFallbackStub* stub, HandleValue receiver,
375 MutableHandleValue val,
376 MutableHandleValue res);
377
378 extern bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
379 ICFallbackStub* stub, Value* stack,
380 HandleValue lhs, HandleValue rhs);
381
382 extern bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame,
383 ICFallbackStub* stub, HandleValue value,
384 MutableHandleValue res);
385
386 extern bool DoOptimizeSpreadCallFallback(JSContext* cx, BaselineFrame* frame,
387 ICFallbackStub* stub,
388 HandleValue value,
389 MutableHandleValue res);
390
391 extern bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame,
392 ICFallbackStub* stub, HandleValue lhs,
393 HandleValue rhs, MutableHandleValue res);
394
395 extern bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame,
396 ICFallbackStub* stub, HandleValue val,
397 MutableHandleValue res);
398
399 extern bool DoToPropertyKeyFallback(JSContext* cx, BaselineFrame* frame,
400 ICFallbackStub* stub, HandleValue val,
401 MutableHandleValue res);
402
403 extern bool DoRestFallback(JSContext* cx, BaselineFrame* frame,
404 ICFallbackStub* stub, MutableHandleValue res);
405
406 extern bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame,
407 ICFallbackStub* stub, HandleValue val,
408 MutableHandleValue res);
409
410 extern bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame,
411 ICFallbackStub* stub, HandleValue lhs,
412 HandleValue rhs, MutableHandleValue ret);
413
414 extern bool DoNewArrayFallback(JSContext* cx, BaselineFrame* frame,
415 ICFallbackStub* stub, MutableHandleValue res);
416
417 extern bool DoNewObjectFallback(JSContext* cx, BaselineFrame* frame,
418 ICFallbackStub* stub, MutableHandleValue res);
419
420 extern bool DoCompareFallback(JSContext* cx, BaselineFrame* frame,
421 ICFallbackStub* stub, HandleValue lhs,
422 HandleValue rhs, MutableHandleValue ret);
423
424 } // namespace jit
425 } // namespace js
426
427 #endif /* jit_BaselineIC_h */
428