/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_IonCaches_h #define jit_IonCaches_h #if defined(JS_CODEGEN_ARM) # include "jit/arm/Assembler-arm.h" #elif defined(JS_CODEGEN_ARM64) # include "jit/arm64/Assembler-arm64.h" #elif defined(JS_CODEGEN_MIPS32) # include "jit/mips32/Assembler-mips32.h" #elif defined(JS_CODEGEN_MIPS64) # include "jit/mips64/Assembler-mips64.h" #endif #include "jit/JitCompartment.h" #include "jit/Registers.h" #include "jit/shared/Assembler-shared.h" #include "js/TrackedOptimizationInfo.h" #include "vm/TypedArrayCommon.h" namespace js { namespace jit { class LInstruction; #define IONCACHE_KIND_LIST(_) \ _(GetProperty) \ _(SetProperty) \ _(BindName) \ _(Name) // Forward declarations of Cache kinds. #define FORWARD_DECLARE(kind) class kind##IC; IONCACHE_KIND_LIST(FORWARD_DECLARE) #undef FORWARD_DECLARE class IonCacheVisitor { public: #define VISIT_INS(op) \ virtual void visit##op##IC(CodeGenerator* codegen) { \ MOZ_CRASH("NYI: " #op "IC"); \ } IONCACHE_KIND_LIST(VISIT_INS) #undef VISIT_INS }; // Common structure encoding the state of a polymorphic inline cache contained // in the code for an IonScript. IonCaches are used for polymorphic operations // where multiple implementations may be required. // // Roughly speaking, the cache initially jumps to an out of line fragment // which invokes a cache function to perform the operation. The cache function // may generate a stub to perform the operation in certain cases (e.g. a // particular shape for an input object) and attach the stub to existing // stubs, forming a daisy chain of tests for how to perform the operation in // different circumstances. // // Eventually, if too many stubs are generated the cache function may disable // the cache, by generating a stub to make a call and perform the operation // within the VM. // // The caches initially generate a patchable jump to an out of line call // to the cache function. Stubs are attached by appending: when attaching a // new stub, we patch the any failure conditions in last generated stub to // jump to the new stub. Failure conditions in the new stub jump to the cache // function which may generate new stubs. // // Control flow Pointers // =======# ----. .----> // # | | // #======> \-----/ // // Initial state: // // JIT Code // +--------+ .---------------. // | | | | // |========| v +----------+ | // |== IC ==|====>| Cache Fn | | // |========| +----------+ | // | |<=# # | // | | #=======# | // +--------+ Rejoin path | // |________ | // | | // IC | | // Entry | | // +------------+ | // | lastJump_ |---------------/ // +------------+ // | ... | // +------------+ // // Attaching stubs: // // Patch the jump pointed to by lastJump_ to jump to the new stub. Update // lastJump_ to be the new stub's failure jump. The failure jump of the new // stub goes to the fallback label, which is the cache function. In this // fashion, new stubs are _appended_ to the chain of stubs, as lastJump_ // points to the _tail_ of the stub chain. // // JIT Code // +--------+ #=======================# // | | # v // |========| # +----------+ +------+ // |== IC ==|=# | Cache Fn |<====| Stub | // |========| +----------+ ^ +------+ // | |<=# # | # // | | #======#=========|=====# // +--------+ Rejoin path | // |________ | // | | // IC | | // Entry | | // +------------+ | // | lastJump_ |---------------/ // +------------+ // | ... | // +------------+ // // While calls may be made to the cache function and other VM functions, the // cache may still be treated as pure during optimization passes, such that // LICM and GVN may be performed on operations around the cache as if the // operation cannot reenter scripted code through an Invoke() or otherwise have // unexpected behavior. This restricts the sorts of stubs which the cache can // generate or the behaviors which called functions can have, and if a called // function performs a possibly impure operation then the operation will be // marked as such and the calling script will be recompiled. // // Similarly, despite the presence of functions and multiple stubs generated // for a cache, the cache itself may be marked as idempotent and become hoisted // or coalesced by LICM or GVN. This also constrains the stubs which can be // generated for the cache. // // * IonCache usage // // IonCache is the base structure of an inline cache, which generates code stubs // dynamically and attaches them to an IonScript. // // A cache must at least provide a static update function which will usualy have // a JSContext*, followed by the cache index. The rest of the arguments of the // update function are usualy corresponding to the register inputs of the cache, // as it must perform the same operation as any of the stubs that it might // produce. The update function call is handled by the visit function of // CodeGenerator corresponding to this IC. // // The CodeGenerator visit function, as opposed to other visit functions, has // two arguments. The first one is the OutOfLineUpdateCache which stores the LIR // instruction. The second one is the IC object. This function would be called // once the IC is registered with the addCache function of CodeGeneratorShared. // // To register a cache, you must call the addCache function as follow: // // MyCodeIC cache(inputReg1, inputValueReg2, outputReg); // if (!addCache(lir, allocateCache(cache))) // return false; // // Once the cache is allocated with the allocateCache function, any modification // made to the cache would be ignored. // // The addCache function will produce a patchable jump at the location where // it is called. This jump will execute generated stubs and fallback on the code // of the visitMyCodeIC function if no stub match. // // Warning: As the addCache function fallback on a VMCall, calls to // addCache should not be in the same path as another VMCall or in the same // path of another addCache as this is not supported by the invalidation // procedure. class IonCache { public: class StubAttacher; enum Kind { # define DEFINE_CACHEKINDS(ickind) Cache_##ickind, IONCACHE_KIND_LIST(DEFINE_CACHEKINDS) # undef DEFINE_CACHEKINDS Cache_Invalid }; // Cache testing and cast. # define CACHEKIND_CASTS(ickind) \ bool is##ickind() const { \ return kind() == Cache_##ickind; \ } \ inline ickind##IC& to##ickind(); \ inline const ickind##IC& to##ickind() const; IONCACHE_KIND_LIST(CACHEKIND_CASTS) # undef CACHEKIND_CASTS virtual Kind kind() const = 0; virtual void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) = 0; public: static const char* CacheName(Kind kind); protected: bool pure_ : 1; bool idempotent_ : 1; bool disabled_ : 1; size_t stubCount_ : 5; CodeLocationLabel fallbackLabel_; // Location of this operation, nullptr for idempotent caches. JSScript* script_; jsbytecode* pc_; // Location to use when updating profiler pseudostack when leaving this // IC code to enter a callee. jsbytecode* profilerLeavePc_; CodeLocationJump initialJump_; CodeLocationJump lastJump_; CodeLocationLabel rejoinLabel_; private: static const size_t MAX_STUBS; void incrementStubCount() { // The IC should stop generating stubs before wrapping stubCount. stubCount_++; MOZ_ASSERT(stubCount_); } public: IonCache() : pure_(false), idempotent_(false), disabled_(false), stubCount_(0), fallbackLabel_(), script_(nullptr), pc_(nullptr), profilerLeavePc_(nullptr), initialJump_(), lastJump_(), rejoinLabel_() { } void disable(); inline bool isDisabled() const { return disabled_; } // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is // the location of the out-of-line update (slow) path. This location will // be set to the exitJump of the last generated stub. void setFallbackLabel(CodeOffset fallbackLabel) { fallbackLabel_ = fallbackLabel; } void setProfilerLeavePC(jsbytecode* pc) { MOZ_ASSERT(pc != nullptr); profilerLeavePc_ = pc; } // Get the address at which IC rejoins the mainline jitcode. void* rejoinAddress() const { return rejoinLabel_.raw(); } void emitInitialJump(MacroAssembler& masm, RepatchLabel& entry); void updateBaseAddress(JitCode* code, MacroAssembler& masm); // Reset the cache around garbage collection. virtual void reset(ReprotectCode reprotect); bool canAttachStub() const { return stubCount_ < MAX_STUBS; } bool empty() const { return stubCount_ == 0; } enum LinkStatus { LINK_ERROR, CACHE_FLUSHED, LINK_GOOD }; // Use the Linker to link the generated code and check if any // monitoring/allocation caused an invalidation of the running ion script, // this function returns CACHE_FLUSHED. In case of allocation issue this // function returns LINK_ERROR. LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion, JitCode** code); // Fixup variables and update jumps in the list of stubs. Increment the // number of attached stubs accordingly. void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump, Handle code); // Combine both linkStub and attachStub into one function. In addition, it // produces a spew augmented with the attachKind string. MOZ_MUST_USE bool linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion, const char* attachKind, JS::TrackedOutcome = JS::TrackedOutcome::ICOptStub_GenericSuccess); #ifdef DEBUG bool isAllocated() { return fallbackLabel_.isSet(); } #endif bool pure() const { return pure_; } bool idempotent() const { return idempotent_; } void setIdempotent() { MOZ_ASSERT(!idempotent_); MOZ_ASSERT(!script_); MOZ_ASSERT(!pc_); idempotent_ = true; } void setScriptedLocation(JSScript* script, jsbytecode* pc) { MOZ_ASSERT(!idempotent_); script_ = script; pc_ = pc; } void getScriptedLocation(MutableHandleScript pscript, jsbytecode** ppc) const { pscript.set(script_); *ppc = pc_; } jsbytecode* pc() const { MOZ_ASSERT(pc_); return pc_; } void trace(JSTracer* trc); }; // Define the cache kind and pre-declare data structures used for calling inline // caches. #define CACHE_HEADER(ickind) \ Kind kind() const { \ return IonCache::Cache_##ickind; \ } \ \ void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) { \ visitor->visit##ickind##IC(codegen); \ } \ \ static const VMFunction UpdateInfo; // Subclasses of IonCache for the various kinds of caches. These do not define // new data members; all caches must be of the same size. // Helper for idempotent GetPropertyIC location tracking. Declared externally // to be forward declarable. // // Since all the scripts stored in CacheLocations are guaranteed to have been // Ion compiled, and are kept alive by function objects in jitcode, and since // the CacheLocations only have the lifespan of the jitcode, there is no need // to trace or mark any of the scripts. Since JSScripts are always allocated // tenured, and never moved, we can keep raw pointers, and there is no need // for GCPtrScripts here. struct CacheLocation { jsbytecode* pc; JSScript* script; CacheLocation(jsbytecode* pcin, JSScript* scriptin) : pc(pcin), script(scriptin) { } }; class GetPropertyIC : public IonCache { protected: // Registers live after the cache, excluding output registers. The initial // value of these registers must be preserved by the cache. LiveRegisterSet liveRegs_; Register object_; ConstantOrRegister id_; TypedOrValueRegister output_; // Only valid if idempotent size_t locationsIndex_; size_t numLocations_; static const size_t MAX_FAILED_UPDATES = 16; uint16_t failedUpdates_; bool monitoredResult_ : 1; bool allowDoubleResult_ : 1; bool hasTypedArrayLengthStub_ : 1; bool hasMappedArgumentsLengthStub_ : 1; bool hasUnmappedArgumentsLengthStub_ : 1; bool hasMappedArgumentsElementStub_ : 1; bool hasUnmappedArgumentsElementStub_ : 1; bool hasGenericProxyStub_ : 1; bool hasDenseStub_ : 1; void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail); public: GetPropertyIC(LiveRegisterSet liveRegs, Register object, const ConstantOrRegister& id, TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult) : liveRegs_(liveRegs), object_(object), id_(id), output_(output), locationsIndex_(0), numLocations_(0), failedUpdates_(0), monitoredResult_(monitoredResult), allowDoubleResult_(allowDoubleResult), hasTypedArrayLengthStub_(false), hasMappedArgumentsLengthStub_(false), hasUnmappedArgumentsLengthStub_(false), hasMappedArgumentsElementStub_(false), hasUnmappedArgumentsElementStub_(false), hasGenericProxyStub_(false), hasDenseStub_(false) { } CACHE_HEADER(GetProperty) void reset(ReprotectCode reprotect); Register object() const { return object_; } ConstantOrRegister id() const { return id_; } TypedOrValueRegister output() const { return output_; } bool monitoredResult() const { return monitoredResult_; } bool hasTypedArrayLengthStub(HandleObject obj) const { return hasTypedArrayLengthStub_; } bool hasArgumentsLengthStub(bool mapped) const { return mapped ? hasMappedArgumentsLengthStub_ : hasUnmappedArgumentsLengthStub_; } bool hasArgumentsElementStub(bool mapped) const { return mapped ? hasMappedArgumentsElementStub_ : hasUnmappedArgumentsElementStub_; } bool hasGenericProxyStub() const { return hasGenericProxyStub_; } bool hasDenseStub() const { return hasDenseStub_; } void setHasDenseStub() { MOZ_ASSERT(!hasDenseStub()); hasDenseStub_ = true; } void setHasTypedArrayLengthStub(HandleObject obj) { MOZ_ASSERT(obj->is()); MOZ_ASSERT(!hasTypedArrayLengthStub_); hasTypedArrayLengthStub_ = true; } void setLocationInfo(size_t locationsIndex, size_t numLocations) { MOZ_ASSERT(idempotent()); MOZ_ASSERT(!numLocations_); MOZ_ASSERT(numLocations); locationsIndex_ = locationsIndex; numLocations_ = numLocations; } void getLocationInfo(uint32_t* index, uint32_t* num) const { MOZ_ASSERT(idempotent()); *index = locationsIndex_; *num = numLocations_; } enum NativeGetPropCacheability { CanAttachNone, CanAttachReadSlot, CanAttachArrayLength, CanAttachCallGetter }; // Helpers for CanAttachNativeGetProp bool allowArrayLength(JSContext* cx) const; bool allowGetters() const { return monitoredResult() && !idempotent(); } void maybeDisable(bool emitted); // Attach the proper stub, if possible MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, bool* emitted); MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr, bool* emitted); MOZ_MUST_USE bool tryAttachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr, bool* emitted); MOZ_MUST_USE bool tryAttachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr, bool* emitted); MOZ_MUST_USE bool tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool resetNeeded, void* returnAddr, bool* emitted); MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr, bool* emitted); MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); MOZ_MUST_USE bool tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); MOZ_MUST_USE bool tryAttachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, bool* emitted); MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, bool* emitted); static bool canAttachDenseElementHole(JSObject* obj, HandleValue idval, TypedOrValueRegister output); MOZ_MUST_USE bool tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, bool* emitted); static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval, TypedOrValueRegister output); MOZ_MUST_USE bool tryAttachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, bool* emitted); MOZ_MUST_USE bool tryAttachModuleNamespace(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr, bool* emitted); static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj, HandleValue id, MutableHandleValue vp); }; class SetPropertyIC : public IonCache { protected: // Registers live after the cache, excluding output registers. The initial // value of these registers must be preserved by the cache. LiveRegisterSet liveRegs_; Register object_; Register temp_; Register tempToUnboxIndex_; FloatRegister tempDouble_; FloatRegister tempFloat32_; ConstantOrRegister id_; ConstantOrRegister value_; bool strict_ : 1; bool needsTypeBarrier_ : 1; bool guardHoles_ : 1; bool hasGenericProxyStub_ : 1; bool hasDenseStub_ : 1; void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail); 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) : liveRegs_(liveRegs), object_(object), temp_(temp), tempToUnboxIndex_(tempToUnboxIndex), tempDouble_(tempDouble), tempFloat32_(tempFloat32), id_(id), value_(value), strict_(strict), needsTypeBarrier_(needsTypeBarrier), guardHoles_(guardHoles), hasGenericProxyStub_(false), hasDenseStub_(false) { } CACHE_HEADER(SetProperty) void reset(ReprotectCode reprotect); Register object() const { return object_; } Register temp() const { return temp_; } Register tempToUnboxIndex() const { return tempToUnboxIndex_; } FloatRegister tempDouble() const { return tempDouble_; } FloatRegister tempFloat32() const { return tempFloat32_; } ConstantOrRegister id() const { return id_; } ConstantOrRegister value() const { return value_; } bool strict() const { return strict_; } bool needsTypeBarrier() const { return needsTypeBarrier_; } bool guardHoles() const { return guardHoles_; } bool hasGenericProxyStub() const { return hasGenericProxyStub_; } bool hasDenseStub() const { return hasDenseStub_; } void setHasDenseStub() { MOZ_ASSERT(!hasDenseStub()); hasDenseStub_ = true; } enum NativeSetPropCacheability { CanAttachNone, CanAttachSetSlot, MaybeCanAttachAddSlot, CanAttachCallSetter }; MOZ_MUST_USE bool attachSetSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleShape shape, bool checkTypeset); MOZ_MUST_USE bool attachCallSetter(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleObject holder, HandleShape shape, void* returnAddr); MOZ_MUST_USE bool attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, HandleShape oldShape, HandleObjectGroup oldGroup, bool checkTypeset); MOZ_MUST_USE bool attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleId id, void* returnAddr); MOZ_MUST_USE bool attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr); MOZ_MUST_USE bool attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, void* returnAddr); static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj, HandleValue idval, HandleValue value); MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted, bool* tryNativeAddSlot); MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, HandleValue value, MutableHandleId id, bool* emitted, bool* tryNativeAddSlot); MOZ_MUST_USE bool tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, HandleObjectGroup oldGroup, HandleShape oldShape, bool tryNativeAddSlot, bool* emitted); MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, const Value& idval, bool* emitted); MOZ_MUST_USE bool tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, HandleValue val, bool* emitted); }; class BindNameIC : public IonCache { protected: Register environmentChain_; PropertyName* name_; Register output_; public: BindNameIC(Register envChain, PropertyName* name, Register output) : environmentChain_(envChain), name_(name), output_(output) { } CACHE_HEADER(BindName) Register environmentChainReg() const { return environmentChain_; } HandlePropertyName name() const { return HandlePropertyName::fromMarkedLocation(&name_); } Register outputReg() const { return output_; } MOZ_MUST_USE bool attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject envChain); MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject envChain, HandleObject holder); static JSObject* update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain); }; class NameIC : public IonCache { protected: // Registers live after the cache, excluding output registers. The initial // value of these registers must be preserved by the cache. LiveRegisterSet liveRegs_; bool typeOf_; Register environmentChain_; PropertyName* name_; TypedOrValueRegister output_; public: NameIC(LiveRegisterSet liveRegs, bool typeOf, Register envChain, PropertyName* name, TypedOrValueRegister output) : liveRegs_(liveRegs), typeOf_(typeOf), environmentChain_(envChain), name_(name), output_(output) { } CACHE_HEADER(Name) Register environmentChainReg() const { return environmentChain_; } HandlePropertyName name() const { return HandlePropertyName::fromMarkedLocation(&name_); } TypedOrValueRegister outputReg() const { return output_; } bool isTypeOf() const { return typeOf_; } MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject envChain, HandleObject holderBase, HandleNativeObject holder, HandleShape shape); MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject envChain, HandleObject obj, HandleObject holder, HandleShape shape, void* returnAddr); MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject envChain); static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain, MutableHandleValue vp); }; #undef CACHE_HEADER // Implement cache casts now that the compiler can see the inheritance. #define CACHE_CASTS(ickind) \ ickind##IC& IonCache::to##ickind() \ { \ MOZ_ASSERT(is##ickind()); \ return *static_cast(this); \ } \ const ickind##IC& IonCache::to##ickind() const \ { \ MOZ_ASSERT(is##ickind()); \ return *static_cast(this); \ } IONCACHE_KIND_LIST(CACHE_CASTS) #undef OPCODE_CASTS bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder); bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape); } // namespace jit } // namespace js #endif /* jit_IonCaches_h */