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