1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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_IonCaches_h
8 #define jit_IonCaches_h
9 
10 #if defined(JS_CODEGEN_ARM)
11 # include "jit/arm/Assembler-arm.h"
12 #elif defined(JS_CODEGEN_ARM64)
13 # include "jit/arm64/Assembler-arm64.h"
14 #elif defined(JS_CODEGEN_MIPS32)
15 # include "jit/mips32/Assembler-mips32.h"
16 #elif defined(JS_CODEGEN_MIPS64)
17 # include "jit/mips64/Assembler-mips64.h"
18 #endif
19 #include "jit/JitCompartment.h"
20 #include "jit/Registers.h"
21 #include "jit/shared/Assembler-shared.h"
22 #include "js/TrackedOptimizationInfo.h"
23 
24 #include "vm/TypedArrayCommon.h"
25 
26 namespace js {
27 namespace jit {
28 
29 class LInstruction;
30 
31 #define IONCACHE_KIND_LIST(_)                                   \
32     _(GetProperty)                                              \
33     _(SetProperty)                                              \
34     _(BindName)                                                 \
35     _(Name)
36 
37 // Forward declarations of Cache kinds.
38 #define FORWARD_DECLARE(kind) class kind##IC;
IONCACHE_KIND_LIST(FORWARD_DECLARE)39 IONCACHE_KIND_LIST(FORWARD_DECLARE)
40 #undef FORWARD_DECLARE
41 
42 class IonCacheVisitor
43 {
44   public:
45 #define VISIT_INS(op)                                               \
46     virtual void visit##op##IC(CodeGenerator* codegen) {            \
47         MOZ_CRASH("NYI: " #op "IC");                                \
48     }
49 
50     IONCACHE_KIND_LIST(VISIT_INS)
51 #undef VISIT_INS
52 };
53 
54 // Common structure encoding the state of a polymorphic inline cache contained
55 // in the code for an IonScript. IonCaches are used for polymorphic operations
56 // where multiple implementations may be required.
57 //
58 // Roughly speaking, the cache initially jumps to an out of line fragment
59 // which invokes a cache function to perform the operation. The cache function
60 // may generate a stub to perform the operation in certain cases (e.g. a
61 // particular shape for an input object) and attach the stub to existing
62 // stubs, forming a daisy chain of tests for how to perform the operation in
63 // different circumstances.
64 //
65 // Eventually, if too many stubs are generated the cache function may disable
66 // the cache, by generating a stub to make a call and perform the operation
67 // within the VM.
68 //
69 // The caches initially generate a patchable jump to an out of line call
70 // to the cache function. Stubs are attached by appending: when attaching a
71 // new stub, we patch the any failure conditions in last generated stub to
72 // jump to the new stub. Failure conditions in the new stub jump to the cache
73 // function which may generate new stubs.
74 //
75 //        Control flow               Pointers
76 //      =======#                 ----.     .---->
77 //             #                     |     |
78 //             #======>              \-----/
79 //
80 // Initial state:
81 //
82 //  JIT Code
83 // +--------+   .---------------.
84 // |        |   |               |
85 // |========|   v +----------+  |
86 // |== IC ==|====>| Cache Fn |  |
87 // |========|     +----------+  |
88 // |        |<=#       #        |
89 // |        |  #=======#        |
90 // +--------+  Rejoin path      |
91 //     |________                |
92 //             |                |
93 //     IC      |                |
94 //   Entry     |                |
95 // +------------+               |
96 // | lastJump_  |---------------/
97 // +------------+
98 // |    ...     |
99 // +------------+
100 //
101 // Attaching stubs:
102 //
103 //   Patch the jump pointed to by lastJump_ to jump to the new stub. Update
104 //   lastJump_ to be the new stub's failure jump. The failure jump of the new
105 //   stub goes to the fallback label, which is the cache function. In this
106 //   fashion, new stubs are _appended_ to the chain of stubs, as lastJump_
107 //   points to the _tail_ of the stub chain.
108 //
109 //  JIT Code
110 // +--------+ #=======================#
111 // |        | #                       v
112 // |========| #   +----------+     +------+
113 // |== IC ==|=#   | Cache Fn |<====| Stub |
114 // |========|     +----------+  ^  +------+
115 // |        |<=#      #         |     #
116 // |        |  #======#=========|=====#
117 // +--------+      Rejoin path  |
118 //     |________                |
119 //             |                |
120 //     IC      |                |
121 //   Entry     |                |
122 // +------------+               |
123 // | lastJump_  |---------------/
124 // +------------+
125 // |    ...     |
126 // +------------+
127 //
128 // While calls may be made to the cache function and other VM functions, the
129 // cache may still be treated as pure during optimization passes, such that
130 // LICM and GVN may be performed on operations around the cache as if the
131 // operation cannot reenter scripted code through an Invoke() or otherwise have
132 // unexpected behavior. This restricts the sorts of stubs which the cache can
133 // generate or the behaviors which called functions can have, and if a called
134 // function performs a possibly impure operation then the operation will be
135 // marked as such and the calling script will be recompiled.
136 //
137 // Similarly, despite the presence of functions and multiple stubs generated
138 // for a cache, the cache itself may be marked as idempotent and become hoisted
139 // or coalesced by LICM or GVN. This also constrains the stubs which can be
140 // generated for the cache.
141 //
142 // * IonCache usage
143 //
144 // IonCache is the base structure of an inline cache, which generates code stubs
145 // dynamically and attaches them to an IonScript.
146 //
147 // A cache must at least provide a static update function which will usualy have
148 // a JSContext*, followed by the cache index. The rest of the arguments of the
149 // update function are usualy corresponding to the register inputs of the cache,
150 // as it must perform the same operation as any of the stubs that it might
151 // produce. The update function call is handled by the visit function of
152 // CodeGenerator corresponding to this IC.
153 //
154 // The CodeGenerator visit function, as opposed to other visit functions, has
155 // two arguments. The first one is the OutOfLineUpdateCache which stores the LIR
156 // instruction. The second one is the IC object.  This function would be called
157 // once the IC is registered with the addCache function of CodeGeneratorShared.
158 //
159 // To register a cache, you must call the addCache function as follow:
160 //
161 //     MyCodeIC cache(inputReg1, inputValueReg2, outputReg);
162 //     if (!addCache(lir, allocateCache(cache)))
163 //         return false;
164 //
165 // Once the cache is allocated with the allocateCache function, any modification
166 // made to the cache would be ignored.
167 //
168 // The addCache function will produce a patchable jump at the location where
169 // it is called. This jump will execute generated stubs and fallback on the code
170 // of the visitMyCodeIC function if no stub match.
171 //
172 //   Warning: As the addCache function fallback on a VMCall, calls to
173 // addCache should not be in the same path as another VMCall or in the same
174 // path of another addCache as this is not supported by the invalidation
175 // procedure.
176 class IonCache
177 {
178   public:
179     class StubAttacher;
180 
181     enum Kind {
182 #   define DEFINE_CACHEKINDS(ickind) Cache_##ickind,
183         IONCACHE_KIND_LIST(DEFINE_CACHEKINDS)
184 #   undef DEFINE_CACHEKINDS
185         Cache_Invalid
186     };
187 
188     // Cache testing and cast.
189 #   define CACHEKIND_CASTS(ickind)                                      \
190     bool is##ickind() const {                                           \
191         return kind() == Cache_##ickind;                                \
192     }                                                                   \
193     inline ickind##IC& to##ickind();                                    \
194     inline const ickind##IC& to##ickind() const;
195     IONCACHE_KIND_LIST(CACHEKIND_CASTS)
196 #   undef CACHEKIND_CASTS
197 
198     virtual Kind kind() const = 0;
199 
200     virtual void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) = 0;
201 
202   public:
203 
204     static const char* CacheName(Kind kind);
205 
206   protected:
207     bool pure_ : 1;
208     bool idempotent_ : 1;
209     bool disabled_ : 1;
210     size_t stubCount_ : 5;
211 
212     CodeLocationLabel fallbackLabel_;
213 
214     // Location of this operation, nullptr for idempotent caches.
215     JSScript* script_;
216     jsbytecode* pc_;
217 
218     // Location to use when updating profiler pseudostack when leaving this
219     // IC code to enter a callee.
220     jsbytecode* profilerLeavePc_;
221 
222     CodeLocationJump initialJump_;
223     CodeLocationJump lastJump_;
224     CodeLocationLabel rejoinLabel_;
225 
226   private:
227     static const size_t MAX_STUBS;
incrementStubCount()228     void incrementStubCount() {
229         // The IC should stop generating stubs before wrapping stubCount.
230         stubCount_++;
231         MOZ_ASSERT(stubCount_);
232     }
233 
234   public:
235 
IonCache()236     IonCache()
237       : pure_(false),
238         idempotent_(false),
239         disabled_(false),
240         stubCount_(0),
241         fallbackLabel_(),
242         script_(nullptr),
243         pc_(nullptr),
244         profilerLeavePc_(nullptr),
245         initialJump_(),
246         lastJump_(),
247         rejoinLabel_()
248     {
249     }
250 
251     void disable();
isDisabled()252     inline bool isDisabled() const {
253         return disabled_;
254     }
255 
256     // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is
257     // the location of the out-of-line update (slow) path.  This location will
258     // be set to the exitJump of the last generated stub.
setFallbackLabel(CodeOffset fallbackLabel)259     void setFallbackLabel(CodeOffset fallbackLabel) {
260         fallbackLabel_ = fallbackLabel;
261     }
262 
setProfilerLeavePC(jsbytecode * pc)263     void setProfilerLeavePC(jsbytecode* pc) {
264         MOZ_ASSERT(pc != nullptr);
265         profilerLeavePc_ = pc;
266     }
267 
268     // Get the address at which IC rejoins the mainline jitcode.
rejoinAddress()269     void* rejoinAddress() const {
270         return rejoinLabel_.raw();
271     }
272 
273     void emitInitialJump(MacroAssembler& masm, RepatchLabel& entry);
274     void updateBaseAddress(JitCode* code, MacroAssembler& masm);
275 
276     // Reset the cache around garbage collection.
277     virtual void reset(ReprotectCode reprotect);
278 
canAttachStub()279     bool canAttachStub() const {
280         return stubCount_ < MAX_STUBS;
281     }
empty()282     bool empty() const {
283         return stubCount_ == 0;
284     }
285 
286     enum LinkStatus {
287         LINK_ERROR,
288         CACHE_FLUSHED,
289         LINK_GOOD
290     };
291 
292     // Use the Linker to link the generated code and check if any
293     // monitoring/allocation caused an invalidation of the running ion script,
294     // this function returns CACHE_FLUSHED. In case of allocation issue this
295     // function returns LINK_ERROR.
296     LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion,
297                         JitCode** code);
298 
299     // Fixup variables and update jumps in the list of stubs.  Increment the
300     // number of attached stubs accordingly.
301     void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
302                     Handle<JitCode*> code);
303 
304     // Combine both linkStub and attachStub into one function. In addition, it
305     // produces a spew augmented with the attachKind string.
306     MOZ_MUST_USE bool linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher,
307                                         IonScript* ion, const char* attachKind,
308                                         JS::TrackedOutcome = JS::TrackedOutcome::ICOptStub_GenericSuccess);
309 
310 #ifdef DEBUG
isAllocated()311     bool isAllocated() {
312         return fallbackLabel_.isSet();
313     }
314 #endif
315 
pure()316     bool pure() const {
317         return pure_;
318     }
idempotent()319     bool idempotent() const {
320         return idempotent_;
321     }
setIdempotent()322     void setIdempotent() {
323         MOZ_ASSERT(!idempotent_);
324         MOZ_ASSERT(!script_);
325         MOZ_ASSERT(!pc_);
326         idempotent_ = true;
327     }
328 
setScriptedLocation(JSScript * script,jsbytecode * pc)329     void setScriptedLocation(JSScript* script, jsbytecode* pc) {
330         MOZ_ASSERT(!idempotent_);
331         script_ = script;
332         pc_ = pc;
333     }
334 
getScriptedLocation(MutableHandleScript pscript,jsbytecode ** ppc)335     void getScriptedLocation(MutableHandleScript pscript, jsbytecode** ppc) const {
336         pscript.set(script_);
337         *ppc = pc_;
338     }
339 
pc()340     jsbytecode* pc() const {
341         MOZ_ASSERT(pc_);
342         return pc_;
343     }
344 
345     void trace(JSTracer* trc);
346 };
347 
348 // Define the cache kind and pre-declare data structures used for calling inline
349 // caches.
350 #define CACHE_HEADER(ickind)                                        \
351     Kind kind() const {                                             \
352         return IonCache::Cache_##ickind;                            \
353     }                                                               \
354                                                                     \
355     void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) { \
356         visitor->visit##ickind##IC(codegen);                        \
357     }                                                               \
358                                                                     \
359     static const VMFunction UpdateInfo;
360 
361 // Subclasses of IonCache for the various kinds of caches. These do not define
362 // new data members; all caches must be of the same size.
363 
364 // Helper for idempotent GetPropertyIC location tracking. Declared externally
365 // to be forward declarable.
366 //
367 // Since all the scripts stored in CacheLocations are guaranteed to have been
368 // Ion compiled, and are kept alive by function objects in jitcode, and since
369 // the CacheLocations only have the lifespan of the jitcode, there is no need
370 // to trace or mark any of the scripts. Since JSScripts are always allocated
371 // tenured, and never moved, we can keep raw pointers, and there is no need
372 // for GCPtrScripts here.
373 struct CacheLocation {
374     jsbytecode* pc;
375     JSScript* script;
376 
CacheLocationCacheLocation377     CacheLocation(jsbytecode* pcin, JSScript* scriptin)
378         : pc(pcin), script(scriptin)
379     { }
380 };
381 
382 class GetPropertyIC : public IonCache
383 {
384   protected:
385     // Registers live after the cache, excluding output registers. The initial
386     // value of these registers must be preserved by the cache.
387     LiveRegisterSet liveRegs_;
388 
389     Register object_;
390     ConstantOrRegister id_;
391     TypedOrValueRegister output_;
392 
393     // Only valid if idempotent
394     size_t locationsIndex_;
395     size_t numLocations_;
396 
397     static const size_t MAX_FAILED_UPDATES = 16;
398     uint16_t failedUpdates_;
399 
400     bool monitoredResult_ : 1;
401     bool allowDoubleResult_ : 1;
402     bool hasTypedArrayLengthStub_ : 1;
403     bool hasMappedArgumentsLengthStub_ : 1;
404     bool hasUnmappedArgumentsLengthStub_ : 1;
405     bool hasMappedArgumentsElementStub_ : 1;
406     bool hasUnmappedArgumentsElementStub_ : 1;
407     bool hasGenericProxyStub_ : 1;
408     bool hasDenseStub_ : 1;
409 
410     void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
411 
412   public:
GetPropertyIC(LiveRegisterSet liveRegs,Register object,const ConstantOrRegister & id,TypedOrValueRegister output,bool monitoredResult,bool allowDoubleResult)413     GetPropertyIC(LiveRegisterSet liveRegs,
414                   Register object, const ConstantOrRegister& id,
415                   TypedOrValueRegister output,
416                   bool monitoredResult, bool allowDoubleResult)
417       : liveRegs_(liveRegs),
418         object_(object),
419         id_(id),
420         output_(output),
421         locationsIndex_(0),
422         numLocations_(0),
423         failedUpdates_(0),
424         monitoredResult_(monitoredResult),
425         allowDoubleResult_(allowDoubleResult),
426         hasTypedArrayLengthStub_(false),
427         hasMappedArgumentsLengthStub_(false),
428         hasUnmappedArgumentsLengthStub_(false),
429         hasMappedArgumentsElementStub_(false),
430         hasUnmappedArgumentsElementStub_(false),
431         hasGenericProxyStub_(false),
432         hasDenseStub_(false)
433     {
434     }
435 
436     CACHE_HEADER(GetProperty)
437 
438     void reset(ReprotectCode reprotect);
439 
object()440     Register object() const {
441         return object_;
442     }
id()443     ConstantOrRegister id() const {
444         return id_;
445     }
output()446     TypedOrValueRegister output() const {
447         return output_;
448     }
monitoredResult()449     bool monitoredResult() const {
450         return monitoredResult_;
451     }
hasTypedArrayLengthStub(HandleObject obj)452     bool hasTypedArrayLengthStub(HandleObject obj) const {
453         return hasTypedArrayLengthStub_;
454     }
hasArgumentsLengthStub(bool mapped)455     bool hasArgumentsLengthStub(bool mapped) const {
456         return mapped ? hasMappedArgumentsLengthStub_ : hasUnmappedArgumentsLengthStub_;
457     }
hasArgumentsElementStub(bool mapped)458     bool hasArgumentsElementStub(bool mapped) const {
459         return mapped ? hasMappedArgumentsElementStub_ : hasUnmappedArgumentsElementStub_;
460     }
hasGenericProxyStub()461     bool hasGenericProxyStub() const {
462         return hasGenericProxyStub_;
463     }
464 
hasDenseStub()465     bool hasDenseStub() const {
466         return hasDenseStub_;
467     }
setHasDenseStub()468     void setHasDenseStub() {
469         MOZ_ASSERT(!hasDenseStub());
470         hasDenseStub_ = true;
471     }
472 
setHasTypedArrayLengthStub(HandleObject obj)473     void setHasTypedArrayLengthStub(HandleObject obj) {
474         MOZ_ASSERT(obj->is<TypedArrayObject>());
475         MOZ_ASSERT(!hasTypedArrayLengthStub_);
476         hasTypedArrayLengthStub_ = true;
477     }
478 
setLocationInfo(size_t locationsIndex,size_t numLocations)479     void setLocationInfo(size_t locationsIndex, size_t numLocations) {
480         MOZ_ASSERT(idempotent());
481         MOZ_ASSERT(!numLocations_);
482         MOZ_ASSERT(numLocations);
483         locationsIndex_ = locationsIndex;
484         numLocations_ = numLocations;
485     }
getLocationInfo(uint32_t * index,uint32_t * num)486     void getLocationInfo(uint32_t* index, uint32_t* num) const {
487         MOZ_ASSERT(idempotent());
488         *index = locationsIndex_;
489         *num = numLocations_;
490     }
491 
492     enum NativeGetPropCacheability {
493         CanAttachNone,
494         CanAttachReadSlot,
495         CanAttachArrayLength,
496         CanAttachCallGetter
497     };
498 
499     // Helpers for CanAttachNativeGetProp
500     bool allowArrayLength(JSContext* cx) const;
allowGetters()501     bool allowGetters() const {
502         return monitoredResult() && !idempotent();
503     }
504 
505     void maybeDisable(bool emitted);
506 
507     // Attach the proper stub, if possible
508     MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
509                                     HandleObject obj, HandleValue idval, bool* emitted);
510 
511     MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
512                                      HandleObject obj, HandleId id, void* returnAddr,
513                                      bool* emitted);
514 
515     MOZ_MUST_USE bool tryAttachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
516                                             HandleObject obj, HandleId id, void* returnAddr,
517                                             bool* emitted);
518 
519     MOZ_MUST_USE bool tryAttachDOMProxyShadowed(JSContext* cx, HandleScript outerScript,
520                                                 IonScript* ion, HandleObject obj, HandleId id,
521                                                 void* returnAddr, bool* emitted);
522 
523     MOZ_MUST_USE bool tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript,
524                                                   IonScript* ion, HandleObject obj, HandleId id,
525                                                   bool resetNeeded, void* returnAddr,
526                                                   bool* emitted);
527 
528     MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
529                                       HandleObject obj, HandleId id, void* returnAddr,
530                                       bool* emitted);
531 
532     MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
533                                        HandleObject obj, HandleId id, void* returnAddr,
534                                        bool* emitted);
535 
536     MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
537                                               IonScript* ion, HandleObject obj, HandleId id,
538                                               void* returnAddr, bool* emitted);
539 
540     MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript,
541                                                   IonScript* ion, HandleObject obj, HandleId id,
542                                                   void* returnAddr, bool* emitted);
543 
544     MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript,
545                                                 IonScript* ion, HandleObject obj, HandleId id,
546                                                 bool* emitted);
547 
548     MOZ_MUST_USE bool tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript,
549                                                IonScript* ion, HandleObject obj, HandleId id,
550                                                bool* emitted);
551 
552     MOZ_MUST_USE bool tryAttachArgumentsElement(JSContext* cx, HandleScript outerScript,
553                                                 IonScript* ion, HandleObject obj, HandleValue idval,
554                                                 bool* emitted);
555 
556     MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
557                                             HandleObject obj, HandleValue idval, bool* emitted);
558 
559     static bool canAttachDenseElementHole(JSObject* obj, HandleValue idval,
560                                           TypedOrValueRegister output);
561     MOZ_MUST_USE bool tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript,
562                                                 IonScript* ion, HandleObject obj,
563                                                 HandleValue idval, bool* emitted);
564 
565     static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
566                                                     TypedOrValueRegister output);
567 
568     MOZ_MUST_USE bool tryAttachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript,
569                                                           IonScript* ion, HandleObject obj,
570                                                           HandleValue idval, bool* emitted);
571 
572     MOZ_MUST_USE bool tryAttachModuleNamespace(JSContext* cx, HandleScript outerScript,
573                                                IonScript* ion, HandleObject obj, HandleId id,
574                                                void* returnAddr, bool* emitted);
575 
576     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
577                                     HandleObject obj, HandleValue id, MutableHandleValue vp);
578 };
579 
580 class SetPropertyIC : public IonCache
581 {
582   protected:
583     // Registers live after the cache, excluding output registers. The initial
584     // value of these registers must be preserved by the cache.
585     LiveRegisterSet liveRegs_;
586 
587     Register object_;
588     Register temp_;
589     Register tempToUnboxIndex_;
590     FloatRegister tempDouble_;
591     FloatRegister tempFloat32_;
592     ConstantOrRegister id_;
593     ConstantOrRegister value_;
594     bool strict_ : 1;
595     bool needsTypeBarrier_ : 1;
596     bool guardHoles_ : 1;
597 
598     bool hasGenericProxyStub_ : 1;
599     bool hasDenseStub_ : 1;
600 
601     void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
602 
603   public:
SetPropertyIC(LiveRegisterSet liveRegs,Register object,Register temp,Register tempToUnboxIndex,FloatRegister tempDouble,FloatRegister tempFloat32,const ConstantOrRegister & id,const ConstantOrRegister & value,bool strict,bool needsTypeBarrier,bool guardHoles)604     SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, Register tempToUnboxIndex,
605                   FloatRegister tempDouble, FloatRegister tempFloat32,
606                   const ConstantOrRegister& id, const ConstantOrRegister& value,
607                   bool strict, bool needsTypeBarrier, bool guardHoles)
608       : liveRegs_(liveRegs),
609         object_(object),
610         temp_(temp),
611         tempToUnboxIndex_(tempToUnboxIndex),
612         tempDouble_(tempDouble),
613         tempFloat32_(tempFloat32),
614         id_(id),
615         value_(value),
616         strict_(strict),
617         needsTypeBarrier_(needsTypeBarrier),
618         guardHoles_(guardHoles),
619         hasGenericProxyStub_(false),
620         hasDenseStub_(false)
621     {
622     }
623 
624     CACHE_HEADER(SetProperty)
625 
626     void reset(ReprotectCode reprotect);
627 
object()628     Register object() const {
629         return object_;
630     }
temp()631     Register temp() const {
632         return temp_;
633     }
tempToUnboxIndex()634     Register tempToUnboxIndex() const {
635         return tempToUnboxIndex_;
636     }
tempDouble()637     FloatRegister tempDouble() const {
638         return tempDouble_;
639     }
tempFloat32()640     FloatRegister tempFloat32() const {
641         return tempFloat32_;
642     }
id()643     ConstantOrRegister id() const {
644         return id_;
645     }
value()646     ConstantOrRegister value() const {
647         return value_;
648     }
strict()649     bool strict() const {
650         return strict_;
651     }
needsTypeBarrier()652     bool needsTypeBarrier() const {
653         return needsTypeBarrier_;
654     }
guardHoles()655     bool guardHoles() const {
656         return guardHoles_;
657     }
hasGenericProxyStub()658     bool hasGenericProxyStub() const {
659         return hasGenericProxyStub_;
660     }
661 
hasDenseStub()662     bool hasDenseStub() const {
663         return hasDenseStub_;
664     }
setHasDenseStub()665     void setHasDenseStub() {
666         MOZ_ASSERT(!hasDenseStub());
667         hasDenseStub_ = true;
668     }
669 
670     enum NativeSetPropCacheability {
671         CanAttachNone,
672         CanAttachSetSlot,
673         MaybeCanAttachAddSlot,
674         CanAttachCallSetter
675     };
676 
677     MOZ_MUST_USE bool attachSetSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
678                                     HandleObject obj, HandleShape shape, bool checkTypeset);
679 
680     MOZ_MUST_USE bool attachCallSetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
681                                        HandleObject obj, HandleObject holder, HandleShape shape,
682                                        void* returnAddr);
683 
684     MOZ_MUST_USE bool attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
685                                     HandleObject obj, HandleId id, HandleShape oldShape,
686                                     HandleObjectGroup oldGroup, bool checkTypeset);
687 
688     MOZ_MUST_USE bool attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
689                                          HandleId id, void* returnAddr);
690 
691     MOZ_MUST_USE bool attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript,
692                                              IonScript* ion, HandleObject obj, HandleId id,
693                                              void* returnAddr);
694 
695     MOZ_MUST_USE bool attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript,
696                                                IonScript* ion, HandleObject obj, HandleId id,
697                                                void* returnAddr);
698 
699     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
700                                     HandleObject obj, HandleValue idval, HandleValue value);
701 
702     MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
703                                       HandleObject obj, HandleId id, bool* emitted,
704                                       bool* tryNativeAddSlot);
705 
706     MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
707                                        HandleObject obj, HandleId id, bool* emitted);
708 
709     MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
710                                               IonScript* ion, HandleObject obj, HandleId id,
711                                               bool* emitted);
712 
713     MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
714                                      HandleObject obj, HandleId id, bool* emitted);
715 
716     MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
717                                     HandleObject obj, HandleValue idval, HandleValue value,
718                                     MutableHandleId id, bool* emitted, bool* tryNativeAddSlot);
719 
720     MOZ_MUST_USE bool tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
721                                        HandleObject obj, HandleId id, HandleObjectGroup oldGroup,
722                                        HandleShape oldShape, bool tryNativeAddSlot, bool* emitted);
723 
724     MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
725                                             HandleObject obj, const Value& idval, bool* emitted);
726 
727     MOZ_MUST_USE bool tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScript,
728                                                  IonScript* ion, HandleObject obj,
729                                                  HandleValue idval, HandleValue val, bool* emitted);
730 };
731 
732 class BindNameIC : public IonCache
733 {
734   protected:
735     Register environmentChain_;
736     PropertyName* name_;
737     Register output_;
738 
739   public:
BindNameIC(Register envChain,PropertyName * name,Register output)740     BindNameIC(Register envChain, PropertyName* name, Register output)
741       : environmentChain_(envChain),
742         name_(name),
743         output_(output)
744     {
745     }
746 
CACHE_HEADER(BindName)747     CACHE_HEADER(BindName)
748 
749     Register environmentChainReg() const {
750         return environmentChain_;
751     }
name()752     HandlePropertyName name() const {
753         return HandlePropertyName::fromMarkedLocation(&name_);
754     }
outputReg()755     Register outputReg() const {
756         return output_;
757     }
758 
759     MOZ_MUST_USE bool attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
760                                    HandleObject envChain);
761 
762     MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
763                                       HandleObject envChain, HandleObject holder);
764 
765     static JSObject*
766     update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain);
767 };
768 
769 class NameIC : public IonCache
770 {
771   protected:
772     // Registers live after the cache, excluding output registers. The initial
773     // value of these registers must be preserved by the cache.
774     LiveRegisterSet liveRegs_;
775 
776     bool typeOf_;
777     Register environmentChain_;
778     PropertyName* name_;
779     TypedOrValueRegister output_;
780 
781   public:
NameIC(LiveRegisterSet liveRegs,bool typeOf,Register envChain,PropertyName * name,TypedOrValueRegister output)782     NameIC(LiveRegisterSet liveRegs, bool typeOf,
783            Register envChain, PropertyName* name,
784            TypedOrValueRegister output)
785       : liveRegs_(liveRegs),
786         typeOf_(typeOf),
787         environmentChain_(envChain),
788         name_(name),
789         output_(output)
790     {
791     }
792 
CACHE_HEADER(Name)793     CACHE_HEADER(Name)
794 
795     Register environmentChainReg() const {
796         return environmentChain_;
797     }
name()798     HandlePropertyName name() const {
799         return HandlePropertyName::fromMarkedLocation(&name_);
800     }
outputReg()801     TypedOrValueRegister outputReg() const {
802         return output_;
803     }
isTypeOf()804     bool isTypeOf() const {
805         return typeOf_;
806     }
807 
808     MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
809                                      HandleObject envChain, HandleObject holderBase,
810                                      HandleNativeObject holder, HandleShape shape);
811 
812     MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
813                                        HandleObject envChain, HandleObject obj,
814                                        HandleObject holder, HandleShape shape,
815                                        void* returnAddr);
816 
817     MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript,
818                                              IonScript* ion, HandleObject envChain);
819 
820     static MOZ_MUST_USE bool
821     update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain,
822            MutableHandleValue vp);
823 };
824 
825 #undef CACHE_HEADER
826 
827 // Implement cache casts now that the compiler can see the inheritance.
828 #define CACHE_CASTS(ickind)                                             \
829     ickind##IC& IonCache::to##ickind()                                  \
830     {                                                                   \
831         MOZ_ASSERT(is##ickind());                                       \
832         return *static_cast<ickind##IC*>(this);                        \
833     }                                                                   \
834     const ickind##IC& IonCache::to##ickind() const                      \
835     {                                                                   \
836         MOZ_ASSERT(is##ickind());                                       \
837         return *static_cast<const ickind##IC*>(this);                  \
838     }
839 IONCACHE_KIND_LIST(CACHE_CASTS)
840 #undef OPCODE_CASTS
841 
842 bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
843 bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape);
844 
845 } // namespace jit
846 } // namespace js
847 
848 #endif /* jit_IonCaches_h */
849