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_IonIC_h
8 #define jit_IonIC_h
9 
10 #include "jit/CacheIR.h"
11 
12 namespace js {
13 namespace jit {
14 
15 class CacheIRStubInfo;
16 
17 // An optimized stub attached to an IonIC.
18 class IonICStub {
19   // Code to jump to when this stub fails. This is either the next optimized
20   // stub or the OOL fallback path.
21   uint8_t* nextCodeRaw_;
22 
23   // The next optimized stub in this chain, or nullptr if this is the last
24   // one.
25   IonICStub* next_;
26 
27   // Info about this stub.
28   CacheIRStubInfo* stubInfo_;
29 
30  public:
IonICStub(uint8_t * fallbackCode,CacheIRStubInfo * stubInfo)31   IonICStub(uint8_t* fallbackCode, CacheIRStubInfo* stubInfo)
32       : nextCodeRaw_(fallbackCode), next_(nullptr), stubInfo_(stubInfo) {}
33 
nextCodeRaw()34   uint8_t* nextCodeRaw() const { return nextCodeRaw_; }
nextCodeRawPtr()35   uint8_t** nextCodeRawPtr() { return &nextCodeRaw_; }
stubInfo()36   CacheIRStubInfo* stubInfo() const { return stubInfo_; }
next()37   IonICStub* next() const { return next_; }
38 
39   uint8_t* stubDataStart();
40 
setNext(IonICStub * next,uint8_t * nextCodeRaw)41   void setNext(IonICStub* next, uint8_t* nextCodeRaw) {
42     MOZ_ASSERT(!next_);
43     MOZ_ASSERT(next && nextCodeRaw);
44     next_ = next;
45     nextCodeRaw_ = nextCodeRaw;
46   }
47 
48   // Null out pointers when we unlink stubs, to ensure we never use
49   // discarded stubs.
poison()50   void poison() {
51     nextCodeRaw_ = nullptr;
52     next_ = nullptr;
53     stubInfo_ = nullptr;
54   }
55 };
56 
57 class IonGetPropertyIC;
58 class IonSetPropertyIC;
59 class IonGetPropSuperIC;
60 class IonGetNameIC;
61 class IonBindNameIC;
62 class IonGetIteratorIC;
63 class IonHasOwnIC;
64 class IonInIC;
65 class IonInstanceOfIC;
66 class IonCompareIC;
67 class IonUnaryArithIC;
68 class IonBinaryArithIC;
69 
70 class IonIC {
71   // This either points at the OOL path for the fallback path, or the code for
72   // the first stub.
73   uint8_t* codeRaw_;
74 
75   // The first optimized stub, or nullptr.
76   IonICStub* firstStub_;
77 
78   // Location of this IC, nullptr for idempotent caches.
79   JSScript* script_;
80   jsbytecode* pc_;
81 
82   // The offset of the rejoin location in the IonScript's code (stubs jump to
83   // this location).
84   uint32_t rejoinOffset_;
85 
86   // The offset of the OOL path in the IonScript's code that calls the IC's
87   // update function.
88   uint32_t fallbackOffset_;
89 
90   CacheKind kind_;
91   bool idempotent_ : 1;
92   ICState state_;
93 
94  protected:
IonIC(CacheKind kind)95   explicit IonIC(CacheKind kind)
96       : codeRaw_(nullptr),
97         firstStub_(nullptr),
98         script_(nullptr),
99         pc_(nullptr),
100         rejoinOffset_(0),
101         fallbackOffset_(0),
102         kind_(kind),
103         idempotent_(false),
104         state_() {}
105 
106   void attachStub(IonICStub* newStub, JitCode* code);
107 
108  public:
setScriptedLocation(JSScript * script,jsbytecode * pc)109   void setScriptedLocation(JSScript* script, jsbytecode* pc) {
110     MOZ_ASSERT(!script_ && !pc_);
111     MOZ_ASSERT(script && pc);
112     script_ = script;
113     pc_ = pc;
114   }
115 
script()116   JSScript* script() const {
117     MOZ_ASSERT(script_);
118     return script_;
119   }
pc()120   jsbytecode* pc() const {
121     MOZ_ASSERT(pc_);
122     return pc_;
123   }
124 
125   // Discard all stubs.
126   void discardStubs(Zone* zone, IonScript* ionScript);
127 
128   // Discard all stubs and reset the ICState.
129   void reset(Zone* zone, IonScript* ionScript);
130 
state()131   ICState& state() { return state_; }
132 
kind()133   CacheKind kind() const { return kind_; }
codeRawPtr()134   uint8_t** codeRawPtr() { return &codeRaw_; }
135 
idempotent()136   bool idempotent() const { return idempotent_; }
setIdempotent()137   void setIdempotent() { idempotent_ = true; }
138 
setFallbackOffset(CodeOffset offset)139   void setFallbackOffset(CodeOffset offset) {
140     fallbackOffset_ = offset.offset();
141   }
setRejoinOffset(CodeOffset offset)142   void setRejoinOffset(CodeOffset offset) { rejoinOffset_ = offset.offset(); }
143 
144   void resetCodeRaw(IonScript* ionScript);
145 
146   uint8_t* fallbackAddr(IonScript* ionScript) const;
147   uint8_t* rejoinAddr(IonScript* ionScript) const;
148 
asGetPropertyIC()149   IonGetPropertyIC* asGetPropertyIC() {
150     MOZ_ASSERT(kind_ == CacheKind::GetProp || kind_ == CacheKind::GetElem);
151     return (IonGetPropertyIC*)this;
152   }
asSetPropertyIC()153   IonSetPropertyIC* asSetPropertyIC() {
154     MOZ_ASSERT(kind_ == CacheKind::SetProp || kind_ == CacheKind::SetElem);
155     return (IonSetPropertyIC*)this;
156   }
asGetPropSuperIC()157   IonGetPropSuperIC* asGetPropSuperIC() {
158     MOZ_ASSERT(kind_ == CacheKind::GetPropSuper ||
159                kind_ == CacheKind::GetElemSuper);
160     return (IonGetPropSuperIC*)this;
161   }
asGetNameIC()162   IonGetNameIC* asGetNameIC() {
163     MOZ_ASSERT(kind_ == CacheKind::GetName);
164     return (IonGetNameIC*)this;
165   }
asBindNameIC()166   IonBindNameIC* asBindNameIC() {
167     MOZ_ASSERT(kind_ == CacheKind::BindName);
168     return (IonBindNameIC*)this;
169   }
asGetIteratorIC()170   IonGetIteratorIC* asGetIteratorIC() {
171     MOZ_ASSERT(kind_ == CacheKind::GetIterator);
172     return (IonGetIteratorIC*)this;
173   }
asHasOwnIC()174   IonHasOwnIC* asHasOwnIC() {
175     MOZ_ASSERT(kind_ == CacheKind::HasOwn);
176     return (IonHasOwnIC*)this;
177   }
asInIC()178   IonInIC* asInIC() {
179     MOZ_ASSERT(kind_ == CacheKind::In);
180     return (IonInIC*)this;
181   }
asInstanceOfIC()182   IonInstanceOfIC* asInstanceOfIC() {
183     MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
184     return (IonInstanceOfIC*)this;
185   }
asCompareIC()186   IonCompareIC* asCompareIC() {
187     MOZ_ASSERT(kind_ == CacheKind::Compare);
188     return (IonCompareIC*)this;
189   }
asUnaryArithIC()190   IonUnaryArithIC* asUnaryArithIC() {
191     MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
192     return (IonUnaryArithIC*)this;
193   }
asBinaryArithIC()194   IonBinaryArithIC* asBinaryArithIC() {
195     MOZ_ASSERT(kind_ == CacheKind::BinaryArith);
196     return (IonBinaryArithIC*)this;
197   }
198 
199   // Returns the Register to use as scratch when entering IC stubs. This
200   // should either be an output register or a temp.
201   Register scratchRegisterForEntryJump();
202 
203   void trace(JSTracer* trc, IonScript* ionScript);
204 
205   void attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
206                          CacheKind kind, IonScript* ionScript, bool* attached,
207                          const PropertyTypeCheckInfo* typeCheckInfo = nullptr);
208 };
209 
210 class IonGetPropertyIC : public IonIC {
211  private:
212   LiveRegisterSet liveRegs_;
213 
214   TypedOrValueRegister value_;
215   ConstantOrRegister id_;
216   TypedOrValueRegister output_;
217   Register maybeTemp_;  // Might be InvalidReg.
218 
219   GetPropertyResultFlags resultFlags_;
220 
221  public:
IonGetPropertyIC(CacheKind kind,LiveRegisterSet liveRegs,TypedOrValueRegister value,const ConstantOrRegister & id,TypedOrValueRegister output,Register maybeTemp,GetPropertyResultFlags resultFlags)222   IonGetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs,
223                    TypedOrValueRegister value, const ConstantOrRegister& id,
224                    TypedOrValueRegister output, Register maybeTemp,
225                    GetPropertyResultFlags resultFlags)
226       : IonIC(kind),
227         liveRegs_(liveRegs),
228         value_(value),
229         id_(id),
230         output_(output),
231         maybeTemp_(maybeTemp),
232         resultFlags_(resultFlags) {}
233 
value()234   TypedOrValueRegister value() const { return value_; }
id()235   ConstantOrRegister id() const { return id_; }
output()236   TypedOrValueRegister output() const { return output_; }
maybeTemp()237   Register maybeTemp() const { return maybeTemp_; }
liveRegs()238   LiveRegisterSet liveRegs() const { return liveRegs_; }
resultFlags()239   GetPropertyResultFlags resultFlags() const { return resultFlags_; }
monitoredResult()240   bool monitoredResult() const {
241     return resultFlags_ & GetPropertyResultFlags::Monitored;
242   }
allowDoubleResult()243   bool allowDoubleResult() const {
244     return resultFlags_ & GetPropertyResultFlags::AllowDouble;
245   }
246 
247   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
248                                   IonGetPropertyIC* ic, HandleValue val,
249                                   HandleValue idVal, MutableHandleValue res);
250 };
251 
252 class IonGetPropSuperIC : public IonIC {
253   LiveRegisterSet liveRegs_;
254 
255   Register object_;
256   TypedOrValueRegister receiver_;
257   ConstantOrRegister id_;
258   TypedOrValueRegister output_;
259 
260  public:
IonGetPropSuperIC(CacheKind kind,LiveRegisterSet liveRegs,Register object,TypedOrValueRegister receiver,const ConstantOrRegister & id,TypedOrValueRegister output)261   IonGetPropSuperIC(CacheKind kind, LiveRegisterSet liveRegs, Register object,
262                     TypedOrValueRegister receiver, const ConstantOrRegister& id,
263                     TypedOrValueRegister output)
264       : IonIC(kind),
265         liveRegs_(liveRegs),
266         object_(object),
267         receiver_(receiver),
268         id_(id),
269         output_(output) {}
270 
object()271   Register object() const { return object_; }
receiver()272   TypedOrValueRegister receiver() const { return receiver_; }
id()273   ConstantOrRegister id() const { return id_; }
output()274   TypedOrValueRegister output() const { return output_; }
liveRegs()275   LiveRegisterSet liveRegs() const { return liveRegs_; }
276 
277   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
278                                   IonGetPropSuperIC* ic, HandleObject obj,
279                                   HandleValue receiver, HandleValue idVal,
280                                   MutableHandleValue res);
281 };
282 
283 class IonSetPropertyIC : public IonIC {
284   LiveRegisterSet liveRegs_;
285 
286   Register object_;
287   Register temp_;
288   ConstantOrRegister id_;
289   ConstantOrRegister rhs_;
290   bool strict_ : 1;
291   bool needsPostBarrier_ : 1;
292   bool needsTypeBarrier_ : 1;
293   bool guardHoles_ : 1;
294 
295  public:
IonSetPropertyIC(CacheKind kind,LiveRegisterSet liveRegs,Register object,Register temp,const ConstantOrRegister & id,const ConstantOrRegister & rhs,bool strict,bool needsPostBarrier,bool needsTypeBarrier,bool guardHoles)296   IonSetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object,
297                    Register temp, const ConstantOrRegister& id,
298                    const ConstantOrRegister& rhs, bool strict,
299                    bool needsPostBarrier, bool needsTypeBarrier,
300                    bool guardHoles)
301       : IonIC(kind),
302         liveRegs_(liveRegs),
303         object_(object),
304         temp_(temp),
305         id_(id),
306         rhs_(rhs),
307         strict_(strict),
308         needsPostBarrier_(needsPostBarrier),
309         needsTypeBarrier_(needsTypeBarrier),
310         guardHoles_(guardHoles) {}
311 
liveRegs()312   LiveRegisterSet liveRegs() const { return liveRegs_; }
object()313   Register object() const { return object_; }
id()314   ConstantOrRegister id() const { return id_; }
rhs()315   ConstantOrRegister rhs() const { return rhs_; }
316 
temp()317   Register temp() const { return temp_; }
318 
strict()319   bool strict() const { return strict_; }
needsPostBarrier()320   bool needsPostBarrier() const { return needsPostBarrier_; }
needsTypeBarrier()321   bool needsTypeBarrier() const { return needsTypeBarrier_; }
guardHoles()322   bool guardHoles() const { return guardHoles_; }
323 
324   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
325                                   IonSetPropertyIC* ic, HandleObject obj,
326                                   HandleValue idVal, HandleValue rhs);
327 };
328 
329 class IonGetNameIC : public IonIC {
330   LiveRegisterSet liveRegs_;
331 
332   Register environment_;
333   ValueOperand output_;
334   Register temp_;
335 
336  public:
IonGetNameIC(LiveRegisterSet liveRegs,Register environment,ValueOperand output,Register temp)337   IonGetNameIC(LiveRegisterSet liveRegs, Register environment,
338                ValueOperand output, Register temp)
339       : IonIC(CacheKind::GetName),
340         liveRegs_(liveRegs),
341         environment_(environment),
342         output_(output),
343         temp_(temp) {}
344 
environment()345   Register environment() const { return environment_; }
output()346   ValueOperand output() const { return output_; }
temp()347   Register temp() const { return temp_; }
liveRegs()348   LiveRegisterSet liveRegs() const { return liveRegs_; }
349 
350   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
351                                   IonGetNameIC* ic, HandleObject envChain,
352                                   MutableHandleValue res);
353 };
354 
355 class IonBindNameIC : public IonIC {
356   LiveRegisterSet liveRegs_;
357 
358   Register environment_;
359   Register output_;
360   Register temp_;
361 
362  public:
IonBindNameIC(LiveRegisterSet liveRegs,Register environment,Register output,Register temp)363   IonBindNameIC(LiveRegisterSet liveRegs, Register environment, Register output,
364                 Register temp)
365       : IonIC(CacheKind::BindName),
366         liveRegs_(liveRegs),
367         environment_(environment),
368         output_(output),
369         temp_(temp) {}
370 
environment()371   Register environment() const { return environment_; }
output()372   Register output() const { return output_; }
temp()373   Register temp() const { return temp_; }
liveRegs()374   LiveRegisterSet liveRegs() const { return liveRegs_; }
375 
376   static JSObject* update(JSContext* cx, HandleScript outerScript,
377                           IonBindNameIC* ic, HandleObject envChain);
378 };
379 
380 class IonGetIteratorIC : public IonIC {
381   LiveRegisterSet liveRegs_;
382   TypedOrValueRegister value_;
383   Register output_;
384   Register temp1_;
385   Register temp2_;
386 
387  public:
IonGetIteratorIC(LiveRegisterSet liveRegs,TypedOrValueRegister value,Register output,Register temp1,Register temp2)388   IonGetIteratorIC(LiveRegisterSet liveRegs, TypedOrValueRegister value,
389                    Register output, Register temp1, Register temp2)
390       : IonIC(CacheKind::GetIterator),
391         liveRegs_(liveRegs),
392         value_(value),
393         output_(output),
394         temp1_(temp1),
395         temp2_(temp2) {}
396 
value()397   TypedOrValueRegister value() const { return value_; }
output()398   Register output() const { return output_; }
temp1()399   Register temp1() const { return temp1_; }
temp2()400   Register temp2() const { return temp2_; }
liveRegs()401   LiveRegisterSet liveRegs() const { return liveRegs_; }
402 
403   static JSObject* update(JSContext* cx, HandleScript outerScript,
404                           IonGetIteratorIC* ic, HandleValue value);
405 };
406 
407 class IonHasOwnIC : public IonIC {
408   LiveRegisterSet liveRegs_;
409 
410   TypedOrValueRegister value_;
411   TypedOrValueRegister id_;
412   Register output_;
413 
414  public:
IonHasOwnIC(LiveRegisterSet liveRegs,TypedOrValueRegister value,TypedOrValueRegister id,Register output)415   IonHasOwnIC(LiveRegisterSet liveRegs, TypedOrValueRegister value,
416               TypedOrValueRegister id, Register output)
417       : IonIC(CacheKind::HasOwn),
418         liveRegs_(liveRegs),
419         value_(value),
420         id_(id),
421         output_(output) {}
422 
value()423   TypedOrValueRegister value() const { return value_; }
id()424   TypedOrValueRegister id() const { return id_; }
output()425   Register output() const { return output_; }
liveRegs()426   LiveRegisterSet liveRegs() const { return liveRegs_; }
427 
428   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
429                                   IonHasOwnIC* ic, HandleValue val,
430                                   HandleValue idVal, int32_t* res);
431 };
432 
433 class IonInIC : public IonIC {
434   LiveRegisterSet liveRegs_;
435 
436   ConstantOrRegister key_;
437   Register object_;
438   Register output_;
439   Register temp_;
440 
441  public:
IonInIC(LiveRegisterSet liveRegs,const ConstantOrRegister & key,Register object,Register output,Register temp)442   IonInIC(LiveRegisterSet liveRegs, const ConstantOrRegister& key,
443           Register object, Register output, Register temp)
444       : IonIC(CacheKind::In),
445         liveRegs_(liveRegs),
446         key_(key),
447         object_(object),
448         output_(output),
449         temp_(temp) {}
450 
key()451   ConstantOrRegister key() const { return key_; }
object()452   Register object() const { return object_; }
output()453   Register output() const { return output_; }
temp()454   Register temp() const { return temp_; }
liveRegs()455   LiveRegisterSet liveRegs() const { return liveRegs_; }
456 
457   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
458                                   IonInIC* ic, HandleValue key,
459                                   HandleObject obj, bool* res);
460 };
461 
462 class IonInstanceOfIC : public IonIC {
463   LiveRegisterSet liveRegs_;
464 
465   TypedOrValueRegister lhs_;
466   Register rhs_;
467   Register output_;
468 
469  public:
IonInstanceOfIC(LiveRegisterSet liveRegs,TypedOrValueRegister lhs,Register rhs,Register output)470   IonInstanceOfIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs,
471                   Register rhs, Register output)
472       : IonIC(CacheKind::InstanceOf),
473         liveRegs_(liveRegs),
474         lhs_(lhs),
475         rhs_(rhs),
476         output_(output) {}
477 
liveRegs()478   LiveRegisterSet liveRegs() const { return liveRegs_; }
lhs()479   TypedOrValueRegister lhs() const { return lhs_; }
rhs()480   Register rhs() const { return rhs_; }
output()481   Register output() const { return output_; }
482 
483   // This signature mimics that of TryAttachInstanceOfStub in baseline
484   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
485                                   IonInstanceOfIC* ic, HandleValue lhs,
486                                   HandleObject rhs, bool* attached);
487 };
488 
489 class IonCompareIC : public IonIC {
490   LiveRegisterSet liveRegs_;
491 
492   TypedOrValueRegister lhs_;
493   TypedOrValueRegister rhs_;
494   Register output_;
495 
496  public:
IonCompareIC(LiveRegisterSet liveRegs,TypedOrValueRegister lhs,TypedOrValueRegister rhs,Register output)497   IonCompareIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs,
498                TypedOrValueRegister rhs, Register output)
499       : IonIC(CacheKind::Compare),
500         liveRegs_(liveRegs),
501         lhs_(lhs),
502         rhs_(rhs),
503         output_(output) {}
504 
liveRegs()505   LiveRegisterSet liveRegs() const { return liveRegs_; }
lhs()506   TypedOrValueRegister lhs() const { return lhs_; }
rhs()507   TypedOrValueRegister rhs() const { return rhs_; }
output()508   Register output() const { return output_; }
509 
510   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
511                                   IonCompareIC* stub, HandleValue lhs,
512                                   HandleValue rhs, bool* res);
513 };
514 
515 class IonUnaryArithIC : public IonIC {
516   LiveRegisterSet liveRegs_;
517 
518   TypedOrValueRegister input_;
519   ValueOperand output_;
520 
521  public:
IonUnaryArithIC(LiveRegisterSet liveRegs,TypedOrValueRegister input,ValueOperand output)522   IonUnaryArithIC(LiveRegisterSet liveRegs, TypedOrValueRegister input,
523                   ValueOperand output)
524       : IonIC(CacheKind::UnaryArith),
525         liveRegs_(liveRegs),
526         input_(input),
527         output_(output) {}
528 
liveRegs()529   LiveRegisterSet liveRegs() const { return liveRegs_; }
input()530   TypedOrValueRegister input() const { return input_; }
output()531   ValueOperand output() const { return output_; }
532 
533   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
534                                   IonUnaryArithIC* stub, HandleValue val,
535                                   MutableHandleValue res);
536 };
537 
538 class IonBinaryArithIC : public IonIC {
539   LiveRegisterSet liveRegs_;
540 
541   TypedOrValueRegister lhs_;
542   TypedOrValueRegister rhs_;
543   ValueOperand output_;
544 
545  public:
IonBinaryArithIC(LiveRegisterSet liveRegs,TypedOrValueRegister lhs,TypedOrValueRegister rhs,ValueOperand output)546   IonBinaryArithIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs,
547                    TypedOrValueRegister rhs, ValueOperand output)
548       : IonIC(CacheKind::BinaryArith),
549         liveRegs_(liveRegs),
550         lhs_(lhs),
551         rhs_(rhs),
552         output_(output) {}
553 
liveRegs()554   LiveRegisterSet liveRegs() const { return liveRegs_; }
lhs()555   TypedOrValueRegister lhs() const { return lhs_; }
rhs()556   TypedOrValueRegister rhs() const { return rhs_; }
output()557   ValueOperand output() const { return output_; }
558 
559   static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript,
560                                   IonBinaryArithIC* stub, HandleValue lhs,
561                                   HandleValue rhs, MutableHandleValue res);
562 };
563 
564 }  // namespace jit
565 }  // namespace js
566 
567 #endif /* jit_IonIC_h */
568