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