Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifndef jit_IonCaches_h |
michael@0 | 8 | #define jit_IonCaches_h |
michael@0 | 9 | |
michael@0 | 10 | #if defined(JS_CODEGEN_ARM) |
michael@0 | 11 | # include "jit/arm/Assembler-arm.h" |
michael@0 | 12 | #elif defined(JS_CODEGEN_MIPS) |
michael@0 | 13 | # include "jit/mips/Assembler-mips.h" |
michael@0 | 14 | #endif |
michael@0 | 15 | #include "jit/Registers.h" |
michael@0 | 16 | #include "jit/shared/Assembler-shared.h" |
michael@0 | 17 | |
michael@0 | 18 | namespace js { |
michael@0 | 19 | |
michael@0 | 20 | class LockedJSContext; |
michael@0 | 21 | class TypedArrayObject; |
michael@0 | 22 | |
michael@0 | 23 | namespace jit { |
michael@0 | 24 | |
michael@0 | 25 | #define IONCACHE_KIND_LIST(_) \ |
michael@0 | 26 | _(GetProperty) \ |
michael@0 | 27 | _(SetProperty) \ |
michael@0 | 28 | _(GetElement) \ |
michael@0 | 29 | _(SetElement) \ |
michael@0 | 30 | _(BindName) \ |
michael@0 | 31 | _(Name) \ |
michael@0 | 32 | _(CallsiteClone) \ |
michael@0 | 33 | _(GetPropertyPar) \ |
michael@0 | 34 | _(GetElementPar) \ |
michael@0 | 35 | _(SetPropertyPar) \ |
michael@0 | 36 | _(SetElementPar) |
michael@0 | 37 | |
michael@0 | 38 | // Forward declarations of Cache kinds. |
michael@0 | 39 | #define FORWARD_DECLARE(kind) class kind##IC; |
michael@0 | 40 | IONCACHE_KIND_LIST(FORWARD_DECLARE) |
michael@0 | 41 | #undef FORWARD_DECLARE |
michael@0 | 42 | |
michael@0 | 43 | class IonCacheVisitor |
michael@0 | 44 | { |
michael@0 | 45 | public: |
michael@0 | 46 | #define VISIT_INS(op) \ |
michael@0 | 47 | virtual bool visit##op##IC(CodeGenerator *codegen) { \ |
michael@0 | 48 | MOZ_ASSUME_UNREACHABLE("NYI: " #op "IC"); \ |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | IONCACHE_KIND_LIST(VISIT_INS) |
michael@0 | 52 | #undef VISIT_INS |
michael@0 | 53 | }; |
michael@0 | 54 | |
michael@0 | 55 | // Common shared temporary state needed during codegen between the different |
michael@0 | 56 | // kinds of caches. Used by OutOfLineUpdateCache. |
michael@0 | 57 | struct AddCacheState |
michael@0 | 58 | { |
michael@0 | 59 | RepatchLabel repatchEntry; |
michael@0 | 60 | Register dispatchScratch; |
michael@0 | 61 | }; |
michael@0 | 62 | |
michael@0 | 63 | |
michael@0 | 64 | // Common structure encoding the state of a polymorphic inline cache contained |
michael@0 | 65 | // in the code for an IonScript. IonCaches are used for polymorphic operations |
michael@0 | 66 | // where multiple implementations may be required. |
michael@0 | 67 | // |
michael@0 | 68 | // Roughly speaking, the cache initially jumps to an out of line fragment |
michael@0 | 69 | // which invokes a cache function to perform the operation. The cache function |
michael@0 | 70 | // may generate a stub to perform the operation in certain cases (e.g. a |
michael@0 | 71 | // particular shape for an input object) and attach the stub to existing |
michael@0 | 72 | // stubs, forming a daisy chain of tests for how to perform the operation in |
michael@0 | 73 | // different circumstances. The details of how stubs are linked up as |
michael@0 | 74 | // described in comments below for the classes RepatchIonCache and |
michael@0 | 75 | // DispatchIonCache. |
michael@0 | 76 | // |
michael@0 | 77 | // Eventually, if too many stubs are generated the cache function may disable |
michael@0 | 78 | // the cache, by generating a stub to make a call and perform the operation |
michael@0 | 79 | // within the VM. |
michael@0 | 80 | // |
michael@0 | 81 | // While calls may be made to the cache function and other VM functions, the |
michael@0 | 82 | // cache may still be treated as pure during optimization passes, such that |
michael@0 | 83 | // LICM and GVN may be performed on operations around the cache as if the |
michael@0 | 84 | // operation cannot reenter scripted code through an Invoke() or otherwise have |
michael@0 | 85 | // unexpected behavior. This restricts the sorts of stubs which the cache can |
michael@0 | 86 | // generate or the behaviors which called functions can have, and if a called |
michael@0 | 87 | // function performs a possibly impure operation then the operation will be |
michael@0 | 88 | // marked as such and the calling script will be recompiled. |
michael@0 | 89 | // |
michael@0 | 90 | // Similarly, despite the presence of functions and multiple stubs generated |
michael@0 | 91 | // for a cache, the cache itself may be marked as idempotent and become hoisted |
michael@0 | 92 | // or coalesced by LICM or GVN. This also constrains the stubs which can be |
michael@0 | 93 | // generated for the cache. |
michael@0 | 94 | // |
michael@0 | 95 | // * IonCache usage |
michael@0 | 96 | // |
michael@0 | 97 | // IonCache is the base structure of an inline cache, which generates code stubs |
michael@0 | 98 | // dynamically and attaches them to an IonScript. |
michael@0 | 99 | // |
michael@0 | 100 | // A cache must at least provide a static update function which will usualy have |
michael@0 | 101 | // a JSContext*, followed by the cache index. The rest of the arguments of the |
michael@0 | 102 | // update function are usualy corresponding to the register inputs of the cache, |
michael@0 | 103 | // as it must perform the same operation as any of the stubs that it might |
michael@0 | 104 | // produce. The update function call is handled by the visit function of |
michael@0 | 105 | // CodeGenerator corresponding to this IC. |
michael@0 | 106 | // |
michael@0 | 107 | // The CodeGenerator visit function, as opposed to other visit functions, has |
michael@0 | 108 | // two arguments. The first one is the OutOfLineUpdateCache which stores the LIR |
michael@0 | 109 | // instruction. The second one is the IC object. This function would be called |
michael@0 | 110 | // once the IC is registered with the addCache function of CodeGeneratorShared. |
michael@0 | 111 | // |
michael@0 | 112 | // To register a cache, you must call the addCache function as follow: |
michael@0 | 113 | // |
michael@0 | 114 | // MyCodeIC cache(inputReg1, inputValueReg2, outputReg); |
michael@0 | 115 | // if (!addCache(lir, allocateCache(cache))) |
michael@0 | 116 | // return false; |
michael@0 | 117 | // |
michael@0 | 118 | // Once the cache is allocated with the allocateCache function, any modification |
michael@0 | 119 | // made to the cache would be ignored. |
michael@0 | 120 | // |
michael@0 | 121 | // The addCache function will produce a patchable jump at the location where |
michael@0 | 122 | // it is called. This jump will execute generated stubs and fallback on the code |
michael@0 | 123 | // of the visitMyCodeIC function if no stub match. |
michael@0 | 124 | // |
michael@0 | 125 | // Warning: As the addCache function fallback on a VMCall, calls to |
michael@0 | 126 | // addCache should not be in the same path as another VMCall or in the same |
michael@0 | 127 | // path of another addCache as this is not supported by the invalidation |
michael@0 | 128 | // procedure. |
michael@0 | 129 | class IonCache |
michael@0 | 130 | { |
michael@0 | 131 | public: |
michael@0 | 132 | class StubAttacher; |
michael@0 | 133 | |
michael@0 | 134 | enum Kind { |
michael@0 | 135 | # define DEFINE_CACHEKINDS(ickind) Cache_##ickind, |
michael@0 | 136 | IONCACHE_KIND_LIST(DEFINE_CACHEKINDS) |
michael@0 | 137 | # undef DEFINE_CACHEKINDS |
michael@0 | 138 | Cache_Invalid |
michael@0 | 139 | }; |
michael@0 | 140 | |
michael@0 | 141 | // Cache testing and cast. |
michael@0 | 142 | # define CACHEKIND_CASTS(ickind) \ |
michael@0 | 143 | bool is##ickind() const { \ |
michael@0 | 144 | return kind() == Cache_##ickind; \ |
michael@0 | 145 | } \ |
michael@0 | 146 | inline ickind##IC &to##ickind(); \ |
michael@0 | 147 | inline const ickind##IC &to##ickind() const; |
michael@0 | 148 | IONCACHE_KIND_LIST(CACHEKIND_CASTS) |
michael@0 | 149 | # undef CACHEKIND_CASTS |
michael@0 | 150 | |
michael@0 | 151 | virtual Kind kind() const = 0; |
michael@0 | 152 | |
michael@0 | 153 | virtual bool accept(CodeGenerator *codegen, IonCacheVisitor *visitor) = 0; |
michael@0 | 154 | |
michael@0 | 155 | public: |
michael@0 | 156 | |
michael@0 | 157 | static const char *CacheName(Kind kind); |
michael@0 | 158 | |
michael@0 | 159 | protected: |
michael@0 | 160 | bool pure_ : 1; |
michael@0 | 161 | bool idempotent_ : 1; |
michael@0 | 162 | bool disabled_ : 1; |
michael@0 | 163 | size_t stubCount_ : 5; |
michael@0 | 164 | |
michael@0 | 165 | CodeLocationLabel fallbackLabel_; |
michael@0 | 166 | |
michael@0 | 167 | // Location of this operation, nullptr for idempotent caches. |
michael@0 | 168 | JSScript *script_; |
michael@0 | 169 | jsbytecode *pc_; |
michael@0 | 170 | |
michael@0 | 171 | private: |
michael@0 | 172 | static const size_t MAX_STUBS; |
michael@0 | 173 | void incrementStubCount() { |
michael@0 | 174 | // The IC should stop generating stubs before wrapping stubCount. |
michael@0 | 175 | stubCount_++; |
michael@0 | 176 | JS_ASSERT(stubCount_); |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | public: |
michael@0 | 180 | |
michael@0 | 181 | IonCache() |
michael@0 | 182 | : pure_(false), |
michael@0 | 183 | idempotent_(false), |
michael@0 | 184 | disabled_(false), |
michael@0 | 185 | stubCount_(0), |
michael@0 | 186 | fallbackLabel_(), |
michael@0 | 187 | script_(nullptr), |
michael@0 | 188 | pc_(nullptr) |
michael@0 | 189 | { |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | virtual void disable(); |
michael@0 | 193 | inline bool isDisabled() const { |
michael@0 | 194 | return disabled_; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is |
michael@0 | 198 | // the location of the out-of-line update (slow) path. This location will |
michael@0 | 199 | // be set to the exitJump of the last generated stub. |
michael@0 | 200 | void setFallbackLabel(CodeOffsetLabel fallbackLabel) { |
michael@0 | 201 | fallbackLabel_ = fallbackLabel; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | virtual void emitInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; |
michael@0 | 205 | virtual void bindInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; |
michael@0 | 206 | virtual void updateBaseAddress(JitCode *code, MacroAssembler &masm); |
michael@0 | 207 | |
michael@0 | 208 | // Initialize the AddCacheState depending on the kind of cache, like |
michael@0 | 209 | // setting a scratch register. Defaults to doing nothing. |
michael@0 | 210 | virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 211 | |
michael@0 | 212 | // Reset the cache around garbage collection. |
michael@0 | 213 | virtual void reset(); |
michael@0 | 214 | |
michael@0 | 215 | // Destroy any extra resources the cache uses upon IonScript finalization. |
michael@0 | 216 | virtual void destroy(); |
michael@0 | 217 | |
michael@0 | 218 | bool canAttachStub() const { |
michael@0 | 219 | return stubCount_ < MAX_STUBS; |
michael@0 | 220 | } |
michael@0 | 221 | bool empty() const { |
michael@0 | 222 | return stubCount_ == 0; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | enum LinkStatus { |
michael@0 | 226 | LINK_ERROR, |
michael@0 | 227 | CACHE_FLUSHED, |
michael@0 | 228 | LINK_GOOD |
michael@0 | 229 | }; |
michael@0 | 230 | |
michael@0 | 231 | // Use the Linker to link the generated code and check if any |
michael@0 | 232 | // monitoring/allocation caused an invalidation of the running ion script, |
michael@0 | 233 | // this function returns CACHE_FLUSHED. In case of allocation issue this |
michael@0 | 234 | // function returns LINK_ERROR. |
michael@0 | 235 | LinkStatus linkCode(JSContext *cx, MacroAssembler &masm, IonScript *ion, JitCode **code); |
michael@0 | 236 | // Fixup variables and update jumps in the list of stubs. Increment the |
michael@0 | 237 | // number of attached stubs accordingly. |
michael@0 | 238 | void attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle<JitCode *> code); |
michael@0 | 239 | |
michael@0 | 240 | // Combine both linkStub and attachStub into one function. In addition, it |
michael@0 | 241 | // produces a spew augmented with the attachKind string. |
michael@0 | 242 | bool linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher, |
michael@0 | 243 | IonScript *ion, const char *attachKind); |
michael@0 | 244 | |
michael@0 | 245 | #ifdef DEBUG |
michael@0 | 246 | bool isAllocated() { |
michael@0 | 247 | return fallbackLabel_.isSet(); |
michael@0 | 248 | } |
michael@0 | 249 | #endif |
michael@0 | 250 | |
michael@0 | 251 | bool pure() const { |
michael@0 | 252 | return pure_; |
michael@0 | 253 | } |
michael@0 | 254 | bool idempotent() const { |
michael@0 | 255 | return idempotent_; |
michael@0 | 256 | } |
michael@0 | 257 | void setIdempotent() { |
michael@0 | 258 | JS_ASSERT(!idempotent_); |
michael@0 | 259 | JS_ASSERT(!script_); |
michael@0 | 260 | JS_ASSERT(!pc_); |
michael@0 | 261 | idempotent_ = true; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | void setScriptedLocation(JSScript *script, jsbytecode *pc) { |
michael@0 | 265 | JS_ASSERT(!idempotent_); |
michael@0 | 266 | script_ = script; |
michael@0 | 267 | pc_ = pc; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | void getScriptedLocation(MutableHandleScript pscript, jsbytecode **ppc) const { |
michael@0 | 271 | pscript.set(script_); |
michael@0 | 272 | *ppc = pc_; |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | jsbytecode *pc() const { |
michael@0 | 276 | JS_ASSERT(pc_); |
michael@0 | 277 | return pc_; |
michael@0 | 278 | } |
michael@0 | 279 | }; |
michael@0 | 280 | |
michael@0 | 281 | // |
michael@0 | 282 | // Repatch caches initially generate a patchable jump to an out of line call |
michael@0 | 283 | // to the cache function. Stubs are attached by appending: when attaching a |
michael@0 | 284 | // new stub, we patch the any failure conditions in last generated stub to |
michael@0 | 285 | // jump to the new stub. Failure conditions in the new stub jump to the cache |
michael@0 | 286 | // function which may generate new stubs. |
michael@0 | 287 | // |
michael@0 | 288 | // Control flow Pointers |
michael@0 | 289 | // =======# ----. .----> |
michael@0 | 290 | // # | | |
michael@0 | 291 | // #======> \-----/ |
michael@0 | 292 | // |
michael@0 | 293 | // Initial state: |
michael@0 | 294 | // |
michael@0 | 295 | // JIT Code |
michael@0 | 296 | // +--------+ .---------------. |
michael@0 | 297 | // | | | | |
michael@0 | 298 | // |========| v +----------+ | |
michael@0 | 299 | // |== IC ==|====>| Cache Fn | | |
michael@0 | 300 | // |========| +----------+ | |
michael@0 | 301 | // | |<=# # | |
michael@0 | 302 | // | | #=======# | |
michael@0 | 303 | // +--------+ Rejoin path | |
michael@0 | 304 | // |________ | |
michael@0 | 305 | // | | |
michael@0 | 306 | // Repatch | | |
michael@0 | 307 | // IC | | |
michael@0 | 308 | // Entry | | |
michael@0 | 309 | // +------------+ | |
michael@0 | 310 | // | lastJump_ |---------------/ |
michael@0 | 311 | // +------------+ |
michael@0 | 312 | // | ... | |
michael@0 | 313 | // +------------+ |
michael@0 | 314 | // |
michael@0 | 315 | // Attaching stubs: |
michael@0 | 316 | // |
michael@0 | 317 | // Patch the jump pointed to by lastJump_ to jump to the new stub. Update |
michael@0 | 318 | // lastJump_ to be the new stub's failure jump. The failure jump of the new |
michael@0 | 319 | // stub goes to the fallback label, which is the cache function. In this |
michael@0 | 320 | // fashion, new stubs are _appended_ to the chain of stubs, as lastJump_ |
michael@0 | 321 | // points to the _tail_ of the stub chain. |
michael@0 | 322 | // |
michael@0 | 323 | // JIT Code |
michael@0 | 324 | // +--------+ #=======================# |
michael@0 | 325 | // | | # v |
michael@0 | 326 | // |========| # +----------+ +------+ |
michael@0 | 327 | // |== IC ==|=# | Cache Fn |<====| Stub | |
michael@0 | 328 | // |========| +----------+ ^ +------+ |
michael@0 | 329 | // | |<=# # | # |
michael@0 | 330 | // | | #======#=========|=====# |
michael@0 | 331 | // +--------+ Rejoin path | |
michael@0 | 332 | // |________ | |
michael@0 | 333 | // | | |
michael@0 | 334 | // Repatch | | |
michael@0 | 335 | // IC | | |
michael@0 | 336 | // Entry | | |
michael@0 | 337 | // +------------+ | |
michael@0 | 338 | // | lastJump_ |---------------/ |
michael@0 | 339 | // +------------+ |
michael@0 | 340 | // | ... | |
michael@0 | 341 | // +------------+ |
michael@0 | 342 | // |
michael@0 | 343 | class RepatchIonCache : public IonCache |
michael@0 | 344 | { |
michael@0 | 345 | protected: |
michael@0 | 346 | class RepatchStubAppender; |
michael@0 | 347 | |
michael@0 | 348 | CodeLocationJump initialJump_; |
michael@0 | 349 | CodeLocationJump lastJump_; |
michael@0 | 350 | |
michael@0 | 351 | // Offset from the initial jump to the rejoin label. |
michael@0 | 352 | #ifdef JS_CODEGEN_ARM |
michael@0 | 353 | static const size_t REJOIN_LABEL_OFFSET = 4; |
michael@0 | 354 | #elif defined(JS_CODEGEN_MIPS) |
michael@0 | 355 | // The size of jump created by MacroAssemblerMIPSCompat::jumpWithPatch. |
michael@0 | 356 | static const size_t REJOIN_LABEL_OFFSET = 4 * sizeof(void *); |
michael@0 | 357 | #else |
michael@0 | 358 | static const size_t REJOIN_LABEL_OFFSET = 0; |
michael@0 | 359 | #endif |
michael@0 | 360 | |
michael@0 | 361 | CodeLocationLabel rejoinLabel() const { |
michael@0 | 362 | uint8_t *ptr = initialJump_.raw(); |
michael@0 | 363 | #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) |
michael@0 | 364 | uint32_t i = 0; |
michael@0 | 365 | while (i < REJOIN_LABEL_OFFSET) |
michael@0 | 366 | ptr = Assembler::nextInstruction(ptr, &i); |
michael@0 | 367 | #endif |
michael@0 | 368 | return CodeLocationLabel(ptr); |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | public: |
michael@0 | 372 | RepatchIonCache() |
michael@0 | 373 | : initialJump_(), |
michael@0 | 374 | lastJump_() |
michael@0 | 375 | { |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | virtual void reset(); |
michael@0 | 379 | |
michael@0 | 380 | // Set the initial jump state of the cache. The initialJump is the inline |
michael@0 | 381 | // jump that will point to out-of-line code (such as the slow path, or |
michael@0 | 382 | // stubs), and the rejoinLabel is the position that all out-of-line paths |
michael@0 | 383 | // will rejoin to. |
michael@0 | 384 | void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); |
michael@0 | 385 | void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); |
michael@0 | 386 | |
michael@0 | 387 | // Update the labels once the code is finalized. |
michael@0 | 388 | void updateBaseAddress(JitCode *code, MacroAssembler &masm); |
michael@0 | 389 | }; |
michael@0 | 390 | |
michael@0 | 391 | // |
michael@0 | 392 | // Dispatch caches avoid patching already-running code. Instead, the jump to |
michael@0 | 393 | // the stub chain is indirect by way of the firstStub_ pointer |
michael@0 | 394 | // below. Initially the pointer points to the cache function which may attach |
michael@0 | 395 | // new stubs. Stubs are attached by prepending: when attaching a new stub, we |
michael@0 | 396 | // jump to the previous stub on failure conditions, then overwrite the |
michael@0 | 397 | // firstStub_ pointer with the newly generated stub. |
michael@0 | 398 | // |
michael@0 | 399 | // This style does not patch the already executing instruction stream, does |
michael@0 | 400 | // not need to worry about cache coherence of cached jump addresses, and does |
michael@0 | 401 | // not have to worry about aligning the exit jumps to ensure atomic patching, |
michael@0 | 402 | // at the expense of an extra memory read to load the very first stub. |
michael@0 | 403 | // |
michael@0 | 404 | // ICs that need to work in parallel execution need to be dispatch style. |
michael@0 | 405 | // |
michael@0 | 406 | // Control flow Pointers Memory load |
michael@0 | 407 | // =======# ----. .----> ****** |
michael@0 | 408 | // # | | * |
michael@0 | 409 | // #======> \-----/ ******* |
michael@0 | 410 | // |
michael@0 | 411 | // Initial state: |
michael@0 | 412 | // |
michael@0 | 413 | // The first stub points to the cache function. |
michael@0 | 414 | // |
michael@0 | 415 | // JIT Code |
michael@0 | 416 | // +--------+ .-------. |
michael@0 | 417 | // | | v | |
michael@0 | 418 | // |========| +---------------+ +----------+ | |
michael@0 | 419 | // |== IC ==|====>| Load and jump |====>| Cache Fn | | |
michael@0 | 420 | // |========| +---------------+ +----------+ | |
michael@0 | 421 | // | |<=# * # | |
michael@0 | 422 | // | | #===========*================# | |
michael@0 | 423 | // +--------+ Rejoin * path | |
michael@0 | 424 | // |________ * | |
michael@0 | 425 | // | * | |
michael@0 | 426 | // Dispatch | * | |
michael@0 | 427 | // IC **|************ | |
michael@0 | 428 | // Entry * | | |
michael@0 | 429 | // +------------+ | |
michael@0 | 430 | // | firstStub_ |-------------------------------------/ |
michael@0 | 431 | // +------------+ |
michael@0 | 432 | // | ... | |
michael@0 | 433 | // +------------+ |
michael@0 | 434 | // |
michael@0 | 435 | // Attaching stubs: |
michael@0 | 436 | // |
michael@0 | 437 | // Assign the address of the new stub to firstStub_. The new stub jumps to |
michael@0 | 438 | // the old address held in firstStub_ on failure. Note that there is no |
michael@0 | 439 | // concept of a fallback label here, new stubs are _prepended_, as |
michael@0 | 440 | // firstStub_ always points to the _head_ of the stub chain. |
michael@0 | 441 | // |
michael@0 | 442 | // JIT Code |
michael@0 | 443 | // +--------+ #=====================# .-----. |
michael@0 | 444 | // | | # v v | |
michael@0 | 445 | // |========| +---------------+ # +----------+ +------+ | |
michael@0 | 446 | // |== IC ==|====>| Load and jump |==# | Cache Fn |<====| Stub | | |
michael@0 | 447 | // |========| +---------------+ +----------+ +------+ | |
michael@0 | 448 | // | |<=# * # # | |
michael@0 | 449 | // | | #===========*================#================# | |
michael@0 | 450 | // +--------+ Rejoin * path | |
michael@0 | 451 | // |________ * | |
michael@0 | 452 | // | * | |
michael@0 | 453 | // Dispatch | * | |
michael@0 | 454 | // IC **|************ | |
michael@0 | 455 | // Entry * | | |
michael@0 | 456 | // +------------+ | |
michael@0 | 457 | // | firstStub_ |----------------------------------------------------/ |
michael@0 | 458 | // +------------+ |
michael@0 | 459 | // | ... | |
michael@0 | 460 | // +------------+ |
michael@0 | 461 | // |
michael@0 | 462 | class DispatchIonCache : public IonCache |
michael@0 | 463 | { |
michael@0 | 464 | protected: |
michael@0 | 465 | class DispatchStubPrepender; |
michael@0 | 466 | |
michael@0 | 467 | uint8_t *firstStub_; |
michael@0 | 468 | CodeLocationLabel rejoinLabel_; |
michael@0 | 469 | CodeOffsetLabel dispatchLabel_; |
michael@0 | 470 | |
michael@0 | 471 | public: |
michael@0 | 472 | DispatchIonCache() |
michael@0 | 473 | : firstStub_(nullptr), |
michael@0 | 474 | rejoinLabel_(), |
michael@0 | 475 | dispatchLabel_() |
michael@0 | 476 | { |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | virtual void reset(); |
michael@0 | 480 | virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 481 | |
michael@0 | 482 | void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); |
michael@0 | 483 | void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); |
michael@0 | 484 | |
michael@0 | 485 | // Fix up the first stub pointer once the code is finalized. |
michael@0 | 486 | void updateBaseAddress(JitCode *code, MacroAssembler &masm); |
michael@0 | 487 | }; |
michael@0 | 488 | |
michael@0 | 489 | // Define the cache kind and pre-declare data structures used for calling inline |
michael@0 | 490 | // caches. |
michael@0 | 491 | #define CACHE_HEADER(ickind) \ |
michael@0 | 492 | Kind kind() const { \ |
michael@0 | 493 | return IonCache::Cache_##ickind; \ |
michael@0 | 494 | } \ |
michael@0 | 495 | \ |
michael@0 | 496 | bool accept(CodeGenerator *codegen, IonCacheVisitor *visitor) { \ |
michael@0 | 497 | return visitor->visit##ickind##IC(codegen); \ |
michael@0 | 498 | } \ |
michael@0 | 499 | \ |
michael@0 | 500 | static const VMFunction UpdateInfo; |
michael@0 | 501 | |
michael@0 | 502 | // Subclasses of IonCache for the various kinds of caches. These do not define |
michael@0 | 503 | // new data members; all caches must be of the same size. |
michael@0 | 504 | |
michael@0 | 505 | // Helper for idempotent GetPropertyIC location tracking. Declared externally |
michael@0 | 506 | // to be forward declarable. |
michael@0 | 507 | // |
michael@0 | 508 | // Since all the scripts stored in CacheLocations are guaranteed to have been |
michael@0 | 509 | // Ion compiled, and are kept alive by function objects in jitcode, and since |
michael@0 | 510 | // the CacheLocations only have the lifespan of the jitcode, there is no need |
michael@0 | 511 | // to trace or mark any of the scripts. Since JSScripts are always allocated |
michael@0 | 512 | // tenured, and never moved, we can keep raw pointers, and there is no need |
michael@0 | 513 | // for HeapPtrScripts here. |
michael@0 | 514 | struct CacheLocation { |
michael@0 | 515 | jsbytecode *pc; |
michael@0 | 516 | JSScript *script; |
michael@0 | 517 | |
michael@0 | 518 | CacheLocation(jsbytecode *pcin, JSScript *scriptin) |
michael@0 | 519 | : pc(pcin), script(scriptin) |
michael@0 | 520 | { } |
michael@0 | 521 | }; |
michael@0 | 522 | |
michael@0 | 523 | class GetPropertyIC : public RepatchIonCache |
michael@0 | 524 | { |
michael@0 | 525 | protected: |
michael@0 | 526 | // Registers live after the cache, excluding output registers. The initial |
michael@0 | 527 | // value of these registers must be preserved by the cache. |
michael@0 | 528 | RegisterSet liveRegs_; |
michael@0 | 529 | |
michael@0 | 530 | Register object_; |
michael@0 | 531 | PropertyName *name_; |
michael@0 | 532 | TypedOrValueRegister output_; |
michael@0 | 533 | |
michael@0 | 534 | // Only valid if idempotent |
michael@0 | 535 | size_t locationsIndex_; |
michael@0 | 536 | size_t numLocations_; |
michael@0 | 537 | |
michael@0 | 538 | bool monitoredResult_ : 1; |
michael@0 | 539 | bool hasTypedArrayLengthStub_ : 1; |
michael@0 | 540 | bool hasStrictArgumentsLengthStub_ : 1; |
michael@0 | 541 | bool hasNormalArgumentsLengthStub_ : 1; |
michael@0 | 542 | bool hasGenericProxyStub_ : 1; |
michael@0 | 543 | |
michael@0 | 544 | public: |
michael@0 | 545 | GetPropertyIC(RegisterSet liveRegs, |
michael@0 | 546 | Register object, PropertyName *name, |
michael@0 | 547 | TypedOrValueRegister output, |
michael@0 | 548 | bool monitoredResult) |
michael@0 | 549 | : liveRegs_(liveRegs), |
michael@0 | 550 | object_(object), |
michael@0 | 551 | name_(name), |
michael@0 | 552 | output_(output), |
michael@0 | 553 | locationsIndex_(0), |
michael@0 | 554 | numLocations_(0), |
michael@0 | 555 | monitoredResult_(monitoredResult), |
michael@0 | 556 | hasTypedArrayLengthStub_(false), |
michael@0 | 557 | hasStrictArgumentsLengthStub_(false), |
michael@0 | 558 | hasNormalArgumentsLengthStub_(false), |
michael@0 | 559 | hasGenericProxyStub_(false) |
michael@0 | 560 | { |
michael@0 | 561 | } |
michael@0 | 562 | |
michael@0 | 563 | CACHE_HEADER(GetProperty) |
michael@0 | 564 | |
michael@0 | 565 | void reset(); |
michael@0 | 566 | |
michael@0 | 567 | Register object() const { |
michael@0 | 568 | return object_; |
michael@0 | 569 | } |
michael@0 | 570 | PropertyName *name() const { |
michael@0 | 571 | return name_; |
michael@0 | 572 | } |
michael@0 | 573 | TypedOrValueRegister output() const { |
michael@0 | 574 | return output_; |
michael@0 | 575 | } |
michael@0 | 576 | bool monitoredResult() const { |
michael@0 | 577 | return monitoredResult_; |
michael@0 | 578 | } |
michael@0 | 579 | bool hasTypedArrayLengthStub() const { |
michael@0 | 580 | return hasTypedArrayLengthStub_; |
michael@0 | 581 | } |
michael@0 | 582 | bool hasArgumentsLengthStub(bool strict) const { |
michael@0 | 583 | return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_; |
michael@0 | 584 | } |
michael@0 | 585 | bool hasGenericProxyStub() const { |
michael@0 | 586 | return hasGenericProxyStub_; |
michael@0 | 587 | } |
michael@0 | 588 | |
michael@0 | 589 | void setLocationInfo(size_t locationsIndex, size_t numLocations) { |
michael@0 | 590 | JS_ASSERT(idempotent()); |
michael@0 | 591 | JS_ASSERT(!numLocations_); |
michael@0 | 592 | JS_ASSERT(numLocations); |
michael@0 | 593 | locationsIndex_ = locationsIndex; |
michael@0 | 594 | numLocations_ = numLocations; |
michael@0 | 595 | } |
michael@0 | 596 | void getLocationInfo(uint32_t *index, uint32_t *num) const { |
michael@0 | 597 | JS_ASSERT(idempotent()); |
michael@0 | 598 | *index = locationsIndex_; |
michael@0 | 599 | *num = numLocations_; |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | enum NativeGetPropCacheability { |
michael@0 | 603 | CanAttachNone, |
michael@0 | 604 | CanAttachReadSlot, |
michael@0 | 605 | CanAttachArrayLength, |
michael@0 | 606 | CanAttachCallGetter |
michael@0 | 607 | }; |
michael@0 | 608 | |
michael@0 | 609 | // Helpers for CanAttachNativeGetProp |
michael@0 | 610 | typedef JSContext * Context; |
michael@0 | 611 | bool allowArrayLength(Context cx, HandleObject obj) const; |
michael@0 | 612 | bool allowGetters() const { |
michael@0 | 613 | return monitoredResult() && !idempotent(); |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | // Attach the proper stub, if possible |
michael@0 | 617 | bool tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 618 | HandlePropertyName name, void *returnAddr, bool *emitted); |
michael@0 | 619 | bool tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 620 | HandlePropertyName name, void *returnAddr, bool *emitted); |
michael@0 | 621 | bool tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 622 | HandlePropertyName name, void *returnAddr, bool *emitted); |
michael@0 | 623 | bool tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 624 | void *returnAddr, bool *emitted); |
michael@0 | 625 | bool tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 626 | HandlePropertyName name, bool resetNeeded, |
michael@0 | 627 | void *returnAddr, bool *emitted); |
michael@0 | 628 | bool tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 629 | HandlePropertyName name, void *returnAddr, bool *emitted); |
michael@0 | 630 | bool tryAttachTypedArrayLength(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 631 | HandlePropertyName name, bool *emitted); |
michael@0 | 632 | |
michael@0 | 633 | bool tryAttachArgumentsLength(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 634 | HandlePropertyName name, bool *emitted); |
michael@0 | 635 | |
michael@0 | 636 | static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp); |
michael@0 | 637 | }; |
michael@0 | 638 | |
michael@0 | 639 | class SetPropertyIC : public RepatchIonCache |
michael@0 | 640 | { |
michael@0 | 641 | protected: |
michael@0 | 642 | // Registers live after the cache, excluding output registers. The initial |
michael@0 | 643 | // value of these registers must be preserved by the cache. |
michael@0 | 644 | RegisterSet liveRegs_; |
michael@0 | 645 | |
michael@0 | 646 | Register object_; |
michael@0 | 647 | PropertyName *name_; |
michael@0 | 648 | ConstantOrRegister value_; |
michael@0 | 649 | bool strict_; |
michael@0 | 650 | bool needsTypeBarrier_; |
michael@0 | 651 | |
michael@0 | 652 | bool hasGenericProxyStub_; |
michael@0 | 653 | |
michael@0 | 654 | public: |
michael@0 | 655 | SetPropertyIC(RegisterSet liveRegs, Register object, PropertyName *name, |
michael@0 | 656 | ConstantOrRegister value, bool strict, bool needsTypeBarrier) |
michael@0 | 657 | : liveRegs_(liveRegs), |
michael@0 | 658 | object_(object), |
michael@0 | 659 | name_(name), |
michael@0 | 660 | value_(value), |
michael@0 | 661 | strict_(strict), |
michael@0 | 662 | needsTypeBarrier_(needsTypeBarrier), |
michael@0 | 663 | hasGenericProxyStub_(false) |
michael@0 | 664 | { |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | CACHE_HEADER(SetProperty) |
michael@0 | 668 | |
michael@0 | 669 | void reset(); |
michael@0 | 670 | |
michael@0 | 671 | Register object() const { |
michael@0 | 672 | return object_; |
michael@0 | 673 | } |
michael@0 | 674 | PropertyName *name() const { |
michael@0 | 675 | return name_; |
michael@0 | 676 | } |
michael@0 | 677 | ConstantOrRegister value() const { |
michael@0 | 678 | return value_; |
michael@0 | 679 | } |
michael@0 | 680 | bool strict() const { |
michael@0 | 681 | return strict_; |
michael@0 | 682 | } |
michael@0 | 683 | bool needsTypeBarrier() const { |
michael@0 | 684 | return needsTypeBarrier_; |
michael@0 | 685 | } |
michael@0 | 686 | bool hasGenericProxyStub() const { |
michael@0 | 687 | return hasGenericProxyStub_; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | enum NativeSetPropCacheability { |
michael@0 | 691 | CanAttachNone, |
michael@0 | 692 | CanAttachSetSlot, |
michael@0 | 693 | MaybeCanAttachAddSlot, |
michael@0 | 694 | CanAttachCallSetter |
michael@0 | 695 | }; |
michael@0 | 696 | |
michael@0 | 697 | bool attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape, |
michael@0 | 698 | bool checkTypeset); |
michael@0 | 699 | bool attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 700 | HandleObject holder, HandleShape shape, void *returnAddr); |
michael@0 | 701 | bool attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape, |
michael@0 | 702 | bool checkTypeset); |
michael@0 | 703 | bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr); |
michael@0 | 704 | bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 705 | void *returnAddr); |
michael@0 | 706 | bool attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj, |
michael@0 | 707 | void *returnAddr); |
michael@0 | 708 | |
michael@0 | 709 | static bool |
michael@0 | 710 | update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value); |
michael@0 | 711 | }; |
michael@0 | 712 | |
michael@0 | 713 | class GetElementIC : public RepatchIonCache |
michael@0 | 714 | { |
michael@0 | 715 | protected: |
michael@0 | 716 | RegisterSet liveRegs_; |
michael@0 | 717 | |
michael@0 | 718 | Register object_; |
michael@0 | 719 | ConstantOrRegister index_; |
michael@0 | 720 | TypedOrValueRegister output_; |
michael@0 | 721 | |
michael@0 | 722 | bool monitoredResult_ : 1; |
michael@0 | 723 | bool allowDoubleResult_ : 1; |
michael@0 | 724 | bool hasDenseStub_ : 1; |
michael@0 | 725 | bool hasStrictArgumentsStub_ : 1; |
michael@0 | 726 | bool hasNormalArgumentsStub_ : 1; |
michael@0 | 727 | |
michael@0 | 728 | size_t failedUpdates_; |
michael@0 | 729 | |
michael@0 | 730 | static const size_t MAX_FAILED_UPDATES; |
michael@0 | 731 | |
michael@0 | 732 | public: |
michael@0 | 733 | GetElementIC(RegisterSet liveRegs, Register object, ConstantOrRegister index, |
michael@0 | 734 | TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult) |
michael@0 | 735 | : liveRegs_(liveRegs), |
michael@0 | 736 | object_(object), |
michael@0 | 737 | index_(index), |
michael@0 | 738 | output_(output), |
michael@0 | 739 | monitoredResult_(monitoredResult), |
michael@0 | 740 | allowDoubleResult_(allowDoubleResult), |
michael@0 | 741 | hasDenseStub_(false), |
michael@0 | 742 | hasStrictArgumentsStub_(false), |
michael@0 | 743 | hasNormalArgumentsStub_(false), |
michael@0 | 744 | failedUpdates_(0) |
michael@0 | 745 | { |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | CACHE_HEADER(GetElement) |
michael@0 | 749 | |
michael@0 | 750 | void reset(); |
michael@0 | 751 | |
michael@0 | 752 | Register object() const { |
michael@0 | 753 | return object_; |
michael@0 | 754 | } |
michael@0 | 755 | ConstantOrRegister index() const { |
michael@0 | 756 | return index_; |
michael@0 | 757 | } |
michael@0 | 758 | TypedOrValueRegister output() const { |
michael@0 | 759 | return output_; |
michael@0 | 760 | } |
michael@0 | 761 | bool monitoredResult() const { |
michael@0 | 762 | return monitoredResult_; |
michael@0 | 763 | } |
michael@0 | 764 | bool allowDoubleResult() const { |
michael@0 | 765 | return allowDoubleResult_; |
michael@0 | 766 | } |
michael@0 | 767 | bool hasDenseStub() const { |
michael@0 | 768 | return hasDenseStub_; |
michael@0 | 769 | } |
michael@0 | 770 | bool hasArgumentsStub(bool strict) const { |
michael@0 | 771 | return strict ? hasStrictArgumentsStub_ : hasNormalArgumentsStub_; |
michael@0 | 772 | } |
michael@0 | 773 | void setHasDenseStub() { |
michael@0 | 774 | JS_ASSERT(!hasDenseStub()); |
michael@0 | 775 | hasDenseStub_ = true; |
michael@0 | 776 | } |
michael@0 | 777 | |
michael@0 | 778 | // Helpers for CanAttachNativeGetProp |
michael@0 | 779 | typedef JSContext * Context; |
michael@0 | 780 | bool allowGetters() const { JS_ASSERT(!idempotent()); return true; } |
michael@0 | 781 | bool allowArrayLength(Context, HandleObject) const { return false; } |
michael@0 | 782 | bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const { |
michael@0 | 783 | return monitoredResult(); |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id); |
michael@0 | 787 | static bool canAttachDenseElement(JSObject *obj, const Value &idval); |
michael@0 | 788 | static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval, |
michael@0 | 789 | TypedOrValueRegister output); |
michael@0 | 790 | |
michael@0 | 791 | bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, |
michael@0 | 792 | HandlePropertyName name, void *returnAddr); |
michael@0 | 793 | bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); |
michael@0 | 794 | bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr, |
michael@0 | 795 | const Value &idval); |
michael@0 | 796 | bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj); |
michael@0 | 797 | |
michael@0 | 798 | static bool |
michael@0 | 799 | update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, |
michael@0 | 800 | MutableHandleValue vp); |
michael@0 | 801 | |
michael@0 | 802 | void incFailedUpdates() { |
michael@0 | 803 | failedUpdates_++; |
michael@0 | 804 | } |
michael@0 | 805 | void resetFailedUpdates() { |
michael@0 | 806 | failedUpdates_ = 0; |
michael@0 | 807 | } |
michael@0 | 808 | bool shouldDisable() const { |
michael@0 | 809 | return !canAttachStub() || |
michael@0 | 810 | (stubCount_ == 0 && failedUpdates_ > MAX_FAILED_UPDATES); |
michael@0 | 811 | } |
michael@0 | 812 | }; |
michael@0 | 813 | |
michael@0 | 814 | class SetElementIC : public RepatchIonCache |
michael@0 | 815 | { |
michael@0 | 816 | protected: |
michael@0 | 817 | Register object_; |
michael@0 | 818 | Register tempToUnboxIndex_; |
michael@0 | 819 | Register temp_; |
michael@0 | 820 | FloatRegister tempFloat_; |
michael@0 | 821 | ValueOperand index_; |
michael@0 | 822 | ConstantOrRegister value_; |
michael@0 | 823 | bool strict_; |
michael@0 | 824 | bool guardHoles_; |
michael@0 | 825 | |
michael@0 | 826 | bool hasDenseStub_ : 1; |
michael@0 | 827 | |
michael@0 | 828 | public: |
michael@0 | 829 | SetElementIC(Register object, Register tempToUnboxIndex, Register temp, |
michael@0 | 830 | FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, |
michael@0 | 831 | bool strict, bool guardHoles) |
michael@0 | 832 | : object_(object), |
michael@0 | 833 | tempToUnboxIndex_(tempToUnboxIndex), |
michael@0 | 834 | temp_(temp), |
michael@0 | 835 | tempFloat_(tempFloat), |
michael@0 | 836 | index_(index), |
michael@0 | 837 | value_(value), |
michael@0 | 838 | strict_(strict), |
michael@0 | 839 | guardHoles_(guardHoles), |
michael@0 | 840 | hasDenseStub_(false) |
michael@0 | 841 | { |
michael@0 | 842 | } |
michael@0 | 843 | |
michael@0 | 844 | CACHE_HEADER(SetElement) |
michael@0 | 845 | |
michael@0 | 846 | void reset(); |
michael@0 | 847 | |
michael@0 | 848 | Register object() const { |
michael@0 | 849 | return object_; |
michael@0 | 850 | } |
michael@0 | 851 | Register tempToUnboxIndex() const { |
michael@0 | 852 | return tempToUnboxIndex_; |
michael@0 | 853 | } |
michael@0 | 854 | Register temp() const { |
michael@0 | 855 | return temp_; |
michael@0 | 856 | } |
michael@0 | 857 | FloatRegister tempFloat() const { |
michael@0 | 858 | return tempFloat_; |
michael@0 | 859 | } |
michael@0 | 860 | ValueOperand index() const { |
michael@0 | 861 | return index_; |
michael@0 | 862 | } |
michael@0 | 863 | ConstantOrRegister value() const { |
michael@0 | 864 | return value_; |
michael@0 | 865 | } |
michael@0 | 866 | bool strict() const { |
michael@0 | 867 | return strict_; |
michael@0 | 868 | } |
michael@0 | 869 | bool guardHoles() const { |
michael@0 | 870 | return guardHoles_; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | bool hasDenseStub() const { |
michael@0 | 874 | return hasDenseStub_; |
michael@0 | 875 | } |
michael@0 | 876 | void setHasDenseStub() { |
michael@0 | 877 | JS_ASSERT(!hasDenseStub()); |
michael@0 | 878 | hasDenseStub_ = true; |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); |
michael@0 | 882 | bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr); |
michael@0 | 883 | |
michael@0 | 884 | static bool |
michael@0 | 885 | update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, |
michael@0 | 886 | HandleValue value); |
michael@0 | 887 | }; |
michael@0 | 888 | |
michael@0 | 889 | class BindNameIC : public RepatchIonCache |
michael@0 | 890 | { |
michael@0 | 891 | protected: |
michael@0 | 892 | Register scopeChain_; |
michael@0 | 893 | PropertyName *name_; |
michael@0 | 894 | Register output_; |
michael@0 | 895 | |
michael@0 | 896 | public: |
michael@0 | 897 | BindNameIC(Register scopeChain, PropertyName *name, Register output) |
michael@0 | 898 | : scopeChain_(scopeChain), |
michael@0 | 899 | name_(name), |
michael@0 | 900 | output_(output) |
michael@0 | 901 | { |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | CACHE_HEADER(BindName) |
michael@0 | 905 | |
michael@0 | 906 | Register scopeChainReg() const { |
michael@0 | 907 | return scopeChain_; |
michael@0 | 908 | } |
michael@0 | 909 | HandlePropertyName name() const { |
michael@0 | 910 | return HandlePropertyName::fromMarkedLocation(&name_); |
michael@0 | 911 | } |
michael@0 | 912 | Register outputReg() const { |
michael@0 | 913 | return output_; |
michael@0 | 914 | } |
michael@0 | 915 | |
michael@0 | 916 | bool attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain); |
michael@0 | 917 | bool attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder); |
michael@0 | 918 | |
michael@0 | 919 | static JSObject * |
michael@0 | 920 | update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain); |
michael@0 | 921 | }; |
michael@0 | 922 | |
michael@0 | 923 | class NameIC : public RepatchIonCache |
michael@0 | 924 | { |
michael@0 | 925 | protected: |
michael@0 | 926 | // Registers live after the cache, excluding output registers. The initial |
michael@0 | 927 | // value of these registers must be preserved by the cache. |
michael@0 | 928 | RegisterSet liveRegs_; |
michael@0 | 929 | |
michael@0 | 930 | bool typeOf_; |
michael@0 | 931 | Register scopeChain_; |
michael@0 | 932 | PropertyName *name_; |
michael@0 | 933 | TypedOrValueRegister output_; |
michael@0 | 934 | |
michael@0 | 935 | public: |
michael@0 | 936 | NameIC(RegisterSet liveRegs, bool typeOf, |
michael@0 | 937 | Register scopeChain, PropertyName *name, |
michael@0 | 938 | TypedOrValueRegister output) |
michael@0 | 939 | : liveRegs_(liveRegs), |
michael@0 | 940 | typeOf_(typeOf), |
michael@0 | 941 | scopeChain_(scopeChain), |
michael@0 | 942 | name_(name), |
michael@0 | 943 | output_(output) |
michael@0 | 944 | { |
michael@0 | 945 | } |
michael@0 | 946 | |
michael@0 | 947 | CACHE_HEADER(Name) |
michael@0 | 948 | |
michael@0 | 949 | Register scopeChainReg() const { |
michael@0 | 950 | return scopeChain_; |
michael@0 | 951 | } |
michael@0 | 952 | HandlePropertyName name() const { |
michael@0 | 953 | return HandlePropertyName::fromMarkedLocation(&name_); |
michael@0 | 954 | } |
michael@0 | 955 | TypedOrValueRegister outputReg() const { |
michael@0 | 956 | return output_; |
michael@0 | 957 | } |
michael@0 | 958 | bool isTypeOf() const { |
michael@0 | 959 | return typeOf_; |
michael@0 | 960 | } |
michael@0 | 961 | |
michael@0 | 962 | bool attachReadSlot(JSContext *cx, IonScript *ion, HandleObject scopeChain, |
michael@0 | 963 | HandleObject holderBase, HandleObject holder, HandleShape shape); |
michael@0 | 964 | bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, |
michael@0 | 965 | HandleShape shape, void *returnAddr); |
michael@0 | 966 | |
michael@0 | 967 | static bool |
michael@0 | 968 | update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp); |
michael@0 | 969 | }; |
michael@0 | 970 | |
michael@0 | 971 | class CallsiteCloneIC : public RepatchIonCache |
michael@0 | 972 | { |
michael@0 | 973 | protected: |
michael@0 | 974 | Register callee_; |
michael@0 | 975 | Register output_; |
michael@0 | 976 | JSScript *callScript_; |
michael@0 | 977 | jsbytecode *callPc_; |
michael@0 | 978 | |
michael@0 | 979 | public: |
michael@0 | 980 | CallsiteCloneIC(Register callee, JSScript *callScript, jsbytecode *callPc, Register output) |
michael@0 | 981 | : callee_(callee), |
michael@0 | 982 | output_(output), |
michael@0 | 983 | callScript_(callScript), |
michael@0 | 984 | callPc_(callPc) |
michael@0 | 985 | { |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | CACHE_HEADER(CallsiteClone) |
michael@0 | 989 | |
michael@0 | 990 | Register calleeReg() const { |
michael@0 | 991 | return callee_; |
michael@0 | 992 | } |
michael@0 | 993 | HandleScript callScript() const { |
michael@0 | 994 | return HandleScript::fromMarkedLocation(&callScript_); |
michael@0 | 995 | } |
michael@0 | 996 | jsbytecode *callPc() const { |
michael@0 | 997 | return callPc_; |
michael@0 | 998 | } |
michael@0 | 999 | Register outputReg() const { |
michael@0 | 1000 | return output_; |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | bool attach(JSContext *cx, IonScript *ion, HandleFunction original, HandleFunction clone); |
michael@0 | 1004 | |
michael@0 | 1005 | static JSObject *update(JSContext *cx, size_t cacheIndex, HandleObject callee); |
michael@0 | 1006 | }; |
michael@0 | 1007 | |
michael@0 | 1008 | class ParallelIonCache : public DispatchIonCache |
michael@0 | 1009 | { |
michael@0 | 1010 | protected: |
michael@0 | 1011 | // A set of all objects that are stubbed. Used to detect duplicates in |
michael@0 | 1012 | // parallel execution. |
michael@0 | 1013 | ShapeSet *stubbedShapes_; |
michael@0 | 1014 | |
michael@0 | 1015 | ParallelIonCache() |
michael@0 | 1016 | : stubbedShapes_(nullptr) |
michael@0 | 1017 | { |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | bool initStubbedShapes(JSContext *cx); |
michael@0 | 1021 | |
michael@0 | 1022 | public: |
michael@0 | 1023 | void reset(); |
michael@0 | 1024 | void destroy(); |
michael@0 | 1025 | |
michael@0 | 1026 | bool hasOrAddStubbedShape(LockedJSContext &cx, Shape *shape, bool *alreadyStubbed); |
michael@0 | 1027 | }; |
michael@0 | 1028 | |
michael@0 | 1029 | class GetPropertyParIC : public ParallelIonCache |
michael@0 | 1030 | { |
michael@0 | 1031 | protected: |
michael@0 | 1032 | Register object_; |
michael@0 | 1033 | PropertyName *name_; |
michael@0 | 1034 | TypedOrValueRegister output_; |
michael@0 | 1035 | bool hasTypedArrayLengthStub_ : 1; |
michael@0 | 1036 | |
michael@0 | 1037 | public: |
michael@0 | 1038 | GetPropertyParIC(Register object, PropertyName *name, TypedOrValueRegister output) |
michael@0 | 1039 | : object_(object), |
michael@0 | 1040 | name_(name), |
michael@0 | 1041 | output_(output), |
michael@0 | 1042 | hasTypedArrayLengthStub_(false) |
michael@0 | 1043 | { |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | CACHE_HEADER(GetPropertyPar) |
michael@0 | 1047 | |
michael@0 | 1048 | #ifdef JS_CODEGEN_X86 |
michael@0 | 1049 | // x86 lacks a general purpose scratch register for dispatch caches and |
michael@0 | 1050 | // must be given one manually. |
michael@0 | 1051 | void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 1052 | #endif |
michael@0 | 1053 | |
michael@0 | 1054 | void reset(); |
michael@0 | 1055 | |
michael@0 | 1056 | Register object() const { |
michael@0 | 1057 | return object_; |
michael@0 | 1058 | } |
michael@0 | 1059 | PropertyName *name() const { |
michael@0 | 1060 | return name_; |
michael@0 | 1061 | } |
michael@0 | 1062 | TypedOrValueRegister output() const { |
michael@0 | 1063 | return output_; |
michael@0 | 1064 | } |
michael@0 | 1065 | bool hasTypedArrayLengthStub() const { |
michael@0 | 1066 | return hasTypedArrayLengthStub_; |
michael@0 | 1067 | } |
michael@0 | 1068 | |
michael@0 | 1069 | // CanAttachNativeGetProp Helpers |
michael@0 | 1070 | typedef LockedJSContext & Context; |
michael@0 | 1071 | bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; } |
michael@0 | 1072 | bool allowGetters() const { return false; } |
michael@0 | 1073 | bool allowArrayLength(Context, HandleObject) const { return true; } |
michael@0 | 1074 | |
michael@0 | 1075 | bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder, |
michael@0 | 1076 | Shape *shape); |
michael@0 | 1077 | bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); |
michael@0 | 1078 | bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); |
michael@0 | 1079 | |
michael@0 | 1080 | static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, |
michael@0 | 1081 | MutableHandleValue vp); |
michael@0 | 1082 | }; |
michael@0 | 1083 | |
michael@0 | 1084 | class GetElementParIC : public ParallelIonCache |
michael@0 | 1085 | { |
michael@0 | 1086 | protected: |
michael@0 | 1087 | Register object_; |
michael@0 | 1088 | ConstantOrRegister index_; |
michael@0 | 1089 | TypedOrValueRegister output_; |
michael@0 | 1090 | |
michael@0 | 1091 | bool monitoredResult_ : 1; |
michael@0 | 1092 | bool allowDoubleResult_ : 1; |
michael@0 | 1093 | |
michael@0 | 1094 | public: |
michael@0 | 1095 | GetElementParIC(Register object, ConstantOrRegister index, |
michael@0 | 1096 | TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult) |
michael@0 | 1097 | : object_(object), |
michael@0 | 1098 | index_(index), |
michael@0 | 1099 | output_(output), |
michael@0 | 1100 | monitoredResult_(monitoredResult), |
michael@0 | 1101 | allowDoubleResult_(allowDoubleResult) |
michael@0 | 1102 | { |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | CACHE_HEADER(GetElementPar) |
michael@0 | 1106 | |
michael@0 | 1107 | #ifdef JS_CODEGEN_X86 |
michael@0 | 1108 | // x86 lacks a general purpose scratch register for dispatch caches and |
michael@0 | 1109 | // must be given one manually. |
michael@0 | 1110 | void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 1111 | #endif |
michael@0 | 1112 | |
michael@0 | 1113 | Register object() const { |
michael@0 | 1114 | return object_; |
michael@0 | 1115 | } |
michael@0 | 1116 | ConstantOrRegister index() const { |
michael@0 | 1117 | return index_; |
michael@0 | 1118 | } |
michael@0 | 1119 | TypedOrValueRegister output() const { |
michael@0 | 1120 | return output_; |
michael@0 | 1121 | } |
michael@0 | 1122 | bool monitoredResult() const { |
michael@0 | 1123 | return monitoredResult_; |
michael@0 | 1124 | } |
michael@0 | 1125 | bool allowDoubleResult() const { |
michael@0 | 1126 | return allowDoubleResult_; |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | // CanAttachNativeGetProp Helpers |
michael@0 | 1130 | typedef LockedJSContext & Context; |
michael@0 | 1131 | bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; } |
michael@0 | 1132 | bool allowGetters() const { return false; } |
michael@0 | 1133 | bool allowArrayLength(Context, HandleObject) const { return false; } |
michael@0 | 1134 | |
michael@0 | 1135 | bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval, |
michael@0 | 1136 | PropertyName *name, JSObject *holder, Shape *shape); |
michael@0 | 1137 | bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); |
michael@0 | 1138 | bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr, |
michael@0 | 1139 | const Value &idval); |
michael@0 | 1140 | |
michael@0 | 1141 | static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, |
michael@0 | 1142 | MutableHandleValue vp); |
michael@0 | 1143 | |
michael@0 | 1144 | }; |
michael@0 | 1145 | |
michael@0 | 1146 | class SetPropertyParIC : public ParallelIonCache |
michael@0 | 1147 | { |
michael@0 | 1148 | protected: |
michael@0 | 1149 | Register object_; |
michael@0 | 1150 | PropertyName *name_; |
michael@0 | 1151 | ConstantOrRegister value_; |
michael@0 | 1152 | bool strict_; |
michael@0 | 1153 | bool needsTypeBarrier_; |
michael@0 | 1154 | |
michael@0 | 1155 | public: |
michael@0 | 1156 | SetPropertyParIC(Register object, PropertyName *name, ConstantOrRegister value, |
michael@0 | 1157 | bool strict, bool needsTypeBarrier) |
michael@0 | 1158 | : object_(object), |
michael@0 | 1159 | name_(name), |
michael@0 | 1160 | value_(value), |
michael@0 | 1161 | strict_(strict), |
michael@0 | 1162 | needsTypeBarrier_(needsTypeBarrier) |
michael@0 | 1163 | { |
michael@0 | 1164 | } |
michael@0 | 1165 | |
michael@0 | 1166 | CACHE_HEADER(SetPropertyPar) |
michael@0 | 1167 | |
michael@0 | 1168 | #ifdef JS_CODEGEN_X86 |
michael@0 | 1169 | // x86 lacks a general purpose scratch register for dispatch caches and |
michael@0 | 1170 | // must be given one manually. |
michael@0 | 1171 | void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 1172 | #endif |
michael@0 | 1173 | |
michael@0 | 1174 | Register object() const { |
michael@0 | 1175 | return object_; |
michael@0 | 1176 | } |
michael@0 | 1177 | PropertyName *name() const { |
michael@0 | 1178 | return name_; |
michael@0 | 1179 | } |
michael@0 | 1180 | ConstantOrRegister value() const { |
michael@0 | 1181 | return value_; |
michael@0 | 1182 | } |
michael@0 | 1183 | bool strict() const { |
michael@0 | 1184 | return strict_; |
michael@0 | 1185 | } |
michael@0 | 1186 | bool needsTypeBarrier() const { |
michael@0 | 1187 | return needsTypeBarrier_; |
michael@0 | 1188 | } |
michael@0 | 1189 | |
michael@0 | 1190 | bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape, |
michael@0 | 1191 | bool checkTypeset); |
michael@0 | 1192 | bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape, |
michael@0 | 1193 | bool checkTypeset); |
michael@0 | 1194 | |
michael@0 | 1195 | static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, |
michael@0 | 1196 | HandleValue value); |
michael@0 | 1197 | }; |
michael@0 | 1198 | |
michael@0 | 1199 | class SetElementParIC : public ParallelIonCache |
michael@0 | 1200 | { |
michael@0 | 1201 | protected: |
michael@0 | 1202 | Register object_; |
michael@0 | 1203 | Register tempToUnboxIndex_; |
michael@0 | 1204 | Register temp_; |
michael@0 | 1205 | FloatRegister tempFloat_; |
michael@0 | 1206 | ValueOperand index_; |
michael@0 | 1207 | ConstantOrRegister value_; |
michael@0 | 1208 | bool strict_; |
michael@0 | 1209 | bool guardHoles_; |
michael@0 | 1210 | |
michael@0 | 1211 | public: |
michael@0 | 1212 | SetElementParIC(Register object, Register tempToUnboxIndex, Register temp, |
michael@0 | 1213 | FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, |
michael@0 | 1214 | bool strict, bool guardHoles) |
michael@0 | 1215 | : object_(object), |
michael@0 | 1216 | tempToUnboxIndex_(tempToUnboxIndex), |
michael@0 | 1217 | temp_(temp), |
michael@0 | 1218 | tempFloat_(tempFloat), |
michael@0 | 1219 | index_(index), |
michael@0 | 1220 | value_(value), |
michael@0 | 1221 | strict_(strict), |
michael@0 | 1222 | guardHoles_(guardHoles) |
michael@0 | 1223 | { |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | CACHE_HEADER(SetElementPar) |
michael@0 | 1227 | |
michael@0 | 1228 | #ifdef JS_CODEGEN_X86 |
michael@0 | 1229 | // x86 lacks a general purpose scratch register for dispatch caches and |
michael@0 | 1230 | // must be given one manually. |
michael@0 | 1231 | void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
michael@0 | 1232 | #endif |
michael@0 | 1233 | |
michael@0 | 1234 | Register object() const { |
michael@0 | 1235 | return object_; |
michael@0 | 1236 | } |
michael@0 | 1237 | Register tempToUnboxIndex() const { |
michael@0 | 1238 | return tempToUnboxIndex_; |
michael@0 | 1239 | } |
michael@0 | 1240 | Register temp() const { |
michael@0 | 1241 | return temp_; |
michael@0 | 1242 | } |
michael@0 | 1243 | FloatRegister tempFloat() const { |
michael@0 | 1244 | return tempFloat_; |
michael@0 | 1245 | } |
michael@0 | 1246 | ValueOperand index() const { |
michael@0 | 1247 | return index_; |
michael@0 | 1248 | } |
michael@0 | 1249 | ConstantOrRegister value() const { |
michael@0 | 1250 | return value_; |
michael@0 | 1251 | } |
michael@0 | 1252 | bool strict() const { |
michael@0 | 1253 | return strict_; |
michael@0 | 1254 | } |
michael@0 | 1255 | bool guardHoles() const { |
michael@0 | 1256 | return guardHoles_; |
michael@0 | 1257 | } |
michael@0 | 1258 | |
michael@0 | 1259 | bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); |
michael@0 | 1260 | bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr); |
michael@0 | 1261 | |
michael@0 | 1262 | static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, |
michael@0 | 1263 | HandleValue idval, HandleValue value); |
michael@0 | 1264 | }; |
michael@0 | 1265 | |
michael@0 | 1266 | #undef CACHE_HEADER |
michael@0 | 1267 | |
michael@0 | 1268 | // Implement cache casts now that the compiler can see the inheritance. |
michael@0 | 1269 | #define CACHE_CASTS(ickind) \ |
michael@0 | 1270 | ickind##IC &IonCache::to##ickind() \ |
michael@0 | 1271 | { \ |
michael@0 | 1272 | JS_ASSERT(is##ickind()); \ |
michael@0 | 1273 | return *static_cast<ickind##IC *>(this); \ |
michael@0 | 1274 | } \ |
michael@0 | 1275 | const ickind##IC &IonCache::to##ickind() const \ |
michael@0 | 1276 | { \ |
michael@0 | 1277 | JS_ASSERT(is##ickind()); \ |
michael@0 | 1278 | return *static_cast<const ickind##IC *>(this); \ |
michael@0 | 1279 | } |
michael@0 | 1280 | IONCACHE_KIND_LIST(CACHE_CASTS) |
michael@0 | 1281 | #undef OPCODE_CASTS |
michael@0 | 1282 | |
michael@0 | 1283 | } // namespace jit |
michael@0 | 1284 | } // namespace js |
michael@0 | 1285 | |
michael@0 | 1286 | #endif /* jit_IonCaches_h */ |