js/src/jit/BaselineBailouts.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 #include "jsprf.h"
michael@0 8 #include "jit/arm/Simulator-arm.h"
michael@0 9 #include "jit/BaselineIC.h"
michael@0 10 #include "jit/BaselineJIT.h"
michael@0 11 #include "jit/CompileInfo.h"
michael@0 12 #include "jit/IonSpewer.h"
michael@0 13 #include "jit/Recover.h"
michael@0 14 #include "jit/RematerializedFrame.h"
michael@0 15
michael@0 16 #include "vm/ArgumentsObject.h"
michael@0 17 #include "vm/Debugger.h"
michael@0 18 #include "vm/TraceLogging.h"
michael@0 19
michael@0 20 #include "jsscriptinlines.h"
michael@0 21
michael@0 22 #include "jit/IonFrames-inl.h"
michael@0 23
michael@0 24 using namespace js;
michael@0 25 using namespace js::jit;
michael@0 26
michael@0 27 // BaselineStackBuilder may reallocate its buffer if the current one is too
michael@0 28 // small. To avoid dangling pointers, BufferPointer represents a pointer into
michael@0 29 // this buffer as a pointer to the header and a fixed offset.
michael@0 30 template <typename T>
michael@0 31 class BufferPointer
michael@0 32 {
michael@0 33 BaselineBailoutInfo **header_;
michael@0 34 size_t offset_;
michael@0 35 bool heap_;
michael@0 36
michael@0 37 public:
michael@0 38 BufferPointer(BaselineBailoutInfo **header, size_t offset, bool heap)
michael@0 39 : header_(header), offset_(offset), heap_(heap)
michael@0 40 { }
michael@0 41
michael@0 42 T *get() const {
michael@0 43 BaselineBailoutInfo *header = *header_;
michael@0 44 if (!heap_)
michael@0 45 return (T*)(header->incomingStack + offset_);
michael@0 46
michael@0 47 uint8_t *p = header->copyStackTop - offset_;
michael@0 48 JS_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop);
michael@0 49 return (T*)p;
michael@0 50 }
michael@0 51
michael@0 52 T &operator*() const { return *get(); }
michael@0 53 T *operator->() const { return get(); }
michael@0 54 };
michael@0 55
michael@0 56 /**
michael@0 57 * BaselineStackBuilder helps abstract the process of rebuilding the C stack on the heap.
michael@0 58 * It takes a bailout iterator and keeps track of the point on the C stack from which
michael@0 59 * the reconstructed frames will be written.
michael@0 60 *
michael@0 61 * It exposes methods to write data into the heap memory storing the reconstructed
michael@0 62 * stack. It also exposes method to easily calculate addresses. This includes both the
michael@0 63 * virtual address that a particular value will be at when it's eventually copied onto
michael@0 64 * the stack, as well as the current actual address of that value (whether on the heap
michael@0 65 * allocated portion being constructed or the existing stack).
michael@0 66 *
michael@0 67 * The abstraction handles transparent re-allocation of the heap memory when it
michael@0 68 * needs to be enlarged to accomodate new data. Similarly to the C stack, the
michael@0 69 * data that's written to the reconstructed stack grows from high to low in memory.
michael@0 70 *
michael@0 71 * The lowest region of the allocated memory contains a BaselineBailoutInfo structure that
michael@0 72 * points to the start and end of the written data.
michael@0 73 */
michael@0 74 struct BaselineStackBuilder
michael@0 75 {
michael@0 76 IonBailoutIterator &iter_;
michael@0 77 IonJSFrameLayout *frame_;
michael@0 78
michael@0 79 static size_t HeaderSize() {
michael@0 80 return AlignBytes(sizeof(BaselineBailoutInfo), sizeof(void *));
michael@0 81 };
michael@0 82 size_t bufferTotal_;
michael@0 83 size_t bufferAvail_;
michael@0 84 size_t bufferUsed_;
michael@0 85 uint8_t *buffer_;
michael@0 86 BaselineBailoutInfo *header_;
michael@0 87
michael@0 88 size_t framePushed_;
michael@0 89
michael@0 90 BaselineStackBuilder(IonBailoutIterator &iter, size_t initialSize)
michael@0 91 : iter_(iter),
michael@0 92 frame_(static_cast<IonJSFrameLayout*>(iter.current())),
michael@0 93 bufferTotal_(initialSize),
michael@0 94 bufferAvail_(0),
michael@0 95 bufferUsed_(0),
michael@0 96 buffer_(nullptr),
michael@0 97 header_(nullptr),
michael@0 98 framePushed_(0)
michael@0 99 {
michael@0 100 JS_ASSERT(bufferTotal_ >= HeaderSize());
michael@0 101 }
michael@0 102
michael@0 103 ~BaselineStackBuilder() {
michael@0 104 js_free(buffer_);
michael@0 105 }
michael@0 106
michael@0 107 bool init() {
michael@0 108 JS_ASSERT(!buffer_);
michael@0 109 JS_ASSERT(bufferUsed_ == 0);
michael@0 110 buffer_ = reinterpret_cast<uint8_t *>(js_calloc(bufferTotal_));
michael@0 111 if (!buffer_)
michael@0 112 return false;
michael@0 113 bufferAvail_ = bufferTotal_ - HeaderSize();
michael@0 114 bufferUsed_ = 0;
michael@0 115
michael@0 116 header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
michael@0 117 header_->incomingStack = reinterpret_cast<uint8_t *>(frame_);
michael@0 118 header_->copyStackTop = buffer_ + bufferTotal_;
michael@0 119 header_->copyStackBottom = header_->copyStackTop;
michael@0 120 header_->setR0 = 0;
michael@0 121 header_->valueR0 = UndefinedValue();
michael@0 122 header_->setR1 = 0;
michael@0 123 header_->valueR1 = UndefinedValue();
michael@0 124 header_->resumeFramePtr = nullptr;
michael@0 125 header_->resumeAddr = nullptr;
michael@0 126 header_->monitorStub = nullptr;
michael@0 127 header_->numFrames = 0;
michael@0 128 return true;
michael@0 129 }
michael@0 130
michael@0 131 bool enlarge() {
michael@0 132 JS_ASSERT(buffer_ != nullptr);
michael@0 133 if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value)
michael@0 134 return false;
michael@0 135 size_t newSize = bufferTotal_ * 2;
michael@0 136 uint8_t *newBuffer = reinterpret_cast<uint8_t *>(js_calloc(newSize));
michael@0 137 if (!newBuffer)
michael@0 138 return false;
michael@0 139 memcpy((newBuffer + newSize) - bufferUsed_, header_->copyStackBottom, bufferUsed_);
michael@0 140 memcpy(newBuffer, header_, sizeof(BaselineBailoutInfo));
michael@0 141 js_free(buffer_);
michael@0 142 buffer_ = newBuffer;
michael@0 143 bufferTotal_ = newSize;
michael@0 144 bufferAvail_ = newSize - (HeaderSize() + bufferUsed_);
michael@0 145
michael@0 146 header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_);
michael@0 147 header_->copyStackTop = buffer_ + bufferTotal_;
michael@0 148 header_->copyStackBottom = header_->copyStackTop - bufferUsed_;
michael@0 149 return true;
michael@0 150 }
michael@0 151
michael@0 152 BaselineBailoutInfo *info() {
michael@0 153 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
michael@0 154 return header_;
michael@0 155 }
michael@0 156
michael@0 157 BaselineBailoutInfo *takeBuffer() {
michael@0 158 JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_));
michael@0 159 buffer_ = nullptr;
michael@0 160 return header_;
michael@0 161 }
michael@0 162
michael@0 163 void resetFramePushed() {
michael@0 164 framePushed_ = 0;
michael@0 165 }
michael@0 166
michael@0 167 size_t framePushed() const {
michael@0 168 return framePushed_;
michael@0 169 }
michael@0 170
michael@0 171 bool subtract(size_t size, const char *info = nullptr) {
michael@0 172 // enlarge the buffer if need be.
michael@0 173 while (size > bufferAvail_) {
michael@0 174 if (!enlarge())
michael@0 175 return false;
michael@0 176 }
michael@0 177
michael@0 178 // write out element.
michael@0 179 header_->copyStackBottom -= size;
michael@0 180 bufferAvail_ -= size;
michael@0 181 bufferUsed_ += size;
michael@0 182 framePushed_ += size;
michael@0 183 if (info) {
michael@0 184 IonSpew(IonSpew_BaselineBailouts,
michael@0 185 " SUB_%03d %p/%p %-15s",
michael@0 186 (int) size, header_->copyStackBottom, virtualPointerAtStackOffset(0), info);
michael@0 187 }
michael@0 188 return true;
michael@0 189 }
michael@0 190
michael@0 191 template <typename T>
michael@0 192 bool write(const T &t) {
michael@0 193 if (!subtract(sizeof(T)))
michael@0 194 return false;
michael@0 195 memcpy(header_->copyStackBottom, &t, sizeof(T));
michael@0 196 return true;
michael@0 197 }
michael@0 198
michael@0 199 template <typename T>
michael@0 200 bool writePtr(T *t, const char *info) {
michael@0 201 if (!write<T *>(t))
michael@0 202 return false;
michael@0 203 if (info)
michael@0 204 IonSpew(IonSpew_BaselineBailouts,
michael@0 205 " WRITE_PTR %p/%p %-15s %p",
michael@0 206 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, t);
michael@0 207 return true;
michael@0 208 }
michael@0 209
michael@0 210 bool writeWord(size_t w, const char *info) {
michael@0 211 if (!write<size_t>(w))
michael@0 212 return false;
michael@0 213 if (info) {
michael@0 214 if (sizeof(size_t) == 4) {
michael@0 215 IonSpew(IonSpew_BaselineBailouts,
michael@0 216 " WRITE_WRD %p/%p %-15s %08x",
michael@0 217 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
michael@0 218 } else {
michael@0 219 IonSpew(IonSpew_BaselineBailouts,
michael@0 220 " WRITE_WRD %p/%p %-15s %016llx",
michael@0 221 header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
michael@0 222 }
michael@0 223 }
michael@0 224 return true;
michael@0 225 }
michael@0 226
michael@0 227 bool writeValue(Value val, const char *info) {
michael@0 228 if (!write<Value>(val))
michael@0 229 return false;
michael@0 230 if (info) {
michael@0 231 IonSpew(IonSpew_BaselineBailouts,
michael@0 232 " WRITE_VAL %p/%p %-15s %016llx",
michael@0 233 header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
michael@0 234 *((uint64_t *) &val));
michael@0 235 }
michael@0 236 return true;
michael@0 237 }
michael@0 238
michael@0 239 Value popValue() {
michael@0 240 JS_ASSERT(bufferUsed_ >= sizeof(Value));
michael@0 241 JS_ASSERT(framePushed_ >= sizeof(Value));
michael@0 242 bufferAvail_ += sizeof(Value);
michael@0 243 bufferUsed_ -= sizeof(Value);
michael@0 244 framePushed_ -= sizeof(Value);
michael@0 245 Value result = *((Value *) header_->copyStackBottom);
michael@0 246 header_->copyStackBottom += sizeof(Value);
michael@0 247 return result;
michael@0 248 }
michael@0 249
michael@0 250 void popValueInto(PCMappingSlotInfo::SlotLocation loc) {
michael@0 251 JS_ASSERT(PCMappingSlotInfo::ValidSlotLocation(loc));
michael@0 252 switch(loc) {
michael@0 253 case PCMappingSlotInfo::SlotInR0:
michael@0 254 header_->setR0 = 1;
michael@0 255 header_->valueR0 = popValue();
michael@0 256 break;
michael@0 257 case PCMappingSlotInfo::SlotInR1:
michael@0 258 header_->setR1 = 1;
michael@0 259 header_->valueR1 = popValue();
michael@0 260 break;
michael@0 261 default:
michael@0 262 JS_ASSERT(loc == PCMappingSlotInfo::SlotIgnore);
michael@0 263 popValue();
michael@0 264 break;
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 void setResumeFramePtr(void *resumeFramePtr) {
michael@0 269 header_->resumeFramePtr = resumeFramePtr;
michael@0 270 }
michael@0 271
michael@0 272 void setResumeAddr(void *resumeAddr) {
michael@0 273 header_->resumeAddr = resumeAddr;
michael@0 274 }
michael@0 275
michael@0 276 void setMonitorStub(ICStub *stub) {
michael@0 277 header_->monitorStub = stub;
michael@0 278 }
michael@0 279
michael@0 280 template <typename T>
michael@0 281 BufferPointer<T> pointerAtStackOffset(size_t offset) {
michael@0 282 if (offset < bufferUsed_) {
michael@0 283 // Calculate offset from copyStackTop.
michael@0 284 offset = header_->copyStackTop - (header_->copyStackBottom + offset);
michael@0 285 return BufferPointer<T>(&header_, offset, /* heap = */ true);
michael@0 286 }
michael@0 287
michael@0 288 return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false);
michael@0 289 }
michael@0 290
michael@0 291 BufferPointer<Value> valuePointerAtStackOffset(size_t offset) {
michael@0 292 return pointerAtStackOffset<Value>(offset);
michael@0 293 }
michael@0 294
michael@0 295 inline uint8_t *virtualPointerAtStackOffset(size_t offset) {
michael@0 296 if (offset < bufferUsed_)
michael@0 297 return reinterpret_cast<uint8_t *>(frame_) - (bufferUsed_ - offset);
michael@0 298 return reinterpret_cast<uint8_t *>(frame_) + (offset - bufferUsed_);
michael@0 299 }
michael@0 300
michael@0 301 inline IonJSFrameLayout *startFrame() {
michael@0 302 return frame_;
michael@0 303 }
michael@0 304
michael@0 305 BufferPointer<IonJSFrameLayout> topFrameAddress() {
michael@0 306 return pointerAtStackOffset<IonJSFrameLayout>(0);
michael@0 307 }
michael@0 308
michael@0 309 //
michael@0 310 // This method should only be called when the builder is in a state where it is
michael@0 311 // starting to construct the stack frame for the next callee. This means that
michael@0 312 // the lowest value on the constructed stack is the return address for the previous
michael@0 313 // caller frame.
michael@0 314 //
michael@0 315 // This method is used to compute the value of the frame pointer (e.g. ebp on x86)
michael@0 316 // that would have been saved by the baseline jitcode when it was entered. In some
michael@0 317 // cases, this value can be bogus since we can ensure that the caller would have saved
michael@0 318 // it anyway.
michael@0 319 //
michael@0 320 void *calculatePrevFramePtr() {
michael@0 321 // Get the incoming frame.
michael@0 322 BufferPointer<IonJSFrameLayout> topFrame = topFrameAddress();
michael@0 323 FrameType type = topFrame->prevType();
michael@0 324
michael@0 325 // For IonJS and Entry frames, the "saved" frame pointer in the baseline
michael@0 326 // frame is meaningless, since Ion saves all registers before calling other ion
michael@0 327 // frames, and the entry frame saves all registers too.
michael@0 328 if (type == JitFrame_IonJS || type == JitFrame_Entry)
michael@0 329 return nullptr;
michael@0 330
michael@0 331 // BaselineStub - Baseline calling into Ion.
michael@0 332 // PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer.
michael@0 333 // STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
michael@0 334 // - IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
michael@0 335 if (type == JitFrame_BaselineStub) {
michael@0 336 size_t offset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize() +
michael@0 337 IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
michael@0 338 return virtualPointerAtStackOffset(offset);
michael@0 339 }
michael@0 340
michael@0 341 JS_ASSERT(type == JitFrame_Rectifier);
michael@0 342 // Rectifier - behaviour depends on the frame preceding the rectifier frame, and
michael@0 343 // whether the arch is x86 or not. The x86 rectifier frame saves the frame pointer,
michael@0 344 // so we can calculate it directly. For other archs, the previous frame pointer
michael@0 345 // is stored on the stack in the frame that precedes the rectifier frame.
michael@0 346 size_t priorOffset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize();
michael@0 347 #if defined(JS_CODEGEN_X86)
michael@0 348 // On X86, the FramePointer is pushed as the first value in the Rectifier frame.
michael@0 349 JS_ASSERT(BaselineFrameReg == FramePointer);
michael@0 350 priorOffset -= sizeof(void *);
michael@0 351 return virtualPointerAtStackOffset(priorOffset);
michael@0 352 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
michael@0 353 // On X64, ARM and MIPS, the frame pointer save location depends on
michael@0 354 // the caller of the rectifier frame.
michael@0 355 BufferPointer<IonRectifierFrameLayout> priorFrame =
michael@0 356 pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
michael@0 357 FrameType priorType = priorFrame->prevType();
michael@0 358 JS_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub);
michael@0 359
michael@0 360 // If the frame preceding the rectifier is an IonJS frame, then once again
michael@0 361 // the frame pointer does not matter.
michael@0 362 if (priorType == JitFrame_IonJS)
michael@0 363 return nullptr;
michael@0 364
michael@0 365 // Otherwise, the frame preceding the rectifier is a BaselineStub frame.
michael@0 366 // let X = STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE
michael@0 367 // X + IonRectifierFrameLayout::Size()
michael@0 368 // + ((IonRectifierFrameLayout *) X)->prevFrameLocalSize()
michael@0 369 // - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr()
michael@0 370 size_t extraOffset = IonRectifierFrameLayout::Size() + priorFrame->prevFrameLocalSize() +
michael@0 371 IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr();
michael@0 372 return virtualPointerAtStackOffset(priorOffset + extraOffset);
michael@0 373 #else
michael@0 374 # error "Bad architecture!"
michael@0 375 #endif
michael@0 376 }
michael@0 377 };
michael@0 378
michael@0 379 static inline bool
michael@0 380 IsInlinableFallback(ICFallbackStub *icEntry)
michael@0 381 {
michael@0 382 return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
michael@0 383 icEntry->isSetProp_Fallback();
michael@0 384 }
michael@0 385
michael@0 386 static inline void*
michael@0 387 GetStubReturnAddress(JSContext *cx, jsbytecode *pc)
michael@0 388 {
michael@0 389 if (IsGetPropPC(pc))
michael@0 390 return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr();
michael@0 391 if (IsSetPropPC(pc))
michael@0 392 return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr();
michael@0 393 // This should be a call op of some kind, now.
michael@0 394 JS_ASSERT(IsCallPC(pc));
michael@0 395 return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr();
michael@0 396 }
michael@0 397
michael@0 398 static inline jsbytecode *
michael@0 399 GetNextNonLoopEntryPc(jsbytecode *pc)
michael@0 400 {
michael@0 401 JSOp op = JSOp(*pc);
michael@0 402 if (op == JSOP_GOTO)
michael@0 403 return pc + GET_JUMP_OFFSET(pc);
michael@0 404 if (op == JSOP_LOOPENTRY || op == JSOP_NOP || op == JSOP_LOOPHEAD)
michael@0 405 return GetNextPc(pc);
michael@0 406 return pc;
michael@0 407 }
michael@0 408
michael@0 409 // For every inline frame, we write out the following data:
michael@0 410 //
michael@0 411 // | ... |
michael@0 412 // +---------------+
michael@0 413 // | Descr(???) | --- Descr size here is (PREV_FRAME_SIZE)
michael@0 414 // +---------------+
michael@0 415 // | ReturnAddr |
michael@0 416 // -- +===============+ --- OVERWRITE STARTS HERE (START_STACK_ADDR)
michael@0 417 // | | PrevFramePtr |
michael@0 418 // | +-> +---------------+
michael@0 419 // | | | Baseline |
michael@0 420 // | | | Frame |
michael@0 421 // | | +---------------+
michael@0 422 // | | | Fixed0 |
michael@0 423 // | | +---------------+
michael@0 424 // +--< | | ... |
michael@0 425 // | | | +---------------+
michael@0 426 // | | | | FixedF |
michael@0 427 // | | | +---------------+
michael@0 428 // | | | | Stack0 |
michael@0 429 // | | | +---------------+
michael@0 430 // | | | | ... |
michael@0 431 // | | | +---------------+
michael@0 432 // | | | | StackS |
michael@0 433 // | -- | +---------------+ --- IF NOT LAST INLINE FRAME,
michael@0 434 // +------------| Descr(BLJS) | --- CALLING INFO STARTS HERE
michael@0 435 // | +---------------+
michael@0 436 // | | ReturnAddr | <-- return into main jitcode after IC
michael@0 437 // -- | +===============+
michael@0 438 // | | | StubPtr |
michael@0 439 // | | +---------------+
michael@0 440 // | +---| FramePtr |
michael@0 441 // | +---------------+
michael@0 442 // | | ArgA |
michael@0 443 // | +---------------+
michael@0 444 // | | ... |
michael@0 445 // +--< +---------------+
michael@0 446 // | | | Arg0 |
michael@0 447 // | | +---------------+
michael@0 448 // | | | ThisV |
michael@0 449 // | -- +---------------+
michael@0 450 // | | ActualArgC |
michael@0 451 // | +---------------+
michael@0 452 // | | CalleeToken |
michael@0 453 // | +---------------+
michael@0 454 // +------------| Descr(BLStub) |
michael@0 455 // +---------------+
michael@0 456 // | ReturnAddr | <-- return into ICCall_Scripted IC
michael@0 457 // -- +===============+ --- IF CALLEE FORMAL ARGS > ActualArgC
michael@0 458 // | | UndefinedU |
michael@0 459 // | +---------------+
michael@0 460 // | | ... |
michael@0 461 // | +---------------+
michael@0 462 // | | Undefined0 |
michael@0 463 // | +---------------+
michael@0 464 // +--< | ArgA |
michael@0 465 // | | +---------------+
michael@0 466 // | | | ... |
michael@0 467 // | | +---------------+
michael@0 468 // | | | Arg0 |
michael@0 469 // | | +---------------+
michael@0 470 // | | | ThisV |
michael@0 471 // | -- +---------------+
michael@0 472 // | | ActualArgC |
michael@0 473 // | +---------------+
michael@0 474 // | | CalleeToken |
michael@0 475 // | +---------------+
michael@0 476 // +------------| Descr(Rect) |
michael@0 477 // +---------------+
michael@0 478 // | ReturnAddr | <-- return into ArgumentsRectifier after call
michael@0 479 // +===============+
michael@0 480 //
michael@0 481 static bool
michael@0 482 InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
michael@0 483 HandleFunction fun, HandleScript script, IonScript *ionScript,
michael@0 484 SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
michael@0 485 AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
michael@0 486 jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
michael@0 487 {
michael@0 488 MOZ_ASSERT(script->hasBaselineScript());
michael@0 489
michael@0 490 // Are we catching an exception?
michael@0 491 bool catchingException = excInfo && excInfo->catchingException();
michael@0 492
michael@0 493 // If we are catching an exception, we are bailing out to a catch or
michael@0 494 // finally block and this is the frame where we will resume. Usually the
michael@0 495 // expression stack should be empty in this case but there can be
michael@0 496 // iterators on the stack.
michael@0 497 uint32_t exprStackSlots;
michael@0 498 if (catchingException)
michael@0 499 exprStackSlots = excInfo->numExprSlots();
michael@0 500 else
michael@0 501 exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
michael@0 502
michael@0 503 builder.resetFramePushed();
michael@0 504
michael@0 505 // Build first baseline frame:
michael@0 506 // +===============+
michael@0 507 // | PrevFramePtr |
michael@0 508 // +---------------+
michael@0 509 // | Baseline |
michael@0 510 // | Frame |
michael@0 511 // +---------------+
michael@0 512 // | Fixed0 |
michael@0 513 // +---------------+
michael@0 514 // | ... |
michael@0 515 // +---------------+
michael@0 516 // | FixedF |
michael@0 517 // +---------------+
michael@0 518 // | Stack0 |
michael@0 519 // +---------------+
michael@0 520 // | ... |
michael@0 521 // +---------------+
michael@0 522 // | StackS |
michael@0 523 // +---------------+ --- IF NOT LAST INLINE FRAME,
michael@0 524 // | Descr(BLJS) | --- CALLING INFO STARTS HERE
michael@0 525 // +---------------+
michael@0 526 // | ReturnAddr | <-- return into main jitcode after IC
michael@0 527 // +===============+
michael@0 528
michael@0 529 IonSpew(IonSpew_BaselineBailouts, " Unpacking %s:%d", script->filename(), script->lineno());
michael@0 530 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-JS FRAME]");
michael@0 531
michael@0 532 // Calculate and write the previous frame pointer value.
michael@0 533 // Record the virtual stack offset at this location. Later on, if we end up
michael@0 534 // writing out a BaselineStub frame for the next callee, we'll need to save the
michael@0 535 // address.
michael@0 536 void *prevFramePtr = builder.calculatePrevFramePtr();
michael@0 537 if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
michael@0 538 return false;
michael@0 539 prevFramePtr = builder.virtualPointerAtStackOffset(0);
michael@0 540
michael@0 541 // Write struct BaselineFrame.
michael@0 542 if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame"))
michael@0 543 return false;
michael@0 544 BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0);
michael@0 545
michael@0 546 // Initialize BaselineFrame::frameSize
michael@0 547 uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset +
michael@0 548 (sizeof(Value) * (script->nfixed() + exprStackSlots));
michael@0 549 IonSpew(IonSpew_BaselineBailouts, " FrameSize=%d", (int) frameSize);
michael@0 550 blFrame->setFrameSize(frameSize);
michael@0 551
michael@0 552 uint32_t flags = 0;
michael@0 553
michael@0 554 // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry.
michael@0 555 // This may be wrong for the last frame of ArgumentCheck bailout, but
michael@0 556 // that will be fixed later.
michael@0 557 if (ionScript->hasSPSInstrumentation()) {
michael@0 558 if (callerPC == nullptr) {
michael@0 559 IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top frame!");
michael@0 560 flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
michael@0 561 } else if (js_JitOptions.profileInlineFrames) {
michael@0 562 IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on inline frame!");
michael@0 563 flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
michael@0 564 }
michael@0 565 }
michael@0 566
michael@0 567 // Initialize BaselineFrame's scopeChain and argsObj
michael@0 568 JSObject *scopeChain = nullptr;
michael@0 569 Value returnValue;
michael@0 570 ArgumentsObject *argsObj = nullptr;
michael@0 571 BailoutKind bailoutKind = iter.bailoutKind();
michael@0 572 if (bailoutKind == Bailout_ArgumentCheck) {
michael@0 573 // Temporary hack -- skip the (unused) scopeChain, because it could be
michael@0 574 // bogus (we can fail before the scope chain slot is set). Strip the
michael@0 575 // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|,
michael@0 576 // which calls |EnsureHasScopeObjects|.
michael@0 577 IonSpew(IonSpew_BaselineBailouts, " Bailout_ArgumentCheck! (no valid scopeChain)");
michael@0 578 iter.skip();
michael@0 579
michael@0 580 // skip |return value|
michael@0 581 iter.skip();
michael@0 582 returnValue = UndefinedValue();
michael@0 583
michael@0 584 // Scripts with |argumentsHasVarBinding| have an extra slot.
michael@0 585 if (script->argumentsHasVarBinding()) {
michael@0 586 IonSpew(IonSpew_BaselineBailouts,
michael@0 587 " Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
michael@0 588 "Using empty arguments object");
michael@0 589 iter.skip();
michael@0 590 }
michael@0 591 } else {
michael@0 592 Value v = iter.read();
michael@0 593 if (v.isObject()) {
michael@0 594 scopeChain = &v.toObject();
michael@0 595 if (fun && fun->isHeavyweight())
michael@0 596 flags |= BaselineFrame::HAS_CALL_OBJ;
michael@0 597 } else {
michael@0 598 JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
michael@0 599
michael@0 600 // Get scope chain from function or script.
michael@0 601 if (fun) {
michael@0 602 // If pcOffset == 0, we may have to push a new call object, so
michael@0 603 // we leave scopeChain nullptr and enter baseline code before
michael@0 604 // the prologue.
michael@0 605 if (iter.pcOffset() != 0 || iter.resumeAfter())
michael@0 606 scopeChain = fun->environment();
michael@0 607 } else {
michael@0 608 // For global, compile-and-go scripts the scope chain is the
michael@0 609 // script's global (Ion does not compile non-compile-and-go
michael@0 610 // scripts). Also note that it's invalid to resume into the
michael@0 611 // prologue in this case because the prologue expects the scope
michael@0 612 // chain in R1 for eval and global scripts.
michael@0 613 JS_ASSERT(!script->isForEval());
michael@0 614 JS_ASSERT(script->compileAndGo());
michael@0 615 scopeChain = &(script->global());
michael@0 616 }
michael@0 617 }
michael@0 618
michael@0 619 // Make sure to add HAS_RVAL to flags here because setFlags() below
michael@0 620 // will clobber it.
michael@0 621 returnValue = iter.read();
michael@0 622 flags |= BaselineFrame::HAS_RVAL;
michael@0 623
michael@0 624 // If script maybe has an arguments object, the third slot will hold it.
michael@0 625 if (script->argumentsHasVarBinding()) {
michael@0 626 v = iter.read();
michael@0 627 JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
michael@0 628 if (v.isObject())
michael@0 629 argsObj = &v.toObject().as<ArgumentsObject>();
michael@0 630 }
michael@0 631 }
michael@0 632 IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain);
michael@0 633 blFrame->setScopeChain(scopeChain);
michael@0 634 IonSpew(IonSpew_BaselineBailouts, " ReturnValue=%016llx", *((uint64_t *) &returnValue));
michael@0 635 blFrame->setReturnValue(returnValue);
michael@0 636
michael@0 637 // Do not need to initialize scratchValue field in BaselineFrame.
michael@0 638 blFrame->setFlags(flags);
michael@0 639
michael@0 640 // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
michael@0 641 if (argsObj)
michael@0 642 blFrame->initArgsObjUnchecked(*argsObj);
michael@0 643
michael@0 644 if (fun) {
michael@0 645 // The unpacked thisv and arguments should overwrite the pushed args present
michael@0 646 // in the calling frame.
michael@0 647 Value thisv = iter.read();
michael@0 648 IonSpew(IonSpew_BaselineBailouts, " Is function!");
michael@0 649 IonSpew(IonSpew_BaselineBailouts, " thisv=%016llx", *((uint64_t *) &thisv));
michael@0 650
michael@0 651 size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis();
michael@0 652 *builder.valuePointerAtStackOffset(thisvOffset) = thisv;
michael@0 653
michael@0 654 JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
michael@0 655 IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u",
michael@0 656 iter.numAllocations(), fun->nargs(), script->nfixed());
michael@0 657
michael@0 658 if (!callerPC) {
michael@0 659 // This is the first frame. Store the formals in a Vector until we
michael@0 660 // are done. Due to UCE and phi elimination, we could store an
michael@0 661 // UndefinedValue() here for formals we think are unused, but
michael@0 662 // locals may still reference the original argument slot
michael@0 663 // (MParameter/LArgument) and expect the original Value.
michael@0 664 JS_ASSERT(startFrameFormals.empty());
michael@0 665 if (!startFrameFormals.resize(fun->nargs()))
michael@0 666 return false;
michael@0 667 }
michael@0 668
michael@0 669 for (uint32_t i = 0; i < fun->nargs(); i++) {
michael@0 670 Value arg = iter.read();
michael@0 671 IonSpew(IonSpew_BaselineBailouts, " arg %d = %016llx",
michael@0 672 (int) i, *((uint64_t *) &arg));
michael@0 673 if (callerPC) {
michael@0 674 size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i);
michael@0 675 *builder.valuePointerAtStackOffset(argOffset) = arg;
michael@0 676 } else {
michael@0 677 startFrameFormals[i] = arg;
michael@0 678 }
michael@0 679 }
michael@0 680 }
michael@0 681
michael@0 682 for (uint32_t i = 0; i < script->nfixed(); i++) {
michael@0 683 Value slot = iter.read();
michael@0 684 if (!builder.writeValue(slot, "FixedValue"))
michael@0 685 return false;
michael@0 686 }
michael@0 687
michael@0 688 // Get the pc. If we are handling an exception, resume at the pc of the
michael@0 689 // catch or finally block.
michael@0 690 jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
michael@0 691 bool resumeAfter = catchingException ? false : iter.resumeAfter();
michael@0 692
michael@0 693 JSOp op = JSOp(*pc);
michael@0 694
michael@0 695 // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
michael@0 696 // On the caller side this must represent like the function wasn't inlined.
michael@0 697 uint32_t pushedSlots = 0;
michael@0 698 AutoValueVector savedCallerArgs(cx);
michael@0 699 bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
michael@0 700 if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
michael@0 701 {
michael@0 702 uint32_t inlined_args = 0;
michael@0 703 if (op == JSOP_FUNCALL)
michael@0 704 inlined_args = 2 + GET_ARGC(pc) - 1;
michael@0 705 else if (op == JSOP_FUNAPPLY)
michael@0 706 inlined_args = 2 + blFrame->numActualArgs();
michael@0 707 else
michael@0 708 inlined_args = 2 + IsSetPropPC(pc);
michael@0 709
michael@0 710 JS_ASSERT(exprStackSlots >= inlined_args);
michael@0 711 pushedSlots = exprStackSlots - inlined_args;
michael@0 712
michael@0 713 IonSpew(IonSpew_BaselineBailouts,
michael@0 714 " pushing %u expression stack slots before fixup",
michael@0 715 pushedSlots);
michael@0 716 for (uint32_t i = 0; i < pushedSlots; i++) {
michael@0 717 Value v = iter.read();
michael@0 718 if (!builder.writeValue(v, "StackValue"))
michael@0 719 return false;
michael@0 720 }
michael@0 721
michael@0 722 if (op == JSOP_FUNCALL) {
michael@0 723 // When funcall got inlined and the native js_fun_call was bypassed,
michael@0 724 // the stack state is incorrect. To restore correctly it must look like
michael@0 725 // js_fun_call was actually called. This means transforming the stack
michael@0 726 // from |target, this, args| to |js_fun_call, target, this, args|
michael@0 727 // The js_fun_call is never read, so just pushing undefined now.
michael@0 728 IonSpew(IonSpew_BaselineBailouts, " pushing undefined to fixup funcall");
michael@0 729 if (!builder.writeValue(UndefinedValue(), "StackValue"))
michael@0 730 return false;
michael@0 731 }
michael@0 732
michael@0 733 if (needToSaveArgs) {
michael@0 734 // When an accessor is inlined, the whole thing is a lie. There
michael@0 735 // should never have been a call there. Fix the caller's stack to
michael@0 736 // forget it ever happened.
michael@0 737
michael@0 738 // When funapply gets inlined we take all arguments out of the
michael@0 739 // arguments array. So the stack state is incorrect. To restore
michael@0 740 // correctly it must look like js_fun_apply was actually called.
michael@0 741 // This means transforming the stack from |target, this, arg1, ...|
michael@0 742 // to |js_fun_apply, target, this, argObject|.
michael@0 743 // Since the information is never read, we can just push undefined
michael@0 744 // for all values.
michael@0 745 if (op == JSOP_FUNAPPLY) {
michael@0 746 IonSpew(IonSpew_BaselineBailouts, " pushing 4x undefined to fixup funapply");
michael@0 747 if (!builder.writeValue(UndefinedValue(), "StackValue"))
michael@0 748 return false;
michael@0 749 if (!builder.writeValue(UndefinedValue(), "StackValue"))
michael@0 750 return false;
michael@0 751 if (!builder.writeValue(UndefinedValue(), "StackValue"))
michael@0 752 return false;
michael@0 753 if (!builder.writeValue(UndefinedValue(), "StackValue"))
michael@0 754 return false;
michael@0 755 }
michael@0 756 // Save the actual arguments. They are needed on the callee side
michael@0 757 // as the arguments. Else we can't recover them.
michael@0 758 if (!savedCallerArgs.resize(inlined_args))
michael@0 759 return false;
michael@0 760 for (uint32_t i = 0; i < inlined_args; i++)
michael@0 761 savedCallerArgs[i] = iter.read();
michael@0 762
michael@0 763 if (IsSetPropPC(pc)) {
michael@0 764 // We would love to just save all the arguments and leave them
michael@0 765 // in the stub frame pushed below, but we will lose the inital
michael@0 766 // argument which the function was called with, which we must
michael@0 767 // return to the caller, even if the setter internally modifies
michael@0 768 // its arguments. Stash the initial argument on the stack, to be
michael@0 769 // later retrieved by the SetProp_Fallback stub.
michael@0 770 Value initialArg = savedCallerArgs[inlined_args - 1];
michael@0 771 IonSpew(IonSpew_BaselineBailouts, " pushing setter's initial argument");
michael@0 772 if (!builder.writeValue(initialArg, "StackValue"))
michael@0 773 return false;
michael@0 774 }
michael@0 775 pushedSlots = exprStackSlots;
michael@0 776 }
michael@0 777 }
michael@0 778
michael@0 779 IonSpew(IonSpew_BaselineBailouts, " pushing %u expression stack slots",
michael@0 780 exprStackSlots - pushedSlots);
michael@0 781 for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
michael@0 782 Value v;
michael@0 783
michael@0 784 if (!iter.moreFrames() && i == exprStackSlots - 1 &&
michael@0 785 cx->runtime()->hasIonReturnOverride())
michael@0 786 {
michael@0 787 // If coming from an invalidation bailout, and this is the topmost
michael@0 788 // value, and a value override has been specified, don't read from the
michael@0 789 // iterator. Otherwise, we risk using a garbage value.
michael@0 790 JS_ASSERT(invalidate);
michael@0 791 iter.skip();
michael@0 792 IonSpew(IonSpew_BaselineBailouts, " [Return Override]");
michael@0 793 v = cx->runtime()->takeIonReturnOverride();
michael@0 794 } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
michael@0 795 // If we are in the middle of propagating an exception from Ion by
michael@0 796 // bailing to baseline due to debug mode, we might not have all
michael@0 797 // the stack if we are at the newest frame.
michael@0 798 //
michael@0 799 // For instance, if calling |f()| pushed an Ion frame which threw,
michael@0 800 // the snapshot expects the return value to be pushed, but it's
michael@0 801 // possible nothing was pushed before we threw. Iterators might
michael@0 802 // still be on the stack, so we can't just drop the stack.
michael@0 803 MOZ_ASSERT(cx->compartment()->debugMode());
michael@0 804 if (iter.moreFrames())
michael@0 805 v = iter.read();
michael@0 806 else
michael@0 807 v = MagicValue(JS_OPTIMIZED_OUT);
michael@0 808 } else {
michael@0 809 v = iter.read();
michael@0 810 }
michael@0 811 if (!builder.writeValue(v, "StackValue"))
michael@0 812 return false;
michael@0 813 }
michael@0 814
michael@0 815 size_t endOfBaselineJSFrameStack = builder.framePushed();
michael@0 816
michael@0 817 // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
michael@0 818 // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
michael@0 819 // ThunkToInterpreter.
michael@0 820 //
michael@0 821 // The algorithm below is the "tortoise and the hare" algorithm. See bug
michael@0 822 // 994444 for more explanation.
michael@0 823 if (!resumeAfter) {
michael@0 824 jsbytecode *fasterPc = pc;
michael@0 825 while (true) {
michael@0 826 pc = GetNextNonLoopEntryPc(pc);
michael@0 827 fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
michael@0 828 if (fasterPc == pc)
michael@0 829 break;
michael@0 830 }
michael@0 831 op = JSOp(*pc);
michael@0 832 }
michael@0 833
michael@0 834 uint32_t pcOff = script->pcToOffset(pc);
michael@0 835 bool isCall = IsCallPC(pc);
michael@0 836 BaselineScript *baselineScript = script->baselineScript();
michael@0 837
michael@0 838 #ifdef DEBUG
michael@0 839 uint32_t expectedDepth;
michael@0 840 bool reachablePC;
michael@0 841 if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
michael@0 842 return false;
michael@0 843
michael@0 844 if (reachablePC) {
michael@0 845 if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
michael@0 846 if (op == JSOP_FUNCALL) {
michael@0 847 // For fun.call(this, ...); the reconstructStackDepth will
michael@0 848 // include the this. When inlining that is not included.
michael@0 849 // So the exprStackSlots will be one less.
michael@0 850 JS_ASSERT(expectedDepth - exprStackSlots <= 1);
michael@0 851 } else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
michael@0 852 // Accessors coming out of ion are inlined via a complete
michael@0 853 // lie perpetrated by the compiler internally. Ion just rearranges
michael@0 854 // the stack, and pretends that it looked like a call all along.
michael@0 855 // This means that the depth is actually one *more* than expected
michael@0 856 // by the interpreter, as there is now a JSFunction, |this| and [arg],
michael@0 857 // rather than the expected |this| and [arg]
michael@0 858 // Note that none of that was pushed, but it's still reflected
michael@0 859 // in exprStackSlots.
michael@0 860 JS_ASSERT(exprStackSlots - expectedDepth == 1);
michael@0 861 } else {
michael@0 862 // For fun.apply({}, arguments) the reconstructStackDepth will
michael@0 863 // have stackdepth 4, but it could be that we inlined the
michael@0 864 // funapply. In that case exprStackSlots, will have the real
michael@0 865 // arguments in the slots and not be 4.
michael@0 866 JS_ASSERT(exprStackSlots == expectedDepth);
michael@0 867 }
michael@0 868 }
michael@0 869 }
michael@0 870
michael@0 871 IonSpew(IonSpew_BaselineBailouts, " Resuming %s pc offset %d (op %s) (line %d) of %s:%d",
michael@0 872 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
michael@0 873 PCToLineNumber(script, pc), script->filename(), (int) script->lineno());
michael@0 874 IonSpew(IonSpew_BaselineBailouts, " Bailout kind: %s",
michael@0 875 BailoutKindString(bailoutKind));
michael@0 876 #endif
michael@0 877
michael@0 878 // If this was the last inline frame, or we are bailing out to a catch or
michael@0 879 // finally block in this frame, then unpacking is almost done.
michael@0 880 if (!iter.moreFrames() || catchingException) {
michael@0 881 // Last frame, so PC for call to next frame is set to nullptr.
michael@0 882 *callPC = nullptr;
michael@0 883
michael@0 884 // If the bailout was a resumeAfter, and the opcode is monitored,
michael@0 885 // then the bailed out state should be in a position to enter
michael@0 886 // into the ICTypeMonitor chain for the op.
michael@0 887 bool enterMonitorChain = false;
michael@0 888 if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) {
michael@0 889 // Not every monitored op has a monitored fallback stub, e.g.
michael@0 890 // JSOP_NEWOBJECT, which always returns the same type for a
michael@0 891 // particular script/pc location.
michael@0 892 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
michael@0 893 ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
michael@0 894 if (fallbackStub->isMonitoredFallback())
michael@0 895 enterMonitorChain = true;
michael@0 896 }
michael@0 897
michael@0 898 uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0;
michael@0 899
michael@0 900 if (resumeAfter && !enterMonitorChain)
michael@0 901 pc = GetNextPc(pc);
michael@0 902
michael@0 903 builder.setResumeFramePtr(prevFramePtr);
michael@0 904
michael@0 905 if (enterMonitorChain) {
michael@0 906 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
michael@0 907 ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
michael@0 908 JS_ASSERT(fallbackStub->isMonitoredFallback());
michael@0 909 IonSpew(IonSpew_BaselineBailouts, " [TYPE-MONITOR CHAIN]");
michael@0 910 ICMonitoredFallbackStub *monFallbackStub = fallbackStub->toMonitoredFallbackStub();
michael@0 911 ICStub *firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
michael@0 912
michael@0 913 // To enter a monitoring chain, we load the top stack value into R0
michael@0 914 IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into R0.");
michael@0 915 builder.popValueInto(PCMappingSlotInfo::SlotInR0);
michael@0 916
michael@0 917 // Need to adjust the frameSize for the frame to match the values popped
michael@0 918 // into registers.
michael@0 919 frameSize -= sizeof(Value);
michael@0 920 blFrame->setFrameSize(frameSize);
michael@0 921 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d",
michael@0 922 (int) sizeof(Value), (int) frameSize);
michael@0 923
michael@0 924 // If resuming into a JSOP_CALL, baseline keeps the arguments on the
michael@0 925 // stack and pops them only after returning from the call IC.
michael@0 926 // Push undefs onto the stack in anticipation of the popping of the
michael@0 927 // callee, thisv, and actual arguments passed from the caller's frame.
michael@0 928 if (isCall) {
michael@0 929 builder.writeValue(UndefinedValue(), "CallOp FillerCallee");
michael@0 930 builder.writeValue(UndefinedValue(), "CallOp FillerThis");
michael@0 931 for (uint32_t i = 0; i < numCallArgs; i++)
michael@0 932 builder.writeValue(UndefinedValue(), "CallOp FillerArg");
michael@0 933
michael@0 934 frameSize += (numCallArgs + 2) * sizeof(Value);
michael@0 935 blFrame->setFrameSize(frameSize);
michael@0 936 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize += %d: %d",
michael@0 937 (int) ((numCallArgs + 2) * sizeof(Value)), (int) frameSize);
michael@0 938 }
michael@0 939
michael@0 940 // Set the resume address to the return point from the IC, and set
michael@0 941 // the monitor stub addr.
michael@0 942 builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
michael@0 943 builder.setMonitorStub(firstMonStub);
michael@0 944 IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p monitorStub=%p",
michael@0 945 baselineScript->returnAddressForIC(icEntry), firstMonStub);
michael@0 946
michael@0 947 } else {
michael@0 948 // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
michael@0 949 // top stack values.
michael@0 950 PCMappingSlotInfo slotInfo;
michael@0 951 uint8_t *nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
michael@0 952 unsigned numUnsynced = slotInfo.numUnsynced();
michael@0 953 JS_ASSERT(numUnsynced <= 2);
michael@0 954 PCMappingSlotInfo::SlotLocation loc1, loc2;
michael@0 955 if (numUnsynced > 0) {
michael@0 956 loc1 = slotInfo.topSlotLocation();
michael@0 957 IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into %d.",
michael@0 958 (int) loc1);
michael@0 959 builder.popValueInto(loc1);
michael@0 960 }
michael@0 961 if (numUnsynced > 1) {
michael@0 962 loc2 = slotInfo.nextSlotLocation();
michael@0 963 IonSpew(IonSpew_BaselineBailouts, " Popping next stack value into %d.",
michael@0 964 (int) loc2);
michael@0 965 JS_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2);
michael@0 966 builder.popValueInto(loc2);
michael@0 967 }
michael@0 968
michael@0 969 // Need to adjust the frameSize for the frame to match the values popped
michael@0 970 // into registers.
michael@0 971 frameSize -= sizeof(Value) * numUnsynced;
michael@0 972 blFrame->setFrameSize(frameSize);
michael@0 973 IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d",
michael@0 974 int(sizeof(Value) * numUnsynced), int(frameSize));
michael@0 975
michael@0 976 // If scopeChain is nullptr, then bailout is occurring during argument check.
michael@0 977 // In this case, resume into the prologue.
michael@0 978 uint8_t *opReturnAddr;
michael@0 979 if (scopeChain == nullptr) {
michael@0 980 // Global and eval scripts expect the scope chain in R1, so only
michael@0 981 // resume into the prologue for function scripts.
michael@0 982 JS_ASSERT(fun);
michael@0 983 JS_ASSERT(numUnsynced == 0);
michael@0 984 opReturnAddr = baselineScript->prologueEntryAddr();
michael@0 985 IonSpew(IonSpew_BaselineBailouts, " Resuming into prologue.");
michael@0 986
michael@0 987 // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame.
michael@0 988 blFrame->unsetPushedSPSFrame();
michael@0 989
michael@0 990 if (cx->runtime()->spsProfiler.enabled()) {
michael@0 991 if (js_JitOptions.profileInlineFrames) {
michael@0 992 // If SPS is enabled, there are two corner cases to handle:
michael@0 993 // 1. If resuming into the prologue, and innermost frame is an inlined
michael@0 994 // frame, and bailout is because of argument check failure, then:
michael@0 995 // Top SPS profiler entry would be for caller frame.
michael@0 996 // Ion would not have set the PC index field on that frame
michael@0 997 // (since this bailout happens before MFunctionBoundary).
michael@0 998 // Make sure that's done now.
michael@0 999 // 2. If resuming into the prologue, and the bailout is NOT because of an
michael@0 1000 // argument check, then:
michael@0 1001 // Top SPS profiler entry would be for callee frame.
michael@0 1002 // Ion would already have pushed an SPS entry for this frame.
michael@0 1003 // The pc for this entry would be set to nullptr.
michael@0 1004 // Make sure it's set to script->pc.
michael@0 1005 if (caller && bailoutKind == Bailout_ArgumentCheck) {
michael@0 1006 IonSpew(IonSpew_BaselineBailouts, " Setting PCidx on innermost "
michael@0 1007 "inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!",
michael@0 1008 caller->filename(), caller->lineno(),
michael@0 1009 caller->pcToOffset(callerPC));
michael@0 1010 cx->runtime()->spsProfiler.updatePC(caller, callerPC);
michael@0 1011
michael@0 1012 } else if (bailoutKind != Bailout_ArgumentCheck) {
michael@0 1013 IonSpew(IonSpew_BaselineBailouts,
michael@0 1014 " Popping SPS entry for innermost inlined frame");
michael@0 1015 cx->runtime()->spsProfiler.exit(script, fun);
michael@0 1016 }
michael@0 1017
michael@0 1018 } else {
michael@0 1019 // If not profiling inline frames, then this is logically simpler.
michael@0 1020 //
michael@0 1021 // 1. If resuming into inline code, then the top SPS entry will be
michael@0 1022 // for the outermost caller, and will have an uninitialized PC.
michael@0 1023 // This will be fixed up later in BailoutIonToBaseline.
michael@0 1024 //
michael@0 1025 // 2. If resuming into top-level code prologue, with ArgumentCheck,
michael@0 1026 // no SPS entry will have been pushed. Can be left alone.
michael@0 1027 //
michael@0 1028 // 3. If resuming into top-level code prologue, without ArgumentCheck,
michael@0 1029 // an SPS entry will have been pushed, and needs to be popped.
michael@0 1030 //
michael@0 1031 // 4. If resuming into top-level code main body, an SPS entry will
michael@0 1032 // have been pushed, and can be left alone.
michael@0 1033 //
michael@0 1034 // Only need to handle case 3 here.
michael@0 1035 if (!caller && bailoutKind != Bailout_ArgumentCheck) {
michael@0 1036 IonSpew(IonSpew_BaselineBailouts,
michael@0 1037 " Popping SPS entry for outermost frame");
michael@0 1038 cx->runtime()->spsProfiler.exit(script, fun);
michael@0 1039 }
michael@0 1040 }
michael@0 1041 }
michael@0 1042 } else {
michael@0 1043 opReturnAddr = nativeCodeForPC;
michael@0 1044 }
michael@0 1045 builder.setResumeAddr(opReturnAddr);
michael@0 1046 IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr);
michael@0 1047 }
michael@0 1048
michael@0 1049 if (cx->runtime()->spsProfiler.enabled()) {
michael@0 1050 if (blFrame->hasPushedSPSFrame()) {
michael@0 1051 // Set PC index to 0 for the innermost frame to match what the
michael@0 1052 // interpreter and Baseline do: they update the SPS pc for
michael@0 1053 // JSOP_CALL ops but set it to 0 when running other ops. Ion code
michael@0 1054 // can set the pc to NullPCIndex and this will confuse SPS when
michael@0 1055 // Baseline calls into the VM at non-CALL ops and re-enters JS.
michael@0 1056 IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0");
michael@0 1057 cx->runtime()->spsProfiler.updatePC(script, script->code());
michael@0 1058 }
michael@0 1059
michael@0 1060 // Register bailout with profiler.
michael@0 1061 const char *filename = script->filename();
michael@0 1062 if (filename == nullptr)
michael@0 1063 filename = "<unknown>";
michael@0 1064 unsigned len = strlen(filename) + 200;
michael@0 1065 char *buf = js_pod_malloc<char>(len);
michael@0 1066 if (buf == nullptr)
michael@0 1067 return false;
michael@0 1068 JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d",
michael@0 1069 BailoutKindString(bailoutKind),
michael@0 1070 resumeAfter ? "after" : "at",
michael@0 1071 js_CodeName[op],
michael@0 1072 int(PCToLineNumber(script, pc)),
michael@0 1073 filename,
michael@0 1074 int(script->lineno()));
michael@0 1075 cx->runtime()->spsProfiler.markEvent(buf);
michael@0 1076 js_free(buf);
michael@0 1077 }
michael@0 1078
michael@0 1079 return true;
michael@0 1080 }
michael@0 1081
michael@0 1082 *callPC = pc;
michael@0 1083
michael@0 1084 // Write out descriptor of BaselineJS frame.
michael@0 1085 size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(),
michael@0 1086 JitFrame_BaselineJS);
michael@0 1087 if (!builder.writeWord(baselineFrameDescr, "Descriptor"))
michael@0 1088 return false;
michael@0 1089
michael@0 1090 // Calculate and write out return address.
michael@0 1091 // The icEntry in question MUST have an inlinable fallback stub.
michael@0 1092 ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
michael@0 1093 JS_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
michael@0 1094 if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr"))
michael@0 1095 return false;
michael@0 1096
michael@0 1097 // Build baseline stub frame:
michael@0 1098 // +===============+
michael@0 1099 // | StubPtr |
michael@0 1100 // +---------------+
michael@0 1101 // | FramePtr |
michael@0 1102 // +---------------+
michael@0 1103 // | ArgA |
michael@0 1104 // +---------------+
michael@0 1105 // | ... |
michael@0 1106 // +---------------+
michael@0 1107 // | Arg0 |
michael@0 1108 // +---------------+
michael@0 1109 // | ThisV |
michael@0 1110 // +---------------+
michael@0 1111 // | ActualArgC |
michael@0 1112 // +---------------+
michael@0 1113 // | CalleeToken |
michael@0 1114 // +---------------+
michael@0 1115 // | Descr(BLStub) |
michael@0 1116 // +---------------+
michael@0 1117 // | ReturnAddr |
michael@0 1118 // +===============+
michael@0 1119
michael@0 1120 IonSpew(IonSpew_BaselineBailouts, " [BASELINE-STUB FRAME]");
michael@0 1121
michael@0 1122 size_t startOfBaselineStubFrame = builder.framePushed();
michael@0 1123
michael@0 1124 // Write stub pointer.
michael@0 1125 JS_ASSERT(IsInlinableFallback(icEntry.fallbackStub()));
michael@0 1126 if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr"))
michael@0 1127 return false;
michael@0 1128
michael@0 1129 // Write previous frame pointer (saved earlier).
michael@0 1130 if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
michael@0 1131 return false;
michael@0 1132 prevFramePtr = builder.virtualPointerAtStackOffset(0);
michael@0 1133
michael@0 1134 // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
michael@0 1135 // Arguments are reversed on the BaselineJS frame's stack values.
michael@0 1136 JS_ASSERT(IsIonInlinablePC(pc));
michael@0 1137 unsigned actualArgc;
michael@0 1138 if (needToSaveArgs) {
michael@0 1139 // For FUNAPPLY or an accessor, the arguments are not on the stack anymore,
michael@0 1140 // but they are copied in a vector and are written here.
michael@0 1141 if (op == JSOP_FUNAPPLY)
michael@0 1142 actualArgc = blFrame->numActualArgs();
michael@0 1143 else
michael@0 1144 actualArgc = IsSetPropPC(pc);
michael@0 1145
michael@0 1146 JS_ASSERT(actualArgc + 2 <= exprStackSlots);
michael@0 1147 JS_ASSERT(savedCallerArgs.length() == actualArgc + 2);
michael@0 1148 for (unsigned i = 0; i < actualArgc + 1; i++) {
michael@0 1149 size_t arg = savedCallerArgs.length() - (i + 1);
michael@0 1150 if (!builder.writeValue(savedCallerArgs[arg], "ArgVal"))
michael@0 1151 return false;
michael@0 1152 }
michael@0 1153 } else {
michael@0 1154 actualArgc = GET_ARGC(pc);
michael@0 1155 if (op == JSOP_FUNCALL) {
michael@0 1156 JS_ASSERT(actualArgc > 0);
michael@0 1157 actualArgc--;
michael@0 1158 }
michael@0 1159
michael@0 1160 JS_ASSERT(actualArgc + 2 <= exprStackSlots);
michael@0 1161 for (unsigned i = 0; i < actualArgc + 1; i++) {
michael@0 1162 size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1);
michael@0 1163 if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
michael@0 1164 return false;
michael@0 1165 }
michael@0 1166 }
michael@0 1167
michael@0 1168 // In case these arguments need to be copied on the stack again for a rectifier frame,
michael@0 1169 // save the framePushed values here for later use.
michael@0 1170 size_t endOfBaselineStubArgs = builder.framePushed();
michael@0 1171
michael@0 1172 // Calculate frame size for descriptor.
michael@0 1173 size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
michael@0 1174 size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize,
michael@0 1175 JitFrame_BaselineStub);
michael@0 1176
michael@0 1177 // Push actual argc
michael@0 1178 if (!builder.writeWord(actualArgc, "ActualArgc"))
michael@0 1179 return false;
michael@0 1180
michael@0 1181 // Push callee token (must be a JS Function)
michael@0 1182 Value callee;
michael@0 1183 if (needToSaveArgs) {
michael@0 1184 // The arguments of FUNAPPLY or inlined accessors are not writen to the stack.
michael@0 1185 // So get the callee from the specially saved vector.
michael@0 1186 callee = savedCallerArgs[0];
michael@0 1187 } else {
michael@0 1188 uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2);
michael@0 1189 size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack)
michael@0 1190 + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value));
michael@0 1191 callee = *builder.valuePointerAtStackOffset(calleeOffset);
michael@0 1192 IonSpew(IonSpew_BaselineBailouts, " CalleeStackSlot=%d", (int) calleeStackSlot);
michael@0 1193 }
michael@0 1194 IonSpew(IonSpew_BaselineBailouts, " Callee = %016llx", *((uint64_t *) &callee));
michael@0 1195 JS_ASSERT(callee.isObject() && callee.toObject().is<JSFunction>());
michael@0 1196 JSFunction *calleeFun = &callee.toObject().as<JSFunction>();
michael@0 1197 if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
michael@0 1198 return false;
michael@0 1199 nextCallee.set(calleeFun);
michael@0 1200
michael@0 1201 // Push BaselineStub frame descriptor
michael@0 1202 if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
michael@0 1203 return false;
michael@0 1204
michael@0 1205 // Push return address into ICCall_Scripted stub, immediately after the call.
michael@0 1206 void *baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
michael@0 1207 JS_ASSERT(baselineCallReturnAddr);
michael@0 1208 if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
michael@0 1209 return false;
michael@0 1210
michael@0 1211 // If actualArgc >= fun->nargs, then we are done. Otherwise, we need to push on
michael@0 1212 // a reconstructed rectifier frame.
michael@0 1213 if (actualArgc >= calleeFun->nargs())
michael@0 1214 return true;
michael@0 1215
michael@0 1216 // Push a reconstructed rectifier frame.
michael@0 1217 // +===============+
michael@0 1218 // | UndefinedU |
michael@0 1219 // +---------------+
michael@0 1220 // | ... |
michael@0 1221 // +---------------+
michael@0 1222 // | Undefined0 |
michael@0 1223 // +---------------+
michael@0 1224 // | ArgA |
michael@0 1225 // +---------------+
michael@0 1226 // | ... |
michael@0 1227 // +---------------+
michael@0 1228 // | Arg0 |
michael@0 1229 // +---------------+
michael@0 1230 // | ThisV |
michael@0 1231 // +---------------+
michael@0 1232 // | ActualArgC |
michael@0 1233 // +---------------+
michael@0 1234 // | CalleeToken |
michael@0 1235 // +---------------+
michael@0 1236 // | Descr(Rect) |
michael@0 1237 // +---------------+
michael@0 1238 // | ReturnAddr |
michael@0 1239 // +===============+
michael@0 1240
michael@0 1241 IonSpew(IonSpew_BaselineBailouts, " [RECTIFIER FRAME]");
michael@0 1242
michael@0 1243 size_t startOfRectifierFrame = builder.framePushed();
michael@0 1244
michael@0 1245 // On x86-only, the frame pointer is saved again in the rectifier frame.
michael@0 1246 #if defined(JS_CODEGEN_X86)
michael@0 1247 if (!builder.writePtr(prevFramePtr, "PrevFramePtr-X86Only"))
michael@0 1248 return false;
michael@0 1249 #endif
michael@0 1250
michael@0 1251 // Push undefined for missing arguments.
michael@0 1252 for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) {
michael@0 1253 if (!builder.writeValue(UndefinedValue(), "FillerVal"))
michael@0 1254 return false;
michael@0 1255 }
michael@0 1256
michael@0 1257 // Copy arguments + thisv from BaselineStub frame.
michael@0 1258 if (!builder.subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs"))
michael@0 1259 return false;
michael@0 1260 BufferPointer<uint8_t> stubArgsEnd =
michael@0 1261 builder.pointerAtStackOffset<uint8_t>(builder.framePushed() - endOfBaselineStubArgs);
michael@0 1262 IonSpew(IonSpew_BaselineBailouts, " MemCpy from %p", stubArgsEnd.get());
michael@0 1263 memcpy(builder.pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(),
michael@0 1264 (actualArgc + 1) * sizeof(Value));
michael@0 1265
michael@0 1266 // Calculate frame size for descriptor.
michael@0 1267 size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame;
michael@0 1268 size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize,
michael@0 1269 JitFrame_Rectifier);
michael@0 1270
michael@0 1271 // Push actualArgc
michael@0 1272 if (!builder.writeWord(actualArgc, "ActualArgc"))
michael@0 1273 return false;
michael@0 1274
michael@0 1275 // Push calleeToken again.
michael@0 1276 if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken"))
michael@0 1277 return false;
michael@0 1278
michael@0 1279 // Push rectifier frame descriptor
michael@0 1280 if (!builder.writeWord(rectifierFrameDescr, "Descriptor"))
michael@0 1281 return false;
michael@0 1282
michael@0 1283 // Push return address into the ArgumentsRectifier code, immediately after the ioncode
michael@0 1284 // call.
michael@0 1285 void *rectReturnAddr = cx->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr();
michael@0 1286 JS_ASSERT(rectReturnAddr);
michael@0 1287 if (!builder.writePtr(rectReturnAddr, "ReturnAddr"))
michael@0 1288 return false;
michael@0 1289
michael@0 1290 return true;
michael@0 1291 }
michael@0 1292
michael@0 1293 uint32_t
michael@0 1294 jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
michael@0 1295 bool invalidate, BaselineBailoutInfo **bailoutInfo,
michael@0 1296 const ExceptionBailoutInfo *excInfo)
michael@0 1297 {
michael@0 1298 JS_ASSERT(bailoutInfo != nullptr);
michael@0 1299 JS_ASSERT(*bailoutInfo == nullptr);
michael@0 1300
michael@0 1301 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
michael@0 1302 TraceLogStopEvent(logger, TraceLogger::IonMonkey);
michael@0 1303 TraceLogStartEvent(logger, TraceLogger::Baseline);
michael@0 1304
michael@0 1305 // The caller of the top frame must be one of the following:
michael@0 1306 // IonJS - Ion calling into Ion.
michael@0 1307 // BaselineStub - Baseline calling into Ion.
michael@0 1308 // Entry - Interpreter or other calling into Ion.
michael@0 1309 // Rectifier - Arguments rectifier calling into Ion.
michael@0 1310 JS_ASSERT(iter.isIonJS());
michael@0 1311 FrameType prevFrameType = iter.prevType();
michael@0 1312 JS_ASSERT(prevFrameType == JitFrame_IonJS ||
michael@0 1313 prevFrameType == JitFrame_BaselineStub ||
michael@0 1314 prevFrameType == JitFrame_Entry ||
michael@0 1315 prevFrameType == JitFrame_Rectifier);
michael@0 1316
michael@0 1317 // All incoming frames are going to look like this:
michael@0 1318 //
michael@0 1319 // +---------------+
michael@0 1320 // | ... |
michael@0 1321 // +---------------+
michael@0 1322 // | Args |
michael@0 1323 // | ... |
michael@0 1324 // +---------------+
michael@0 1325 // | ThisV |
michael@0 1326 // +---------------+
michael@0 1327 // | ActualArgC |
michael@0 1328 // +---------------+
michael@0 1329 // | CalleeToken |
michael@0 1330 // +---------------+
michael@0 1331 // | Descriptor |
michael@0 1332 // +---------------+
michael@0 1333 // | ReturnAddr |
michael@0 1334 // +---------------+
michael@0 1335 // | ||||| | <---- Overwrite starting here.
michael@0 1336 // | ||||| |
michael@0 1337 // | ||||| |
michael@0 1338 // +---------------+
michael@0 1339
michael@0 1340 IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
michael@0 1341 iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(),
michael@0 1342 (int) prevFrameType);
michael@0 1343
michael@0 1344 bool catchingException;
michael@0 1345 bool propagatingExceptionForDebugMode;
michael@0 1346 if (excInfo) {
michael@0 1347 catchingException = excInfo->catchingException();
michael@0 1348 propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
michael@0 1349
michael@0 1350 if (catchingException)
michael@0 1351 IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
michael@0 1352
michael@0 1353 if (propagatingExceptionForDebugMode)
michael@0 1354 IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode");
michael@0 1355 } else {
michael@0 1356 catchingException = false;
michael@0 1357 propagatingExceptionForDebugMode = false;
michael@0 1358 }
michael@0 1359
michael@0 1360 IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u",
michael@0 1361 iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
michael@0 1362
michael@0 1363 if (!excInfo)
michael@0 1364 iter.ionScript()->incNumBailouts();
michael@0 1365 iter.script()->updateBaselineOrIonRaw();
michael@0 1366
michael@0 1367 // Allocate buffer to hold stack replacement data.
michael@0 1368 BaselineStackBuilder builder(iter, 1024);
michael@0 1369 if (!builder.init())
michael@0 1370 return BAILOUT_RETURN_FATAL_ERROR;
michael@0 1371 IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
michael@0 1372
michael@0 1373 SnapshotIterator snapIter(iter);
michael@0 1374
michael@0 1375 RootedFunction callee(cx, iter.maybeCallee());
michael@0 1376 RootedScript scr(cx, iter.script());
michael@0 1377 if (callee) {
michael@0 1378 IonSpew(IonSpew_BaselineBailouts, " Callee function (%s:%u)",
michael@0 1379 scr->filename(), scr->lineno());
michael@0 1380 } else {
michael@0 1381 IonSpew(IonSpew_BaselineBailouts, " No callee!");
michael@0 1382 }
michael@0 1383
michael@0 1384 if (iter.isConstructing())
michael@0 1385 IonSpew(IonSpew_BaselineBailouts, " Constructing!");
michael@0 1386 else
michael@0 1387 IonSpew(IonSpew_BaselineBailouts, " Not constructing!");
michael@0 1388
michael@0 1389 IonSpew(IonSpew_BaselineBailouts, " Restoring frames:");
michael@0 1390 size_t frameNo = 0;
michael@0 1391
michael@0 1392 // Reconstruct baseline frames using the builder.
michael@0 1393 RootedScript caller(cx);
michael@0 1394 jsbytecode *callerPC = nullptr;
michael@0 1395 RootedFunction fun(cx, callee);
michael@0 1396 AutoValueVector startFrameFormals(cx);
michael@0 1397
michael@0 1398 RootedScript topCaller(cx);
michael@0 1399 jsbytecode *topCallerPC = nullptr;
michael@0 1400
michael@0 1401 while (true) {
michael@0 1402 MOZ_ASSERT(snapIter.instruction()->isResumePoint());
michael@0 1403
michael@0 1404 if (frameNo > 0) {
michael@0 1405 TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
michael@0 1406 TraceLogStartEvent(logger, TraceLogger::Baseline);
michael@0 1407 }
michael@0 1408
michael@0 1409 IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo);
michael@0 1410
michael@0 1411 // If we are bailing out to a catch or finally block in this frame,
michael@0 1412 // pass excInfo to InitFromBailout and don't unpack any other frames.
michael@0 1413 bool handleException = (catchingException && excInfo->frameNo() == frameNo);
michael@0 1414
michael@0 1415 // We also need to pass excInfo if we're bailing out in place for
michael@0 1416 // debug mode.
michael@0 1417 bool passExcInfo = handleException || propagatingExceptionForDebugMode;
michael@0 1418
michael@0 1419 jsbytecode *callPC = nullptr;
michael@0 1420 RootedFunction nextCallee(cx, nullptr);
michael@0 1421 if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
michael@0 1422 snapIter, invalidate, builder, startFrameFormals,
michael@0 1423 &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
michael@0 1424 {
michael@0 1425 return BAILOUT_RETURN_FATAL_ERROR;
michael@0 1426 }
michael@0 1427
michael@0 1428 if (!snapIter.moreFrames()) {
michael@0 1429 JS_ASSERT(!callPC);
michael@0 1430 break;
michael@0 1431 }
michael@0 1432
michael@0 1433 if (handleException)
michael@0 1434 break;
michael@0 1435
michael@0 1436 JS_ASSERT(nextCallee);
michael@0 1437 JS_ASSERT(callPC);
michael@0 1438 caller = scr;
michael@0 1439 callerPC = callPC;
michael@0 1440 fun = nextCallee;
michael@0 1441 scr = fun->existingScriptForInlinedFunction();
michael@0 1442
michael@0 1443 // Save top caller info for adjusting SPS frames later.
michael@0 1444 if (!topCaller) {
michael@0 1445 JS_ASSERT(frameNo == 0);
michael@0 1446 topCaller = caller;
michael@0 1447 topCallerPC = callerPC;
michael@0 1448 }
michael@0 1449
michael@0 1450 frameNo++;
michael@0 1451
michael@0 1452 snapIter.nextInstruction();
michael@0 1453 }
michael@0 1454 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
michael@0 1455
michael@0 1456 // If there were multiple inline frames unpacked, and inline frame profiling
michael@0 1457 // is off, then the current top SPS frame is for the outermost caller, and
michael@0 1458 // has an uninitialized PC. Initialize it now.
michael@0 1459 if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
michael@0 1460 cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
michael@0 1461
michael@0 1462 BailoutKind bailoutKind = snapIter.bailoutKind();
michael@0 1463
michael@0 1464 if (!startFrameFormals.empty()) {
michael@0 1465 // Set the first frame's formals, see the comment in InitFromBailout.
michael@0 1466 Value *argv = builder.startFrame()->argv() + 1; // +1 to skip |this|.
michael@0 1467 mozilla::PodCopy(argv, startFrameFormals.begin(), startFrameFormals.length());
michael@0 1468 }
michael@0 1469
michael@0 1470 // Do stack check.
michael@0 1471 bool overRecursed = false;
michael@0 1472 BaselineBailoutInfo *info = builder.info();
michael@0 1473 uint8_t *newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom);
michael@0 1474 #ifdef JS_ARM_SIMULATOR
michael@0 1475 if (Simulator::Current()->overRecursed(uintptr_t(newsp)))
michael@0 1476 overRecursed = true;
michael@0 1477 #else
michael@0 1478 JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true);
michael@0 1479 #endif
michael@0 1480 if (overRecursed) {
michael@0 1481 IonSpew(IonSpew_BaselineBailouts, " Overrecursion check failed!");
michael@0 1482 return BAILOUT_RETURN_OVERRECURSED;
michael@0 1483 }
michael@0 1484
michael@0 1485 // Take the reconstructed baseline stack so it doesn't get freed when builder destructs.
michael@0 1486 info = builder.takeBuffer();
michael@0 1487 info->numFrames = frameNo + 1;
michael@0 1488 info->bailoutKind = bailoutKind;
michael@0 1489 *bailoutInfo = info;
michael@0 1490 return BAILOUT_RETURN_OK;
michael@0 1491 }
michael@0 1492
michael@0 1493 static bool
michael@0 1494 HandleBoundsCheckFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
michael@0 1495 {
michael@0 1496 IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d, inlined into %s:%d",
michael@0 1497 innerScript->filename(), innerScript->lineno(),
michael@0 1498 outerScript->filename(), outerScript->lineno());
michael@0 1499
michael@0 1500 JS_ASSERT(!outerScript->ionScript()->invalidated());
michael@0 1501
michael@0 1502 // TODO: Currently this mimic's Ion's handling of this case. Investigate setting
michael@0 1503 // the flag on innerScript as opposed to outerScript, and maybe invalidating both
michael@0 1504 // inner and outer scripts, instead of just the outer one.
michael@0 1505 if (!outerScript->failedBoundsCheck())
michael@0 1506 outerScript->setFailedBoundsCheck();
michael@0 1507 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to bounds check failure");
michael@0 1508 return Invalidate(cx, outerScript);
michael@0 1509 }
michael@0 1510
michael@0 1511 static bool
michael@0 1512 HandleShapeGuardFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
michael@0 1513 {
michael@0 1514 IonSpew(IonSpew_Bailouts, "Shape guard failure %s:%d, inlined into %s:%d",
michael@0 1515 innerScript->filename(), innerScript->lineno(),
michael@0 1516 outerScript->filename(), outerScript->lineno());
michael@0 1517
michael@0 1518 JS_ASSERT(!outerScript->ionScript()->invalidated());
michael@0 1519
michael@0 1520 // TODO: Currently this mimic's Ion's handling of this case. Investigate setting
michael@0 1521 // the flag on innerScript as opposed to outerScript, and maybe invalidating both
michael@0 1522 // inner and outer scripts, instead of just the outer one.
michael@0 1523 outerScript->setFailedShapeGuard();
michael@0 1524 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to shape guard failure");
michael@0 1525 return Invalidate(cx, outerScript);
michael@0 1526 }
michael@0 1527
michael@0 1528 static bool
michael@0 1529 HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerScript)
michael@0 1530 {
michael@0 1531 IonSpew(IonSpew_Bailouts, "Baseline info failure %s:%d, inlined into %s:%d",
michael@0 1532 innerScript->filename(), innerScript->lineno(),
michael@0 1533 outerScript->filename(), outerScript->lineno());
michael@0 1534
michael@0 1535 JS_ASSERT(!outerScript->ionScript()->invalidated());
michael@0 1536
michael@0 1537 IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
michael@0 1538 return Invalidate(cx, outerScript);
michael@0 1539 }
michael@0 1540
michael@0 1541 static bool
michael@0 1542 CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
michael@0 1543 BaselineFrame *frame)
michael@0 1544 {
michael@0 1545 RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
michael@0 1546
michael@0 1547 // We might not have rematerialized a frame if the user never requested a
michael@0 1548 // Debugger.Frame for it.
michael@0 1549 if (!rematFrame)
michael@0 1550 return true;
michael@0 1551
michael@0 1552 MOZ_ASSERT(rematFrame->script() == frame->script());
michael@0 1553 MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
michael@0 1554
michael@0 1555 frame->setScopeChain(rematFrame->scopeChain());
michael@0 1556 frame->thisValue() = rematFrame->thisValue();
michael@0 1557
michael@0 1558 for (unsigned i = 0; i < frame->numActualArgs(); i++)
michael@0 1559 frame->argv()[i] = rematFrame->argv()[i];
michael@0 1560
michael@0 1561 for (size_t i = 0; i < frame->script()->nfixed(); i++)
michael@0 1562 *frame->valueSlot(i) = rematFrame->locals()[i];
michael@0 1563
michael@0 1564 IonSpew(IonSpew_BaselineBailouts,
michael@0 1565 " Copied from rematerialized frame at (%p,%u)",
michael@0 1566 fp, inlineDepth);
michael@0 1567
michael@0 1568 if (cx->compartment()->debugMode())
michael@0 1569 return Debugger::handleIonBailout(cx, rematFrame, frame);
michael@0 1570
michael@0 1571 return true;
michael@0 1572 }
michael@0 1573
michael@0 1574 uint32_t
michael@0 1575 jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
michael@0 1576 {
michael@0 1577 // The caller pushes R0 and R1 on the stack without rooting them.
michael@0 1578 // Since GC here is very unlikely just suppress it.
michael@0 1579 JSContext *cx = GetJSContextFromJitCode();
michael@0 1580 js::gc::AutoSuppressGC suppressGC(cx);
michael@0 1581
michael@0 1582 IonSpew(IonSpew_BaselineBailouts, " Done restoring frames");
michael@0 1583
michael@0 1584 // Check that we can get the current script's PC.
michael@0 1585 #ifdef DEBUG
michael@0 1586 jsbytecode *pc;
michael@0 1587 cx->currentScript(&pc);
michael@0 1588 IonSpew(IonSpew_BaselineBailouts, " Got pc=%p", pc);
michael@0 1589 #endif
michael@0 1590
michael@0 1591 uint32_t numFrames = bailoutInfo->numFrames;
michael@0 1592 JS_ASSERT(numFrames > 0);
michael@0 1593 BailoutKind bailoutKind = bailoutInfo->bailoutKind;
michael@0 1594
michael@0 1595 // Free the bailout buffer.
michael@0 1596 js_free(bailoutInfo);
michael@0 1597 bailoutInfo = nullptr;
michael@0 1598
michael@0 1599 // Ensure the frame has a call object if it needs one. If the scope chain
michael@0 1600 // is nullptr, we will enter baseline code at the prologue so no need to do
michael@0 1601 // anything in that case.
michael@0 1602 BaselineFrame *topFrame = GetTopBaselineFrame(cx);
michael@0 1603 if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame))
michael@0 1604 return false;
michael@0 1605
michael@0 1606 // Create arguments objects for bailed out frames, to maintain the invariant
michael@0 1607 // that script->needsArgsObj() implies frame->hasArgsObj().
michael@0 1608 RootedScript innerScript(cx, nullptr);
michael@0 1609 RootedScript outerScript(cx, nullptr);
michael@0 1610
michael@0 1611 JS_ASSERT(cx->currentlyRunningInJit());
michael@0 1612 JitFrameIterator iter(cx);
michael@0 1613 uint8_t *outerFp = nullptr;
michael@0 1614
michael@0 1615 uint32_t frameno = 0;
michael@0 1616 while (frameno < numFrames) {
michael@0 1617 JS_ASSERT(!iter.isIonJS());
michael@0 1618
michael@0 1619 if (iter.isBaselineJS()) {
michael@0 1620 BaselineFrame *frame = iter.baselineFrame();
michael@0 1621 MOZ_ASSERT(frame->script()->hasBaselineScript());
michael@0 1622
michael@0 1623 // If the frame doesn't even have a scope chain set yet, then it's resuming
michael@0 1624 // into the the prologue before the scope chain is initialized. Any
michael@0 1625 // necessary args object will also be initialized there.
michael@0 1626 if (frame->scopeChain() && frame->script()->needsArgsObj()) {
michael@0 1627 ArgumentsObject *argsObj;
michael@0 1628 if (frame->hasArgsObj()) {
michael@0 1629 argsObj = &frame->argsObj();
michael@0 1630 } else {
michael@0 1631 argsObj = ArgumentsObject::createExpected(cx, frame);
michael@0 1632 if (!argsObj)
michael@0 1633 return false;
michael@0 1634 }
michael@0 1635
michael@0 1636 // The arguments is a local binding and needsArgsObj does not
michael@0 1637 // check if it is clobbered. Ensure that the local binding
michael@0 1638 // restored during bailout before storing the arguments object
michael@0 1639 // to the slot.
michael@0 1640 RootedScript script(cx, frame->script());
michael@0 1641 SetFrameArgumentsObject(cx, frame, script, argsObj);
michael@0 1642 }
michael@0 1643
michael@0 1644 if (frameno == 0)
michael@0 1645 innerScript = frame->script();
michael@0 1646
michael@0 1647 if (frameno == numFrames - 1) {
michael@0 1648 outerScript = frame->script();
michael@0 1649 outerFp = iter.fp();
michael@0 1650 }
michael@0 1651
michael@0 1652 frameno++;
michael@0 1653 }
michael@0 1654
michael@0 1655 ++iter;
michael@0 1656 }
michael@0 1657
michael@0 1658 MOZ_ASSERT(innerScript);
michael@0 1659 MOZ_ASSERT(outerScript);
michael@0 1660 MOZ_ASSERT(outerFp);
michael@0 1661
michael@0 1662 // If we rematerialized Ion frames due to debug mode toggling, copy their
michael@0 1663 // values into the baseline frame. We need to do this even when debug mode
michael@0 1664 // is off, as we should respect the mutations made while debug mode was
michael@0 1665 // on.
michael@0 1666 JitActivation *act = cx->mainThread().activation()->asJit();
michael@0 1667 if (act->hasRematerializedFrame(outerFp)) {
michael@0 1668 JitFrameIterator iter(cx);
michael@0 1669 size_t inlineDepth = numFrames;
michael@0 1670 while (inlineDepth > 0) {
michael@0 1671 if (iter.isBaselineJS() &&
michael@0 1672 !CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
michael@0 1673 iter.baselineFrame()))
michael@0 1674 {
michael@0 1675 return false;
michael@0 1676 }
michael@0 1677 ++iter;
michael@0 1678 }
michael@0 1679
michael@0 1680 // After copying from all the rematerialized frames, remove them from
michael@0 1681 // the table to keep the table up to date.
michael@0 1682 act->removeRematerializedFrame(outerFp);
michael@0 1683 }
michael@0 1684
michael@0 1685 IonSpew(IonSpew_BaselineBailouts,
michael@0 1686 " Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
michael@0 1687 outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(),
michael@0 1688 innerScript->filename(), innerScript->lineno(), innerScript->getUseCount(),
michael@0 1689 (unsigned) bailoutKind);
michael@0 1690
michael@0 1691 switch (bailoutKind) {
michael@0 1692 case Bailout_Normal:
michael@0 1693 // Do nothing.
michael@0 1694 break;
michael@0 1695 case Bailout_ArgumentCheck:
michael@0 1696 // Do nothing, bailout will resume before the argument monitor ICs.
michael@0 1697 break;
michael@0 1698 case Bailout_BoundsCheck:
michael@0 1699 if (!HandleBoundsCheckFailure(cx, outerScript, innerScript))
michael@0 1700 return false;
michael@0 1701 break;
michael@0 1702 case Bailout_ShapeGuard:
michael@0 1703 if (!HandleShapeGuardFailure(cx, outerScript, innerScript))
michael@0 1704 return false;
michael@0 1705 break;
michael@0 1706 case Bailout_BaselineInfo:
michael@0 1707 if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
michael@0 1708 return false;
michael@0 1709 break;
michael@0 1710 case Bailout_IonExceptionDebugMode:
michael@0 1711 // Return false to resume in HandleException with reconstructed
michael@0 1712 // baseline frame.
michael@0 1713 return false;
michael@0 1714 default:
michael@0 1715 MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!");
michael@0 1716 }
michael@0 1717
michael@0 1718 if (!CheckFrequentBailouts(cx, outerScript))
michael@0 1719 return false;
michael@0 1720
michael@0 1721 return true;
michael@0 1722 }

mercurial