BaselineIC.h (17946B)
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_BaselineIC_h 8 #define jit_BaselineIC_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 13 #include <stddef.h> 14 #include <stdint.h> 15 16 #include "jit/ICState.h" 17 #include "jit/JitCode.h" 18 #include "jit/shared/Assembler-shared.h" 19 #include "jit/TypeData.h" 20 #include "js/TypeDecls.h" 21 22 class JS_PUBLIC_API JSTracer; 23 24 enum class JSOp : uint8_t; 25 26 namespace js { 27 28 MOZ_COLD void ReportOutOfMemory(JSContext* cx); 29 30 namespace jit { 31 32 class BaselineFrame; 33 class CacheIRStubInfo; 34 class ICScript; 35 class ICStubSpace; 36 37 enum class VMFunctionId; 38 39 // [SMDOC] JIT Inline Caches (ICs) 40 // 41 // Baseline Inline Caches are polymorphic caches that aggressively 42 // share their stub code. 43 // 44 // Every polymorphic site contains a linked list of stubs which are 45 // specific to that site. These stubs are composed of a |StubData| 46 // structure that stores parametrization information (e.g. 47 // the shape pointer for a shape-check-and-property-get stub), any 48 // dynamic information (e.g. warm-up counters), a pointer to the stub code, 49 // and a pointer to the next stub state in the linked list. 50 // 51 // Every BaselineScript keeps an table of |CacheDescriptor| data 52 // structures, which store the following: 53 // A pointer to the first StubData in the cache. 54 // The bytecode PC of the relevant IC. 55 // The machine-code PC where the call to the stubcode returns. 56 // 57 // A diagram: 58 // 59 // Control flow Pointers 60 // =======# ----. .----> 61 // # | | 62 // #======> \-----/ 63 // 64 // 65 // .---------------------------------------. 66 // | .-------------------------. | 67 // | | .----. | | 68 // Baseline | | | | | | 69 // JIT Code 0 ^ 1 ^ 2 ^ | | | 70 // +--------------+ .-->+-----+ +-----+ +-----+ | | | 71 // | | #=|==>| |==>| |==>| FB | | | | 72 // | | # | +-----+ +-----+ +-----+ | | | 73 // | | # | # # # | | | 74 // |==============|==# | # # # | | | 75 // |=== IC =======| | # # # | | | 76 // .->|==============|<===|======#=========#=========# | | | 77 // | | | | | | | 78 // | | | | | | | 79 // | | | | | | | 80 // | | | | v | | 81 // | | | | +---------+ | | 82 // | | | | | Fallback| | | 83 // | | | | | Stub | | | 84 // | | | | | Code | | | 85 // | | | | +---------+ | | 86 // | +--------------+ | | | 87 // | |_______ | +---------+ | | 88 // | | | | Stub |<---/ | 89 // | IC | \--. | Code | | 90 // | Descriptor | | +---------+ | 91 // | Table v | | 92 // | +-----------------+ | +---------+ | 93 // \--| Ins | PC | Stub |----/ | Stub |<-------/ 94 // +-----------------+ | Code | 95 // | ... | +---------+ 96 // +-----------------+ 97 // Shared 98 // Stub Code 99 // 100 101 class ICStub; 102 class ICCacheIRStub; 103 class ICFallbackStub; 104 105 #ifdef JS_JITSPEW 106 void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...) 107 MOZ_FORMAT_PRINTF(3, 4); 108 #else 109 # define FallbackICSpew(...) 110 #endif 111 112 // An entry in the ICScript IC table. There's one ICEntry per IC. 113 class ICEntry { 114 // A pointer to the first IC stub for this instruction. 115 ICStub* firstStub_; 116 117 public: 118 explicit ICEntry(ICStub* firstStub) : firstStub_(firstStub) {} 119 120 ICStub* firstStub() const { 121 MOZ_ASSERT(firstStub_); 122 return firstStub_; 123 } 124 125 void setFirstStub(ICStub* stub) { firstStub_ = stub; } 126 127 static constexpr size_t offsetOfFirstStub() { 128 return offsetof(ICEntry, firstStub_); 129 } 130 131 void trace(JSTracer* trc, ICFallbackStub* fallbackStub); 132 bool traceWeak(JSTracer* trc, ICFallbackStub* fallbackStub); 133 }; 134 135 // 136 // Base class for all IC stubs. 137 // 138 class ICStub { 139 friend class ICFallbackStub; 140 141 protected: 142 // The raw jitcode to call for this stub. 143 uint8_t* stubCode_; 144 145 // Counts the number of times the stub was entered 146 // 147 // See Bug 1494473 comment 6 for a mechanism to handle overflow if overflow 148 // becomes a concern. 149 uint32_t enteredCount_ = 0; 150 151 // Tracks input types for some CacheIR stubs, to help optimize 152 // polymorphic cases. Stored in the base class to make use of 153 // padding bytes. 154 TypeData typeData_; 155 156 // Whether this is an ICFallbackStub or an ICCacheIRStub. 157 bool isFallback_; 158 159 ICStub(uint8_t* stubCode, bool isFallback) 160 : stubCode_(stubCode), isFallback_(isFallback) { 161 #ifndef ENABLE_PORTABLE_BASELINE_INTERP 162 MOZ_ASSERT(stubCode != nullptr); 163 #endif // !ENABLE_PORTABLE_BASELINE_INTERP 164 } 165 166 public: 167 inline bool isFallback() const { return isFallback_; } 168 169 inline ICStub* maybeNext() const; 170 171 inline const ICFallbackStub* toFallbackStub() const { 172 MOZ_ASSERT(isFallback()); 173 return reinterpret_cast<const ICFallbackStub*>(this); 174 } 175 176 inline ICFallbackStub* toFallbackStub() { 177 MOZ_ASSERT(isFallback()); 178 return reinterpret_cast<ICFallbackStub*>(this); 179 } 180 181 ICCacheIRStub* toCacheIRStub() { 182 MOZ_ASSERT(!isFallback()); 183 return reinterpret_cast<ICCacheIRStub*>(this); 184 } 185 const ICCacheIRStub* toCacheIRStub() const { 186 MOZ_ASSERT(!isFallback()); 187 return reinterpret_cast<const ICCacheIRStub*>(this); 188 } 189 190 bool usesTrampolineCode() const { 191 // All fallback code is stored in a single JitCode instance, so we can't 192 // call JitCode::FromExecutable on the raw pointer. 193 return isFallback(); 194 } 195 196 #ifndef ENABLE_PORTABLE_BASELINE_INTERP 197 JitCode* jitCode() { 198 MOZ_ASSERT(!usesTrampolineCode()); 199 return JitCode::FromExecutable(stubCode_); 200 } 201 bool hasJitCode() { return !!stubCode_; } 202 #else // !ENABLE_PORTABLE_BASELINE_INTERP 203 JitCode* jitCode() { return nullptr; } 204 bool hasJitCode() { return false; } 205 uint8_t* rawJitCode() const { return stubCode_; } 206 void updateRawJitCode(uint8_t* ptr) { stubCode_ = ptr; } 207 #endif 208 209 uint32_t enteredCount() const { return enteredCount_; } 210 inline void incrementEnteredCount() { enteredCount_++; } 211 void resetEnteredCount() { enteredCount_ = 0; } 212 213 static constexpr size_t offsetOfStubCode() { 214 return offsetof(ICStub, stubCode_); 215 } 216 static constexpr size_t offsetOfEnteredCount() { 217 return offsetof(ICStub, enteredCount_); 218 } 219 }; 220 221 class ICFallbackStub final : public ICStub { 222 friend class ICStubConstIterator; 223 224 protected: 225 // The PC offset of this IC's bytecode op within the JSScript. 226 uint32_t pcOffset_; 227 228 // The state of this IC. 229 ICState state_{}; 230 231 public: 232 explicit ICFallbackStub(uint32_t pcOffset, TrampolinePtr stubCode) 233 : ICStub(stubCode.value, /* isFallback = */ true), pcOffset_(pcOffset) {} 234 235 inline size_t numOptimizedStubs() const { return state_.numOptimizedStubs(); } 236 237 bool newStubIsFirstStub() const { return state_.newStubIsFirstStub(); } 238 239 ICState& state() { return state_; } 240 241 uint32_t pcOffset() const { return pcOffset_; } 242 243 // Add a new stub to the IC chain terminated by this fallback stub. 244 inline void addNewStub(ICEntry* icEntry, ICCacheIRStub* stub); 245 246 void discardStubs(Zone* zone, ICEntry* icEntry); 247 248 void clearUsedByTranspiler() { state_.clearUsedByTranspiler(); } 249 void setUsedByTranspiler() { state_.setUsedByTranspiler(); } 250 bool usedByTranspiler() const { return state_.usedByTranspiler(); } 251 252 void clearMayHaveFoldedStub() { state_.clearMayHaveFoldedStub(); } 253 void setMayHaveFoldedStub() { state_.setMayHaveFoldedStub(); } 254 bool mayHaveFoldedStub() const { return state_.mayHaveFoldedStub(); } 255 256 TrialInliningState trialInliningState() const { 257 return state_.trialInliningState(); 258 } 259 void setTrialInliningState(TrialInliningState state) { 260 state_.setTrialInliningState(state); 261 } 262 263 void trackNotAttached(); 264 265 void unlinkStub(Zone* zone, ICEntry* icEntry, ICCacheIRStub* prev, 266 ICCacheIRStub* stub); 267 void unlinkStubUnbarriered(ICEntry* icEntry, ICCacheIRStub* prev, 268 ICCacheIRStub* stub); 269 }; 270 271 class ICCacheIRStub final : public ICStub { 272 // Pointer to next IC stub. 273 ICStub* next_ = nullptr; 274 275 const CacheIRStubInfo* stubInfo_; 276 277 #ifndef JS_64BIT 278 // Ensure stub data is 8-byte aligned on 32-bit. 279 uintptr_t padding_ = 0; 280 #endif 281 282 public: 283 ICCacheIRStub(JitCode* stubCode, const CacheIRStubInfo* stubInfo) 284 : ICStub(stubCode ? stubCode->raw() : nullptr, /* isFallback = */ false), 285 stubInfo_(stubInfo) { 286 MOZ_ASSERT_IF(!IsPortableBaselineInterpreterEnabled(), stubCode); 287 } 288 289 ICStub* next() const { return next_; } 290 void setNext(ICStub* stub) { next_ = stub; } 291 292 ICCacheIRStub* nextCacheIR() const { 293 return next_->isFallback() ? nullptr : next_->toCacheIRStub(); 294 } 295 296 const CacheIRStubInfo* stubInfo() const { return stubInfo_; } 297 uint8_t* stubDataStart(); 298 299 void trace(JSTracer* trc); 300 bool traceWeak(JSTracer* trc); 301 302 ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace); 303 304 // Returns true if this stub can call JS or VM code that can trigger a GC. 305 bool makesGCCalls() const; 306 307 static constexpr size_t offsetOfNext() { 308 return offsetof(ICCacheIRStub, next_); 309 } 310 311 void setTypeData(TypeData data) { typeData_ = data; } 312 TypeData typeData() const { return typeData_; } 313 }; 314 315 // Assert stub size is what we expect to catch regressions. 316 #ifdef JS_64BIT 317 static_assert(sizeof(ICFallbackStub) == 3 * sizeof(uintptr_t)); 318 static_assert(sizeof(ICCacheIRStub) == 4 * sizeof(uintptr_t)); 319 #else 320 static_assert(sizeof(ICFallbackStub) == 5 * sizeof(uintptr_t)); 321 static_assert(sizeof(ICCacheIRStub) == 6 * sizeof(uintptr_t)); 322 #endif 323 324 inline ICStub* ICStub::maybeNext() const { 325 return isFallback() ? nullptr : toCacheIRStub()->next(); 326 } 327 328 inline void ICFallbackStub::addNewStub(ICEntry* icEntry, ICCacheIRStub* stub) { 329 MOZ_ASSERT(stub->next() == nullptr); 330 stub->setNext(icEntry->firstStub()); 331 icEntry->setFirstStub(stub); 332 state_.trackAttached(); 333 } 334 335 AllocatableGeneralRegisterSet BaselineICAvailableGeneralRegs(size_t numInputs); 336 337 bool ICSupportsPolymorphicTypeData(JSOp op); 338 339 struct IonOsrTempData; 340 341 extern bool DoCallFallback(JSContext* cx, BaselineFrame* frame, 342 ICFallbackStub* stub, uint32_t argc, Value* vp, 343 MutableHandleValue res); 344 345 extern bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, 346 ICFallbackStub* stub, Value* vp, 347 MutableHandleValue res); 348 349 extern bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame, 350 ICFallbackStub* stub, HandleValue arg, 351 MutableHandleValue ret); 352 353 extern bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, 354 ICFallbackStub* stub, HandleValue lhs, 355 HandleValue rhs, HandleValue receiver, 356 MutableHandleValue res); 357 358 extern bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame, 359 ICFallbackStub* stub, HandleValue lhs, 360 HandleValue rhs, MutableHandleValue res); 361 362 extern bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame, 363 ICFallbackStub* stub, Value* stack, 364 HandleValue objv, HandleValue index, 365 HandleValue rhs); 366 367 extern bool DoInFallback(JSContext* cx, BaselineFrame* frame, 368 ICFallbackStub* stub, HandleValue key, 369 HandleValue objValue, MutableHandleValue res); 370 371 extern bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, 372 ICFallbackStub* stub, HandleValue keyValue, 373 HandleValue objValue, MutableHandleValue res); 374 375 extern bool DoCheckPrivateFieldFallback(JSContext* cx, BaselineFrame* frame, 376 ICFallbackStub* stub, 377 HandleValue objValue, 378 HandleValue keyValue, 379 MutableHandleValue res); 380 381 extern bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame, 382 ICFallbackStub* stub, HandleObject envChain, 383 MutableHandleValue res); 384 385 extern bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame, 386 ICFallbackStub* stub, HandleObject envChain, 387 MutableHandleValue res); 388 389 extern bool DoLazyConstantFallback(JSContext* cx, BaselineFrame* frame, 390 ICFallbackStub* stub, 391 MutableHandleValue res); 392 393 extern bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame, 394 ICFallbackStub* stub, HandleValue val, 395 MutableHandleValue res); 396 397 extern bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, 398 ICFallbackStub* stub, HandleValue receiver, 399 HandleValue val, MutableHandleValue res); 400 401 extern bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame, 402 ICFallbackStub* stub, Value* stack, 403 HandleValue lhs, HandleValue rhs); 404 405 extern bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, 406 ICFallbackStub* stub, HandleValue value, 407 MutableHandleValue res); 408 409 extern bool DoOptimizeSpreadCallFallback(JSContext* cx, BaselineFrame* frame, 410 ICFallbackStub* stub, 411 HandleValue value, 412 MutableHandleValue res); 413 414 extern bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, 415 ICFallbackStub* stub, HandleValue lhs, 416 HandleValue rhs, MutableHandleValue res); 417 418 extern bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, 419 ICFallbackStub* stub, HandleValue val, 420 MutableHandleValue res); 421 422 extern bool DoTypeOfEqFallback(JSContext* cx, BaselineFrame* frame, 423 ICFallbackStub* stub, HandleValue val, 424 MutableHandleValue res); 425 426 extern bool DoToPropertyKeyFallback(JSContext* cx, BaselineFrame* frame, 427 ICFallbackStub* stub, HandleValue val, 428 MutableHandleValue res); 429 430 extern bool DoRestFallback(JSContext* cx, BaselineFrame* frame, 431 ICFallbackStub* stub, MutableHandleValue res); 432 433 extern bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame, 434 ICFallbackStub* stub, HandleValue val, 435 MutableHandleValue res); 436 437 extern bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, 438 ICFallbackStub* stub, HandleValue lhs, 439 HandleValue rhs, MutableHandleValue ret); 440 441 extern bool DoNewArrayFallback(JSContext* cx, BaselineFrame* frame, 442 ICFallbackStub* stub, MutableHandleValue res); 443 444 extern bool DoNewObjectFallback(JSContext* cx, BaselineFrame* frame, 445 ICFallbackStub* stub, MutableHandleValue res); 446 447 extern bool DoLambdaFallback(JSContext* cx, BaselineFrame* frame, 448 ICFallbackStub* stub, MutableHandleValue res); 449 450 extern bool DoCompareFallback(JSContext* cx, BaselineFrame* frame, 451 ICFallbackStub* stub, HandleValue lhs, 452 HandleValue rhs, MutableHandleValue ret); 453 454 extern bool DoCloseIterFallback(JSContext* cx, BaselineFrame* frame, 455 ICFallbackStub* stub, HandleObject iter); 456 457 extern bool DoOptimizeGetIteratorFallback(JSContext* cx, BaselineFrame* frame, 458 ICFallbackStub* stub, 459 HandleValue value, 460 MutableHandleValue res); 461 extern bool DoGetImportFallback(JSContext* cx, BaselineFrame* frame, 462 ICFallbackStub* stub, MutableHandleValue res); 463 464 } // namespace jit 465 } // namespace js 466 467 #endif /* jit_BaselineIC_h */