1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/BaselineBailouts.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1722 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jsprf.h" 1.11 +#include "jit/arm/Simulator-arm.h" 1.12 +#include "jit/BaselineIC.h" 1.13 +#include "jit/BaselineJIT.h" 1.14 +#include "jit/CompileInfo.h" 1.15 +#include "jit/IonSpewer.h" 1.16 +#include "jit/Recover.h" 1.17 +#include "jit/RematerializedFrame.h" 1.18 + 1.19 +#include "vm/ArgumentsObject.h" 1.20 +#include "vm/Debugger.h" 1.21 +#include "vm/TraceLogging.h" 1.22 + 1.23 +#include "jsscriptinlines.h" 1.24 + 1.25 +#include "jit/IonFrames-inl.h" 1.26 + 1.27 +using namespace js; 1.28 +using namespace js::jit; 1.29 + 1.30 +// BaselineStackBuilder may reallocate its buffer if the current one is too 1.31 +// small. To avoid dangling pointers, BufferPointer represents a pointer into 1.32 +// this buffer as a pointer to the header and a fixed offset. 1.33 +template <typename T> 1.34 +class BufferPointer 1.35 +{ 1.36 + BaselineBailoutInfo **header_; 1.37 + size_t offset_; 1.38 + bool heap_; 1.39 + 1.40 + public: 1.41 + BufferPointer(BaselineBailoutInfo **header, size_t offset, bool heap) 1.42 + : header_(header), offset_(offset), heap_(heap) 1.43 + { } 1.44 + 1.45 + T *get() const { 1.46 + BaselineBailoutInfo *header = *header_; 1.47 + if (!heap_) 1.48 + return (T*)(header->incomingStack + offset_); 1.49 + 1.50 + uint8_t *p = header->copyStackTop - offset_; 1.51 + JS_ASSERT(p >= header->copyStackBottom && p < header->copyStackTop); 1.52 + return (T*)p; 1.53 + } 1.54 + 1.55 + T &operator*() const { return *get(); } 1.56 + T *operator->() const { return get(); } 1.57 +}; 1.58 + 1.59 +/** 1.60 + * BaselineStackBuilder helps abstract the process of rebuilding the C stack on the heap. 1.61 + * It takes a bailout iterator and keeps track of the point on the C stack from which 1.62 + * the reconstructed frames will be written. 1.63 + * 1.64 + * It exposes methods to write data into the heap memory storing the reconstructed 1.65 + * stack. It also exposes method to easily calculate addresses. This includes both the 1.66 + * virtual address that a particular value will be at when it's eventually copied onto 1.67 + * the stack, as well as the current actual address of that value (whether on the heap 1.68 + * allocated portion being constructed or the existing stack). 1.69 + * 1.70 + * The abstraction handles transparent re-allocation of the heap memory when it 1.71 + * needs to be enlarged to accomodate new data. Similarly to the C stack, the 1.72 + * data that's written to the reconstructed stack grows from high to low in memory. 1.73 + * 1.74 + * The lowest region of the allocated memory contains a BaselineBailoutInfo structure that 1.75 + * points to the start and end of the written data. 1.76 + */ 1.77 +struct BaselineStackBuilder 1.78 +{ 1.79 + IonBailoutIterator &iter_; 1.80 + IonJSFrameLayout *frame_; 1.81 + 1.82 + static size_t HeaderSize() { 1.83 + return AlignBytes(sizeof(BaselineBailoutInfo), sizeof(void *)); 1.84 + }; 1.85 + size_t bufferTotal_; 1.86 + size_t bufferAvail_; 1.87 + size_t bufferUsed_; 1.88 + uint8_t *buffer_; 1.89 + BaselineBailoutInfo *header_; 1.90 + 1.91 + size_t framePushed_; 1.92 + 1.93 + BaselineStackBuilder(IonBailoutIterator &iter, size_t initialSize) 1.94 + : iter_(iter), 1.95 + frame_(static_cast<IonJSFrameLayout*>(iter.current())), 1.96 + bufferTotal_(initialSize), 1.97 + bufferAvail_(0), 1.98 + bufferUsed_(0), 1.99 + buffer_(nullptr), 1.100 + header_(nullptr), 1.101 + framePushed_(0) 1.102 + { 1.103 + JS_ASSERT(bufferTotal_ >= HeaderSize()); 1.104 + } 1.105 + 1.106 + ~BaselineStackBuilder() { 1.107 + js_free(buffer_); 1.108 + } 1.109 + 1.110 + bool init() { 1.111 + JS_ASSERT(!buffer_); 1.112 + JS_ASSERT(bufferUsed_ == 0); 1.113 + buffer_ = reinterpret_cast<uint8_t *>(js_calloc(bufferTotal_)); 1.114 + if (!buffer_) 1.115 + return false; 1.116 + bufferAvail_ = bufferTotal_ - HeaderSize(); 1.117 + bufferUsed_ = 0; 1.118 + 1.119 + header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_); 1.120 + header_->incomingStack = reinterpret_cast<uint8_t *>(frame_); 1.121 + header_->copyStackTop = buffer_ + bufferTotal_; 1.122 + header_->copyStackBottom = header_->copyStackTop; 1.123 + header_->setR0 = 0; 1.124 + header_->valueR0 = UndefinedValue(); 1.125 + header_->setR1 = 0; 1.126 + header_->valueR1 = UndefinedValue(); 1.127 + header_->resumeFramePtr = nullptr; 1.128 + header_->resumeAddr = nullptr; 1.129 + header_->monitorStub = nullptr; 1.130 + header_->numFrames = 0; 1.131 + return true; 1.132 + } 1.133 + 1.134 + bool enlarge() { 1.135 + JS_ASSERT(buffer_ != nullptr); 1.136 + if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value) 1.137 + return false; 1.138 + size_t newSize = bufferTotal_ * 2; 1.139 + uint8_t *newBuffer = reinterpret_cast<uint8_t *>(js_calloc(newSize)); 1.140 + if (!newBuffer) 1.141 + return false; 1.142 + memcpy((newBuffer + newSize) - bufferUsed_, header_->copyStackBottom, bufferUsed_); 1.143 + memcpy(newBuffer, header_, sizeof(BaselineBailoutInfo)); 1.144 + js_free(buffer_); 1.145 + buffer_ = newBuffer; 1.146 + bufferTotal_ = newSize; 1.147 + bufferAvail_ = newSize - (HeaderSize() + bufferUsed_); 1.148 + 1.149 + header_ = reinterpret_cast<BaselineBailoutInfo *>(buffer_); 1.150 + header_->copyStackTop = buffer_ + bufferTotal_; 1.151 + header_->copyStackBottom = header_->copyStackTop - bufferUsed_; 1.152 + return true; 1.153 + } 1.154 + 1.155 + BaselineBailoutInfo *info() { 1.156 + JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_)); 1.157 + return header_; 1.158 + } 1.159 + 1.160 + BaselineBailoutInfo *takeBuffer() { 1.161 + JS_ASSERT(header_ == reinterpret_cast<BaselineBailoutInfo *>(buffer_)); 1.162 + buffer_ = nullptr; 1.163 + return header_; 1.164 + } 1.165 + 1.166 + void resetFramePushed() { 1.167 + framePushed_ = 0; 1.168 + } 1.169 + 1.170 + size_t framePushed() const { 1.171 + return framePushed_; 1.172 + } 1.173 + 1.174 + bool subtract(size_t size, const char *info = nullptr) { 1.175 + // enlarge the buffer if need be. 1.176 + while (size > bufferAvail_) { 1.177 + if (!enlarge()) 1.178 + return false; 1.179 + } 1.180 + 1.181 + // write out element. 1.182 + header_->copyStackBottom -= size; 1.183 + bufferAvail_ -= size; 1.184 + bufferUsed_ += size; 1.185 + framePushed_ += size; 1.186 + if (info) { 1.187 + IonSpew(IonSpew_BaselineBailouts, 1.188 + " SUB_%03d %p/%p %-15s", 1.189 + (int) size, header_->copyStackBottom, virtualPointerAtStackOffset(0), info); 1.190 + } 1.191 + return true; 1.192 + } 1.193 + 1.194 + template <typename T> 1.195 + bool write(const T &t) { 1.196 + if (!subtract(sizeof(T))) 1.197 + return false; 1.198 + memcpy(header_->copyStackBottom, &t, sizeof(T)); 1.199 + return true; 1.200 + } 1.201 + 1.202 + template <typename T> 1.203 + bool writePtr(T *t, const char *info) { 1.204 + if (!write<T *>(t)) 1.205 + return false; 1.206 + if (info) 1.207 + IonSpew(IonSpew_BaselineBailouts, 1.208 + " WRITE_PTR %p/%p %-15s %p", 1.209 + header_->copyStackBottom, virtualPointerAtStackOffset(0), info, t); 1.210 + return true; 1.211 + } 1.212 + 1.213 + bool writeWord(size_t w, const char *info) { 1.214 + if (!write<size_t>(w)) 1.215 + return false; 1.216 + if (info) { 1.217 + if (sizeof(size_t) == 4) { 1.218 + IonSpew(IonSpew_BaselineBailouts, 1.219 + " WRITE_WRD %p/%p %-15s %08x", 1.220 + header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w); 1.221 + } else { 1.222 + IonSpew(IonSpew_BaselineBailouts, 1.223 + " WRITE_WRD %p/%p %-15s %016llx", 1.224 + header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w); 1.225 + } 1.226 + } 1.227 + return true; 1.228 + } 1.229 + 1.230 + bool writeValue(Value val, const char *info) { 1.231 + if (!write<Value>(val)) 1.232 + return false; 1.233 + if (info) { 1.234 + IonSpew(IonSpew_BaselineBailouts, 1.235 + " WRITE_VAL %p/%p %-15s %016llx", 1.236 + header_->copyStackBottom, virtualPointerAtStackOffset(0), info, 1.237 + *((uint64_t *) &val)); 1.238 + } 1.239 + return true; 1.240 + } 1.241 + 1.242 + Value popValue() { 1.243 + JS_ASSERT(bufferUsed_ >= sizeof(Value)); 1.244 + JS_ASSERT(framePushed_ >= sizeof(Value)); 1.245 + bufferAvail_ += sizeof(Value); 1.246 + bufferUsed_ -= sizeof(Value); 1.247 + framePushed_ -= sizeof(Value); 1.248 + Value result = *((Value *) header_->copyStackBottom); 1.249 + header_->copyStackBottom += sizeof(Value); 1.250 + return result; 1.251 + } 1.252 + 1.253 + void popValueInto(PCMappingSlotInfo::SlotLocation loc) { 1.254 + JS_ASSERT(PCMappingSlotInfo::ValidSlotLocation(loc)); 1.255 + switch(loc) { 1.256 + case PCMappingSlotInfo::SlotInR0: 1.257 + header_->setR0 = 1; 1.258 + header_->valueR0 = popValue(); 1.259 + break; 1.260 + case PCMappingSlotInfo::SlotInR1: 1.261 + header_->setR1 = 1; 1.262 + header_->valueR1 = popValue(); 1.263 + break; 1.264 + default: 1.265 + JS_ASSERT(loc == PCMappingSlotInfo::SlotIgnore); 1.266 + popValue(); 1.267 + break; 1.268 + } 1.269 + } 1.270 + 1.271 + void setResumeFramePtr(void *resumeFramePtr) { 1.272 + header_->resumeFramePtr = resumeFramePtr; 1.273 + } 1.274 + 1.275 + void setResumeAddr(void *resumeAddr) { 1.276 + header_->resumeAddr = resumeAddr; 1.277 + } 1.278 + 1.279 + void setMonitorStub(ICStub *stub) { 1.280 + header_->monitorStub = stub; 1.281 + } 1.282 + 1.283 + template <typename T> 1.284 + BufferPointer<T> pointerAtStackOffset(size_t offset) { 1.285 + if (offset < bufferUsed_) { 1.286 + // Calculate offset from copyStackTop. 1.287 + offset = header_->copyStackTop - (header_->copyStackBottom + offset); 1.288 + return BufferPointer<T>(&header_, offset, /* heap = */ true); 1.289 + } 1.290 + 1.291 + return BufferPointer<T>(&header_, offset - bufferUsed_, /* heap = */ false); 1.292 + } 1.293 + 1.294 + BufferPointer<Value> valuePointerAtStackOffset(size_t offset) { 1.295 + return pointerAtStackOffset<Value>(offset); 1.296 + } 1.297 + 1.298 + inline uint8_t *virtualPointerAtStackOffset(size_t offset) { 1.299 + if (offset < bufferUsed_) 1.300 + return reinterpret_cast<uint8_t *>(frame_) - (bufferUsed_ - offset); 1.301 + return reinterpret_cast<uint8_t *>(frame_) + (offset - bufferUsed_); 1.302 + } 1.303 + 1.304 + inline IonJSFrameLayout *startFrame() { 1.305 + return frame_; 1.306 + } 1.307 + 1.308 + BufferPointer<IonJSFrameLayout> topFrameAddress() { 1.309 + return pointerAtStackOffset<IonJSFrameLayout>(0); 1.310 + } 1.311 + 1.312 + // 1.313 + // This method should only be called when the builder is in a state where it is 1.314 + // starting to construct the stack frame for the next callee. This means that 1.315 + // the lowest value on the constructed stack is the return address for the previous 1.316 + // caller frame. 1.317 + // 1.318 + // This method is used to compute the value of the frame pointer (e.g. ebp on x86) 1.319 + // that would have been saved by the baseline jitcode when it was entered. In some 1.320 + // cases, this value can be bogus since we can ensure that the caller would have saved 1.321 + // it anyway. 1.322 + // 1.323 + void *calculatePrevFramePtr() { 1.324 + // Get the incoming frame. 1.325 + BufferPointer<IonJSFrameLayout> topFrame = topFrameAddress(); 1.326 + FrameType type = topFrame->prevType(); 1.327 + 1.328 + // For IonJS and Entry frames, the "saved" frame pointer in the baseline 1.329 + // frame is meaningless, since Ion saves all registers before calling other ion 1.330 + // frames, and the entry frame saves all registers too. 1.331 + if (type == JitFrame_IonJS || type == JitFrame_Entry) 1.332 + return nullptr; 1.333 + 1.334 + // BaselineStub - Baseline calling into Ion. 1.335 + // PrevFramePtr needs to point to the BaselineStubFrame's saved frame pointer. 1.336 + // STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE 1.337 + // - IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr() 1.338 + if (type == JitFrame_BaselineStub) { 1.339 + size_t offset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize() + 1.340 + IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr(); 1.341 + return virtualPointerAtStackOffset(offset); 1.342 + } 1.343 + 1.344 + JS_ASSERT(type == JitFrame_Rectifier); 1.345 + // Rectifier - behaviour depends on the frame preceding the rectifier frame, and 1.346 + // whether the arch is x86 or not. The x86 rectifier frame saves the frame pointer, 1.347 + // so we can calculate it directly. For other archs, the previous frame pointer 1.348 + // is stored on the stack in the frame that precedes the rectifier frame. 1.349 + size_t priorOffset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize(); 1.350 +#if defined(JS_CODEGEN_X86) 1.351 + // On X86, the FramePointer is pushed as the first value in the Rectifier frame. 1.352 + JS_ASSERT(BaselineFrameReg == FramePointer); 1.353 + priorOffset -= sizeof(void *); 1.354 + return virtualPointerAtStackOffset(priorOffset); 1.355 +#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) 1.356 + // On X64, ARM and MIPS, the frame pointer save location depends on 1.357 + // the caller of the rectifier frame. 1.358 + BufferPointer<IonRectifierFrameLayout> priorFrame = 1.359 + pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset); 1.360 + FrameType priorType = priorFrame->prevType(); 1.361 + JS_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub); 1.362 + 1.363 + // If the frame preceding the rectifier is an IonJS frame, then once again 1.364 + // the frame pointer does not matter. 1.365 + if (priorType == JitFrame_IonJS) 1.366 + return nullptr; 1.367 + 1.368 + // Otherwise, the frame preceding the rectifier is a BaselineStub frame. 1.369 + // let X = STACK_START_ADDR + IonJSFrameLayout::Size() + PREV_FRAME_SIZE 1.370 + // X + IonRectifierFrameLayout::Size() 1.371 + // + ((IonRectifierFrameLayout *) X)->prevFrameLocalSize() 1.372 + // - BaselineStubFrameLayout::reverseOffsetOfSavedFramePtr() 1.373 + size_t extraOffset = IonRectifierFrameLayout::Size() + priorFrame->prevFrameLocalSize() + 1.374 + IonBaselineStubFrameLayout::reverseOffsetOfSavedFramePtr(); 1.375 + return virtualPointerAtStackOffset(priorOffset + extraOffset); 1.376 +#else 1.377 +# error "Bad architecture!" 1.378 +#endif 1.379 + } 1.380 +}; 1.381 + 1.382 +static inline bool 1.383 +IsInlinableFallback(ICFallbackStub *icEntry) 1.384 +{ 1.385 + return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() || 1.386 + icEntry->isSetProp_Fallback(); 1.387 +} 1.388 + 1.389 +static inline void* 1.390 +GetStubReturnAddress(JSContext *cx, jsbytecode *pc) 1.391 +{ 1.392 + if (IsGetPropPC(pc)) 1.393 + return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr(); 1.394 + if (IsSetPropPC(pc)) 1.395 + return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr(); 1.396 + // This should be a call op of some kind, now. 1.397 + JS_ASSERT(IsCallPC(pc)); 1.398 + return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr(); 1.399 +} 1.400 + 1.401 +static inline jsbytecode * 1.402 +GetNextNonLoopEntryPc(jsbytecode *pc) 1.403 +{ 1.404 + JSOp op = JSOp(*pc); 1.405 + if (op == JSOP_GOTO) 1.406 + return pc + GET_JUMP_OFFSET(pc); 1.407 + if (op == JSOP_LOOPENTRY || op == JSOP_NOP || op == JSOP_LOOPHEAD) 1.408 + return GetNextPc(pc); 1.409 + return pc; 1.410 +} 1.411 + 1.412 +// For every inline frame, we write out the following data: 1.413 +// 1.414 +// | ... | 1.415 +// +---------------+ 1.416 +// | Descr(???) | --- Descr size here is (PREV_FRAME_SIZE) 1.417 +// +---------------+ 1.418 +// | ReturnAddr | 1.419 +// -- +===============+ --- OVERWRITE STARTS HERE (START_STACK_ADDR) 1.420 +// | | PrevFramePtr | 1.421 +// | +-> +---------------+ 1.422 +// | | | Baseline | 1.423 +// | | | Frame | 1.424 +// | | +---------------+ 1.425 +// | | | Fixed0 | 1.426 +// | | +---------------+ 1.427 +// +--< | | ... | 1.428 +// | | | +---------------+ 1.429 +// | | | | FixedF | 1.430 +// | | | +---------------+ 1.431 +// | | | | Stack0 | 1.432 +// | | | +---------------+ 1.433 +// | | | | ... | 1.434 +// | | | +---------------+ 1.435 +// | | | | StackS | 1.436 +// | -- | +---------------+ --- IF NOT LAST INLINE FRAME, 1.437 +// +------------| Descr(BLJS) | --- CALLING INFO STARTS HERE 1.438 +// | +---------------+ 1.439 +// | | ReturnAddr | <-- return into main jitcode after IC 1.440 +// -- | +===============+ 1.441 +// | | | StubPtr | 1.442 +// | | +---------------+ 1.443 +// | +---| FramePtr | 1.444 +// | +---------------+ 1.445 +// | | ArgA | 1.446 +// | +---------------+ 1.447 +// | | ... | 1.448 +// +--< +---------------+ 1.449 +// | | | Arg0 | 1.450 +// | | +---------------+ 1.451 +// | | | ThisV | 1.452 +// | -- +---------------+ 1.453 +// | | ActualArgC | 1.454 +// | +---------------+ 1.455 +// | | CalleeToken | 1.456 +// | +---------------+ 1.457 +// +------------| Descr(BLStub) | 1.458 +// +---------------+ 1.459 +// | ReturnAddr | <-- return into ICCall_Scripted IC 1.460 +// -- +===============+ --- IF CALLEE FORMAL ARGS > ActualArgC 1.461 +// | | UndefinedU | 1.462 +// | +---------------+ 1.463 +// | | ... | 1.464 +// | +---------------+ 1.465 +// | | Undefined0 | 1.466 +// | +---------------+ 1.467 +// +--< | ArgA | 1.468 +// | | +---------------+ 1.469 +// | | | ... | 1.470 +// | | +---------------+ 1.471 +// | | | Arg0 | 1.472 +// | | +---------------+ 1.473 +// | | | ThisV | 1.474 +// | -- +---------------+ 1.475 +// | | ActualArgC | 1.476 +// | +---------------+ 1.477 +// | | CalleeToken | 1.478 +// | +---------------+ 1.479 +// +------------| Descr(Rect) | 1.480 +// +---------------+ 1.481 +// | ReturnAddr | <-- return into ArgumentsRectifier after call 1.482 +// +===============+ 1.483 +// 1.484 +static bool 1.485 +InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, 1.486 + HandleFunction fun, HandleScript script, IonScript *ionScript, 1.487 + SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder, 1.488 + AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee, 1.489 + jsbytecode **callPC, const ExceptionBailoutInfo *excInfo) 1.490 +{ 1.491 + MOZ_ASSERT(script->hasBaselineScript()); 1.492 + 1.493 + // Are we catching an exception? 1.494 + bool catchingException = excInfo && excInfo->catchingException(); 1.495 + 1.496 + // If we are catching an exception, we are bailing out to a catch or 1.497 + // finally block and this is the frame where we will resume. Usually the 1.498 + // expression stack should be empty in this case but there can be 1.499 + // iterators on the stack. 1.500 + uint32_t exprStackSlots; 1.501 + if (catchingException) 1.502 + exprStackSlots = excInfo->numExprSlots(); 1.503 + else 1.504 + exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun)); 1.505 + 1.506 + builder.resetFramePushed(); 1.507 + 1.508 + // Build first baseline frame: 1.509 + // +===============+ 1.510 + // | PrevFramePtr | 1.511 + // +---------------+ 1.512 + // | Baseline | 1.513 + // | Frame | 1.514 + // +---------------+ 1.515 + // | Fixed0 | 1.516 + // +---------------+ 1.517 + // | ... | 1.518 + // +---------------+ 1.519 + // | FixedF | 1.520 + // +---------------+ 1.521 + // | Stack0 | 1.522 + // +---------------+ 1.523 + // | ... | 1.524 + // +---------------+ 1.525 + // | StackS | 1.526 + // +---------------+ --- IF NOT LAST INLINE FRAME, 1.527 + // | Descr(BLJS) | --- CALLING INFO STARTS HERE 1.528 + // +---------------+ 1.529 + // | ReturnAddr | <-- return into main jitcode after IC 1.530 + // +===============+ 1.531 + 1.532 + IonSpew(IonSpew_BaselineBailouts, " Unpacking %s:%d", script->filename(), script->lineno()); 1.533 + IonSpew(IonSpew_BaselineBailouts, " [BASELINE-JS FRAME]"); 1.534 + 1.535 + // Calculate and write the previous frame pointer value. 1.536 + // Record the virtual stack offset at this location. Later on, if we end up 1.537 + // writing out a BaselineStub frame for the next callee, we'll need to save the 1.538 + // address. 1.539 + void *prevFramePtr = builder.calculatePrevFramePtr(); 1.540 + if (!builder.writePtr(prevFramePtr, "PrevFramePtr")) 1.541 + return false; 1.542 + prevFramePtr = builder.virtualPointerAtStackOffset(0); 1.543 + 1.544 + // Write struct BaselineFrame. 1.545 + if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame")) 1.546 + return false; 1.547 + BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0); 1.548 + 1.549 + // Initialize BaselineFrame::frameSize 1.550 + uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset + 1.551 + (sizeof(Value) * (script->nfixed() + exprStackSlots)); 1.552 + IonSpew(IonSpew_BaselineBailouts, " FrameSize=%d", (int) frameSize); 1.553 + blFrame->setFrameSize(frameSize); 1.554 + 1.555 + uint32_t flags = 0; 1.556 + 1.557 + // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry. 1.558 + // This may be wrong for the last frame of ArgumentCheck bailout, but 1.559 + // that will be fixed later. 1.560 + if (ionScript->hasSPSInstrumentation()) { 1.561 + if (callerPC == nullptr) { 1.562 + IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on top frame!"); 1.563 + flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME; 1.564 + } else if (js_JitOptions.profileInlineFrames) { 1.565 + IonSpew(IonSpew_BaselineBailouts, " Setting SPS flag on inline frame!"); 1.566 + flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME; 1.567 + } 1.568 + } 1.569 + 1.570 + // Initialize BaselineFrame's scopeChain and argsObj 1.571 + JSObject *scopeChain = nullptr; 1.572 + Value returnValue; 1.573 + ArgumentsObject *argsObj = nullptr; 1.574 + BailoutKind bailoutKind = iter.bailoutKind(); 1.575 + if (bailoutKind == Bailout_ArgumentCheck) { 1.576 + // Temporary hack -- skip the (unused) scopeChain, because it could be 1.577 + // bogus (we can fail before the scope chain slot is set). Strip the 1.578 + // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|, 1.579 + // which calls |EnsureHasScopeObjects|. 1.580 + IonSpew(IonSpew_BaselineBailouts, " Bailout_ArgumentCheck! (no valid scopeChain)"); 1.581 + iter.skip(); 1.582 + 1.583 + // skip |return value| 1.584 + iter.skip(); 1.585 + returnValue = UndefinedValue(); 1.586 + 1.587 + // Scripts with |argumentsHasVarBinding| have an extra slot. 1.588 + if (script->argumentsHasVarBinding()) { 1.589 + IonSpew(IonSpew_BaselineBailouts, 1.590 + " Bailout_ArgumentCheck for script with argumentsHasVarBinding!" 1.591 + "Using empty arguments object"); 1.592 + iter.skip(); 1.593 + } 1.594 + } else { 1.595 + Value v = iter.read(); 1.596 + if (v.isObject()) { 1.597 + scopeChain = &v.toObject(); 1.598 + if (fun && fun->isHeavyweight()) 1.599 + flags |= BaselineFrame::HAS_CALL_OBJ; 1.600 + } else { 1.601 + JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT)); 1.602 + 1.603 + // Get scope chain from function or script. 1.604 + if (fun) { 1.605 + // If pcOffset == 0, we may have to push a new call object, so 1.606 + // we leave scopeChain nullptr and enter baseline code before 1.607 + // the prologue. 1.608 + if (iter.pcOffset() != 0 || iter.resumeAfter()) 1.609 + scopeChain = fun->environment(); 1.610 + } else { 1.611 + // For global, compile-and-go scripts the scope chain is the 1.612 + // script's global (Ion does not compile non-compile-and-go 1.613 + // scripts). Also note that it's invalid to resume into the 1.614 + // prologue in this case because the prologue expects the scope 1.615 + // chain in R1 for eval and global scripts. 1.616 + JS_ASSERT(!script->isForEval()); 1.617 + JS_ASSERT(script->compileAndGo()); 1.618 + scopeChain = &(script->global()); 1.619 + } 1.620 + } 1.621 + 1.622 + // Make sure to add HAS_RVAL to flags here because setFlags() below 1.623 + // will clobber it. 1.624 + returnValue = iter.read(); 1.625 + flags |= BaselineFrame::HAS_RVAL; 1.626 + 1.627 + // If script maybe has an arguments object, the third slot will hold it. 1.628 + if (script->argumentsHasVarBinding()) { 1.629 + v = iter.read(); 1.630 + JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT)); 1.631 + if (v.isObject()) 1.632 + argsObj = &v.toObject().as<ArgumentsObject>(); 1.633 + } 1.634 + } 1.635 + IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain); 1.636 + blFrame->setScopeChain(scopeChain); 1.637 + IonSpew(IonSpew_BaselineBailouts, " ReturnValue=%016llx", *((uint64_t *) &returnValue)); 1.638 + blFrame->setReturnValue(returnValue); 1.639 + 1.640 + // Do not need to initialize scratchValue field in BaselineFrame. 1.641 + blFrame->setFlags(flags); 1.642 + 1.643 + // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags. 1.644 + if (argsObj) 1.645 + blFrame->initArgsObjUnchecked(*argsObj); 1.646 + 1.647 + if (fun) { 1.648 + // The unpacked thisv and arguments should overwrite the pushed args present 1.649 + // in the calling frame. 1.650 + Value thisv = iter.read(); 1.651 + IonSpew(IonSpew_BaselineBailouts, " Is function!"); 1.652 + IonSpew(IonSpew_BaselineBailouts, " thisv=%016llx", *((uint64_t *) &thisv)); 1.653 + 1.654 + size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis(); 1.655 + *builder.valuePointerAtStackOffset(thisvOffset) = thisv; 1.656 + 1.657 + JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun)); 1.658 + IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u", 1.659 + iter.numAllocations(), fun->nargs(), script->nfixed()); 1.660 + 1.661 + if (!callerPC) { 1.662 + // This is the first frame. Store the formals in a Vector until we 1.663 + // are done. Due to UCE and phi elimination, we could store an 1.664 + // UndefinedValue() here for formals we think are unused, but 1.665 + // locals may still reference the original argument slot 1.666 + // (MParameter/LArgument) and expect the original Value. 1.667 + JS_ASSERT(startFrameFormals.empty()); 1.668 + if (!startFrameFormals.resize(fun->nargs())) 1.669 + return false; 1.670 + } 1.671 + 1.672 + for (uint32_t i = 0; i < fun->nargs(); i++) { 1.673 + Value arg = iter.read(); 1.674 + IonSpew(IonSpew_BaselineBailouts, " arg %d = %016llx", 1.675 + (int) i, *((uint64_t *) &arg)); 1.676 + if (callerPC) { 1.677 + size_t argOffset = builder.framePushed() + IonJSFrameLayout::offsetOfActualArg(i); 1.678 + *builder.valuePointerAtStackOffset(argOffset) = arg; 1.679 + } else { 1.680 + startFrameFormals[i] = arg; 1.681 + } 1.682 + } 1.683 + } 1.684 + 1.685 + for (uint32_t i = 0; i < script->nfixed(); i++) { 1.686 + Value slot = iter.read(); 1.687 + if (!builder.writeValue(slot, "FixedValue")) 1.688 + return false; 1.689 + } 1.690 + 1.691 + // Get the pc. If we are handling an exception, resume at the pc of the 1.692 + // catch or finally block. 1.693 + jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset()); 1.694 + bool resumeAfter = catchingException ? false : iter.resumeAfter(); 1.695 + 1.696 + JSOp op = JSOp(*pc); 1.697 + 1.698 + // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side. 1.699 + // On the caller side this must represent like the function wasn't inlined. 1.700 + uint32_t pushedSlots = 0; 1.701 + AutoValueVector savedCallerArgs(cx); 1.702 + bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc); 1.703 + if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs)) 1.704 + { 1.705 + uint32_t inlined_args = 0; 1.706 + if (op == JSOP_FUNCALL) 1.707 + inlined_args = 2 + GET_ARGC(pc) - 1; 1.708 + else if (op == JSOP_FUNAPPLY) 1.709 + inlined_args = 2 + blFrame->numActualArgs(); 1.710 + else 1.711 + inlined_args = 2 + IsSetPropPC(pc); 1.712 + 1.713 + JS_ASSERT(exprStackSlots >= inlined_args); 1.714 + pushedSlots = exprStackSlots - inlined_args; 1.715 + 1.716 + IonSpew(IonSpew_BaselineBailouts, 1.717 + " pushing %u expression stack slots before fixup", 1.718 + pushedSlots); 1.719 + for (uint32_t i = 0; i < pushedSlots; i++) { 1.720 + Value v = iter.read(); 1.721 + if (!builder.writeValue(v, "StackValue")) 1.722 + return false; 1.723 + } 1.724 + 1.725 + if (op == JSOP_FUNCALL) { 1.726 + // When funcall got inlined and the native js_fun_call was bypassed, 1.727 + // the stack state is incorrect. To restore correctly it must look like 1.728 + // js_fun_call was actually called. This means transforming the stack 1.729 + // from |target, this, args| to |js_fun_call, target, this, args| 1.730 + // The js_fun_call is never read, so just pushing undefined now. 1.731 + IonSpew(IonSpew_BaselineBailouts, " pushing undefined to fixup funcall"); 1.732 + if (!builder.writeValue(UndefinedValue(), "StackValue")) 1.733 + return false; 1.734 + } 1.735 + 1.736 + if (needToSaveArgs) { 1.737 + // When an accessor is inlined, the whole thing is a lie. There 1.738 + // should never have been a call there. Fix the caller's stack to 1.739 + // forget it ever happened. 1.740 + 1.741 + // When funapply gets inlined we take all arguments out of the 1.742 + // arguments array. So the stack state is incorrect. To restore 1.743 + // correctly it must look like js_fun_apply was actually called. 1.744 + // This means transforming the stack from |target, this, arg1, ...| 1.745 + // to |js_fun_apply, target, this, argObject|. 1.746 + // Since the information is never read, we can just push undefined 1.747 + // for all values. 1.748 + if (op == JSOP_FUNAPPLY) { 1.749 + IonSpew(IonSpew_BaselineBailouts, " pushing 4x undefined to fixup funapply"); 1.750 + if (!builder.writeValue(UndefinedValue(), "StackValue")) 1.751 + return false; 1.752 + if (!builder.writeValue(UndefinedValue(), "StackValue")) 1.753 + return false; 1.754 + if (!builder.writeValue(UndefinedValue(), "StackValue")) 1.755 + return false; 1.756 + if (!builder.writeValue(UndefinedValue(), "StackValue")) 1.757 + return false; 1.758 + } 1.759 + // Save the actual arguments. They are needed on the callee side 1.760 + // as the arguments. Else we can't recover them. 1.761 + if (!savedCallerArgs.resize(inlined_args)) 1.762 + return false; 1.763 + for (uint32_t i = 0; i < inlined_args; i++) 1.764 + savedCallerArgs[i] = iter.read(); 1.765 + 1.766 + if (IsSetPropPC(pc)) { 1.767 + // We would love to just save all the arguments and leave them 1.768 + // in the stub frame pushed below, but we will lose the inital 1.769 + // argument which the function was called with, which we must 1.770 + // return to the caller, even if the setter internally modifies 1.771 + // its arguments. Stash the initial argument on the stack, to be 1.772 + // later retrieved by the SetProp_Fallback stub. 1.773 + Value initialArg = savedCallerArgs[inlined_args - 1]; 1.774 + IonSpew(IonSpew_BaselineBailouts, " pushing setter's initial argument"); 1.775 + if (!builder.writeValue(initialArg, "StackValue")) 1.776 + return false; 1.777 + } 1.778 + pushedSlots = exprStackSlots; 1.779 + } 1.780 + } 1.781 + 1.782 + IonSpew(IonSpew_BaselineBailouts, " pushing %u expression stack slots", 1.783 + exprStackSlots - pushedSlots); 1.784 + for (uint32_t i = pushedSlots; i < exprStackSlots; i++) { 1.785 + Value v; 1.786 + 1.787 + if (!iter.moreFrames() && i == exprStackSlots - 1 && 1.788 + cx->runtime()->hasIonReturnOverride()) 1.789 + { 1.790 + // If coming from an invalidation bailout, and this is the topmost 1.791 + // value, and a value override has been specified, don't read from the 1.792 + // iterator. Otherwise, we risk using a garbage value. 1.793 + JS_ASSERT(invalidate); 1.794 + iter.skip(); 1.795 + IonSpew(IonSpew_BaselineBailouts, " [Return Override]"); 1.796 + v = cx->runtime()->takeIonReturnOverride(); 1.797 + } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) { 1.798 + // If we are in the middle of propagating an exception from Ion by 1.799 + // bailing to baseline due to debug mode, we might not have all 1.800 + // the stack if we are at the newest frame. 1.801 + // 1.802 + // For instance, if calling |f()| pushed an Ion frame which threw, 1.803 + // the snapshot expects the return value to be pushed, but it's 1.804 + // possible nothing was pushed before we threw. Iterators might 1.805 + // still be on the stack, so we can't just drop the stack. 1.806 + MOZ_ASSERT(cx->compartment()->debugMode()); 1.807 + if (iter.moreFrames()) 1.808 + v = iter.read(); 1.809 + else 1.810 + v = MagicValue(JS_OPTIMIZED_OUT); 1.811 + } else { 1.812 + v = iter.read(); 1.813 + } 1.814 + if (!builder.writeValue(v, "StackValue")) 1.815 + return false; 1.816 + } 1.817 + 1.818 + size_t endOfBaselineJSFrameStack = builder.framePushed(); 1.819 + 1.820 + // If we are resuming at a LOOPENTRY op, resume at the next op to avoid 1.821 + // a bailout -> enter Ion -> bailout loop with --ion-eager. See also 1.822 + // ThunkToInterpreter. 1.823 + // 1.824 + // The algorithm below is the "tortoise and the hare" algorithm. See bug 1.825 + // 994444 for more explanation. 1.826 + if (!resumeAfter) { 1.827 + jsbytecode *fasterPc = pc; 1.828 + while (true) { 1.829 + pc = GetNextNonLoopEntryPc(pc); 1.830 + fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc)); 1.831 + if (fasterPc == pc) 1.832 + break; 1.833 + } 1.834 + op = JSOp(*pc); 1.835 + } 1.836 + 1.837 + uint32_t pcOff = script->pcToOffset(pc); 1.838 + bool isCall = IsCallPC(pc); 1.839 + BaselineScript *baselineScript = script->baselineScript(); 1.840 + 1.841 +#ifdef DEBUG 1.842 + uint32_t expectedDepth; 1.843 + bool reachablePC; 1.844 + if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC)) 1.845 + return false; 1.846 + 1.847 + if (reachablePC) { 1.848 + if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) { 1.849 + if (op == JSOP_FUNCALL) { 1.850 + // For fun.call(this, ...); the reconstructStackDepth will 1.851 + // include the this. When inlining that is not included. 1.852 + // So the exprStackSlots will be one less. 1.853 + JS_ASSERT(expectedDepth - exprStackSlots <= 1); 1.854 + } else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) { 1.855 + // Accessors coming out of ion are inlined via a complete 1.856 + // lie perpetrated by the compiler internally. Ion just rearranges 1.857 + // the stack, and pretends that it looked like a call all along. 1.858 + // This means that the depth is actually one *more* than expected 1.859 + // by the interpreter, as there is now a JSFunction, |this| and [arg], 1.860 + // rather than the expected |this| and [arg] 1.861 + // Note that none of that was pushed, but it's still reflected 1.862 + // in exprStackSlots. 1.863 + JS_ASSERT(exprStackSlots - expectedDepth == 1); 1.864 + } else { 1.865 + // For fun.apply({}, arguments) the reconstructStackDepth will 1.866 + // have stackdepth 4, but it could be that we inlined the 1.867 + // funapply. In that case exprStackSlots, will have the real 1.868 + // arguments in the slots and not be 4. 1.869 + JS_ASSERT(exprStackSlots == expectedDepth); 1.870 + } 1.871 + } 1.872 + } 1.873 + 1.874 + IonSpew(IonSpew_BaselineBailouts, " Resuming %s pc offset %d (op %s) (line %d) of %s:%d", 1.875 + resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op], 1.876 + PCToLineNumber(script, pc), script->filename(), (int) script->lineno()); 1.877 + IonSpew(IonSpew_BaselineBailouts, " Bailout kind: %s", 1.878 + BailoutKindString(bailoutKind)); 1.879 +#endif 1.880 + 1.881 + // If this was the last inline frame, or we are bailing out to a catch or 1.882 + // finally block in this frame, then unpacking is almost done. 1.883 + if (!iter.moreFrames() || catchingException) { 1.884 + // Last frame, so PC for call to next frame is set to nullptr. 1.885 + *callPC = nullptr; 1.886 + 1.887 + // If the bailout was a resumeAfter, and the opcode is monitored, 1.888 + // then the bailed out state should be in a position to enter 1.889 + // into the ICTypeMonitor chain for the op. 1.890 + bool enterMonitorChain = false; 1.891 + if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) { 1.892 + // Not every monitored op has a monitored fallback stub, e.g. 1.893 + // JSOP_NEWOBJECT, which always returns the same type for a 1.894 + // particular script/pc location. 1.895 + ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff); 1.896 + ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback(); 1.897 + if (fallbackStub->isMonitoredFallback()) 1.898 + enterMonitorChain = true; 1.899 + } 1.900 + 1.901 + uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0; 1.902 + 1.903 + if (resumeAfter && !enterMonitorChain) 1.904 + pc = GetNextPc(pc); 1.905 + 1.906 + builder.setResumeFramePtr(prevFramePtr); 1.907 + 1.908 + if (enterMonitorChain) { 1.909 + ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff); 1.910 + ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback(); 1.911 + JS_ASSERT(fallbackStub->isMonitoredFallback()); 1.912 + IonSpew(IonSpew_BaselineBailouts, " [TYPE-MONITOR CHAIN]"); 1.913 + ICMonitoredFallbackStub *monFallbackStub = fallbackStub->toMonitoredFallbackStub(); 1.914 + ICStub *firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub(); 1.915 + 1.916 + // To enter a monitoring chain, we load the top stack value into R0 1.917 + IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into R0."); 1.918 + builder.popValueInto(PCMappingSlotInfo::SlotInR0); 1.919 + 1.920 + // Need to adjust the frameSize for the frame to match the values popped 1.921 + // into registers. 1.922 + frameSize -= sizeof(Value); 1.923 + blFrame->setFrameSize(frameSize); 1.924 + IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d", 1.925 + (int) sizeof(Value), (int) frameSize); 1.926 + 1.927 + // If resuming into a JSOP_CALL, baseline keeps the arguments on the 1.928 + // stack and pops them only after returning from the call IC. 1.929 + // Push undefs onto the stack in anticipation of the popping of the 1.930 + // callee, thisv, and actual arguments passed from the caller's frame. 1.931 + if (isCall) { 1.932 + builder.writeValue(UndefinedValue(), "CallOp FillerCallee"); 1.933 + builder.writeValue(UndefinedValue(), "CallOp FillerThis"); 1.934 + for (uint32_t i = 0; i < numCallArgs; i++) 1.935 + builder.writeValue(UndefinedValue(), "CallOp FillerArg"); 1.936 + 1.937 + frameSize += (numCallArgs + 2) * sizeof(Value); 1.938 + blFrame->setFrameSize(frameSize); 1.939 + IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize += %d: %d", 1.940 + (int) ((numCallArgs + 2) * sizeof(Value)), (int) frameSize); 1.941 + } 1.942 + 1.943 + // Set the resume address to the return point from the IC, and set 1.944 + // the monitor stub addr. 1.945 + builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry)); 1.946 + builder.setMonitorStub(firstMonStub); 1.947 + IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p monitorStub=%p", 1.948 + baselineScript->returnAddressForIC(icEntry), firstMonStub); 1.949 + 1.950 + } else { 1.951 + // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the 1.952 + // top stack values. 1.953 + PCMappingSlotInfo slotInfo; 1.954 + uint8_t *nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo); 1.955 + unsigned numUnsynced = slotInfo.numUnsynced(); 1.956 + JS_ASSERT(numUnsynced <= 2); 1.957 + PCMappingSlotInfo::SlotLocation loc1, loc2; 1.958 + if (numUnsynced > 0) { 1.959 + loc1 = slotInfo.topSlotLocation(); 1.960 + IonSpew(IonSpew_BaselineBailouts, " Popping top stack value into %d.", 1.961 + (int) loc1); 1.962 + builder.popValueInto(loc1); 1.963 + } 1.964 + if (numUnsynced > 1) { 1.965 + loc2 = slotInfo.nextSlotLocation(); 1.966 + IonSpew(IonSpew_BaselineBailouts, " Popping next stack value into %d.", 1.967 + (int) loc2); 1.968 + JS_ASSERT_IF(loc1 != PCMappingSlotInfo::SlotIgnore, loc1 != loc2); 1.969 + builder.popValueInto(loc2); 1.970 + } 1.971 + 1.972 + // Need to adjust the frameSize for the frame to match the values popped 1.973 + // into registers. 1.974 + frameSize -= sizeof(Value) * numUnsynced; 1.975 + blFrame->setFrameSize(frameSize); 1.976 + IonSpew(IonSpew_BaselineBailouts, " Adjusted framesize -= %d: %d", 1.977 + int(sizeof(Value) * numUnsynced), int(frameSize)); 1.978 + 1.979 + // If scopeChain is nullptr, then bailout is occurring during argument check. 1.980 + // In this case, resume into the prologue. 1.981 + uint8_t *opReturnAddr; 1.982 + if (scopeChain == nullptr) { 1.983 + // Global and eval scripts expect the scope chain in R1, so only 1.984 + // resume into the prologue for function scripts. 1.985 + JS_ASSERT(fun); 1.986 + JS_ASSERT(numUnsynced == 0); 1.987 + opReturnAddr = baselineScript->prologueEntryAddr(); 1.988 + IonSpew(IonSpew_BaselineBailouts, " Resuming into prologue."); 1.989 + 1.990 + // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame. 1.991 + blFrame->unsetPushedSPSFrame(); 1.992 + 1.993 + if (cx->runtime()->spsProfiler.enabled()) { 1.994 + if (js_JitOptions.profileInlineFrames) { 1.995 + // If SPS is enabled, there are two corner cases to handle: 1.996 + // 1. If resuming into the prologue, and innermost frame is an inlined 1.997 + // frame, and bailout is because of argument check failure, then: 1.998 + // Top SPS profiler entry would be for caller frame. 1.999 + // Ion would not have set the PC index field on that frame 1.1000 + // (since this bailout happens before MFunctionBoundary). 1.1001 + // Make sure that's done now. 1.1002 + // 2. If resuming into the prologue, and the bailout is NOT because of an 1.1003 + // argument check, then: 1.1004 + // Top SPS profiler entry would be for callee frame. 1.1005 + // Ion would already have pushed an SPS entry for this frame. 1.1006 + // The pc for this entry would be set to nullptr. 1.1007 + // Make sure it's set to script->pc. 1.1008 + if (caller && bailoutKind == Bailout_ArgumentCheck) { 1.1009 + IonSpew(IonSpew_BaselineBailouts, " Setting PCidx on innermost " 1.1010 + "inlined frame's parent's SPS entry (%s:%d) (pcIdx=%d)!", 1.1011 + caller->filename(), caller->lineno(), 1.1012 + caller->pcToOffset(callerPC)); 1.1013 + cx->runtime()->spsProfiler.updatePC(caller, callerPC); 1.1014 + 1.1015 + } else if (bailoutKind != Bailout_ArgumentCheck) { 1.1016 + IonSpew(IonSpew_BaselineBailouts, 1.1017 + " Popping SPS entry for innermost inlined frame"); 1.1018 + cx->runtime()->spsProfiler.exit(script, fun); 1.1019 + } 1.1020 + 1.1021 + } else { 1.1022 + // If not profiling inline frames, then this is logically simpler. 1.1023 + // 1.1024 + // 1. If resuming into inline code, then the top SPS entry will be 1.1025 + // for the outermost caller, and will have an uninitialized PC. 1.1026 + // This will be fixed up later in BailoutIonToBaseline. 1.1027 + // 1.1028 + // 2. If resuming into top-level code prologue, with ArgumentCheck, 1.1029 + // no SPS entry will have been pushed. Can be left alone. 1.1030 + // 1.1031 + // 3. If resuming into top-level code prologue, without ArgumentCheck, 1.1032 + // an SPS entry will have been pushed, and needs to be popped. 1.1033 + // 1.1034 + // 4. If resuming into top-level code main body, an SPS entry will 1.1035 + // have been pushed, and can be left alone. 1.1036 + // 1.1037 + // Only need to handle case 3 here. 1.1038 + if (!caller && bailoutKind != Bailout_ArgumentCheck) { 1.1039 + IonSpew(IonSpew_BaselineBailouts, 1.1040 + " Popping SPS entry for outermost frame"); 1.1041 + cx->runtime()->spsProfiler.exit(script, fun); 1.1042 + } 1.1043 + } 1.1044 + } 1.1045 + } else { 1.1046 + opReturnAddr = nativeCodeForPC; 1.1047 + } 1.1048 + builder.setResumeAddr(opReturnAddr); 1.1049 + IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr); 1.1050 + } 1.1051 + 1.1052 + if (cx->runtime()->spsProfiler.enabled()) { 1.1053 + if (blFrame->hasPushedSPSFrame()) { 1.1054 + // Set PC index to 0 for the innermost frame to match what the 1.1055 + // interpreter and Baseline do: they update the SPS pc for 1.1056 + // JSOP_CALL ops but set it to 0 when running other ops. Ion code 1.1057 + // can set the pc to NullPCIndex and this will confuse SPS when 1.1058 + // Baseline calls into the VM at non-CALL ops and re-enters JS. 1.1059 + IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0"); 1.1060 + cx->runtime()->spsProfiler.updatePC(script, script->code()); 1.1061 + } 1.1062 + 1.1063 + // Register bailout with profiler. 1.1064 + const char *filename = script->filename(); 1.1065 + if (filename == nullptr) 1.1066 + filename = "<unknown>"; 1.1067 + unsigned len = strlen(filename) + 200; 1.1068 + char *buf = js_pod_malloc<char>(len); 1.1069 + if (buf == nullptr) 1.1070 + return false; 1.1071 + JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d", 1.1072 + BailoutKindString(bailoutKind), 1.1073 + resumeAfter ? "after" : "at", 1.1074 + js_CodeName[op], 1.1075 + int(PCToLineNumber(script, pc)), 1.1076 + filename, 1.1077 + int(script->lineno())); 1.1078 + cx->runtime()->spsProfiler.markEvent(buf); 1.1079 + js_free(buf); 1.1080 + } 1.1081 + 1.1082 + return true; 1.1083 + } 1.1084 + 1.1085 + *callPC = pc; 1.1086 + 1.1087 + // Write out descriptor of BaselineJS frame. 1.1088 + size_t baselineFrameDescr = MakeFrameDescriptor((uint32_t) builder.framePushed(), 1.1089 + JitFrame_BaselineJS); 1.1090 + if (!builder.writeWord(baselineFrameDescr, "Descriptor")) 1.1091 + return false; 1.1092 + 1.1093 + // Calculate and write out return address. 1.1094 + // The icEntry in question MUST have an inlinable fallback stub. 1.1095 + ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff); 1.1096 + JS_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback())); 1.1097 + if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr")) 1.1098 + return false; 1.1099 + 1.1100 + // Build baseline stub frame: 1.1101 + // +===============+ 1.1102 + // | StubPtr | 1.1103 + // +---------------+ 1.1104 + // | FramePtr | 1.1105 + // +---------------+ 1.1106 + // | ArgA | 1.1107 + // +---------------+ 1.1108 + // | ... | 1.1109 + // +---------------+ 1.1110 + // | Arg0 | 1.1111 + // +---------------+ 1.1112 + // | ThisV | 1.1113 + // +---------------+ 1.1114 + // | ActualArgC | 1.1115 + // +---------------+ 1.1116 + // | CalleeToken | 1.1117 + // +---------------+ 1.1118 + // | Descr(BLStub) | 1.1119 + // +---------------+ 1.1120 + // | ReturnAddr | 1.1121 + // +===============+ 1.1122 + 1.1123 + IonSpew(IonSpew_BaselineBailouts, " [BASELINE-STUB FRAME]"); 1.1124 + 1.1125 + size_t startOfBaselineStubFrame = builder.framePushed(); 1.1126 + 1.1127 + // Write stub pointer. 1.1128 + JS_ASSERT(IsInlinableFallback(icEntry.fallbackStub())); 1.1129 + if (!builder.writePtr(icEntry.fallbackStub(), "StubPtr")) 1.1130 + return false; 1.1131 + 1.1132 + // Write previous frame pointer (saved earlier). 1.1133 + if (!builder.writePtr(prevFramePtr, "PrevFramePtr")) 1.1134 + return false; 1.1135 + prevFramePtr = builder.virtualPointerAtStackOffset(0); 1.1136 + 1.1137 + // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame. 1.1138 + // Arguments are reversed on the BaselineJS frame's stack values. 1.1139 + JS_ASSERT(IsIonInlinablePC(pc)); 1.1140 + unsigned actualArgc; 1.1141 + if (needToSaveArgs) { 1.1142 + // For FUNAPPLY or an accessor, the arguments are not on the stack anymore, 1.1143 + // but they are copied in a vector and are written here. 1.1144 + if (op == JSOP_FUNAPPLY) 1.1145 + actualArgc = blFrame->numActualArgs(); 1.1146 + else 1.1147 + actualArgc = IsSetPropPC(pc); 1.1148 + 1.1149 + JS_ASSERT(actualArgc + 2 <= exprStackSlots); 1.1150 + JS_ASSERT(savedCallerArgs.length() == actualArgc + 2); 1.1151 + for (unsigned i = 0; i < actualArgc + 1; i++) { 1.1152 + size_t arg = savedCallerArgs.length() - (i + 1); 1.1153 + if (!builder.writeValue(savedCallerArgs[arg], "ArgVal")) 1.1154 + return false; 1.1155 + } 1.1156 + } else { 1.1157 + actualArgc = GET_ARGC(pc); 1.1158 + if (op == JSOP_FUNCALL) { 1.1159 + JS_ASSERT(actualArgc > 0); 1.1160 + actualArgc--; 1.1161 + } 1.1162 + 1.1163 + JS_ASSERT(actualArgc + 2 <= exprStackSlots); 1.1164 + for (unsigned i = 0; i < actualArgc + 1; i++) { 1.1165 + size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1); 1.1166 + if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal")) 1.1167 + return false; 1.1168 + } 1.1169 + } 1.1170 + 1.1171 + // In case these arguments need to be copied on the stack again for a rectifier frame, 1.1172 + // save the framePushed values here for later use. 1.1173 + size_t endOfBaselineStubArgs = builder.framePushed(); 1.1174 + 1.1175 + // Calculate frame size for descriptor. 1.1176 + size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame; 1.1177 + size_t baselineStubFrameDescr = MakeFrameDescriptor((uint32_t) baselineStubFrameSize, 1.1178 + JitFrame_BaselineStub); 1.1179 + 1.1180 + // Push actual argc 1.1181 + if (!builder.writeWord(actualArgc, "ActualArgc")) 1.1182 + return false; 1.1183 + 1.1184 + // Push callee token (must be a JS Function) 1.1185 + Value callee; 1.1186 + if (needToSaveArgs) { 1.1187 + // The arguments of FUNAPPLY or inlined accessors are not writen to the stack. 1.1188 + // So get the callee from the specially saved vector. 1.1189 + callee = savedCallerArgs[0]; 1.1190 + } else { 1.1191 + uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2); 1.1192 + size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack) 1.1193 + + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value)); 1.1194 + callee = *builder.valuePointerAtStackOffset(calleeOffset); 1.1195 + IonSpew(IonSpew_BaselineBailouts, " CalleeStackSlot=%d", (int) calleeStackSlot); 1.1196 + } 1.1197 + IonSpew(IonSpew_BaselineBailouts, " Callee = %016llx", *((uint64_t *) &callee)); 1.1198 + JS_ASSERT(callee.isObject() && callee.toObject().is<JSFunction>()); 1.1199 + JSFunction *calleeFun = &callee.toObject().as<JSFunction>(); 1.1200 + if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken")) 1.1201 + return false; 1.1202 + nextCallee.set(calleeFun); 1.1203 + 1.1204 + // Push BaselineStub frame descriptor 1.1205 + if (!builder.writeWord(baselineStubFrameDescr, "Descriptor")) 1.1206 + return false; 1.1207 + 1.1208 + // Push return address into ICCall_Scripted stub, immediately after the call. 1.1209 + void *baselineCallReturnAddr = GetStubReturnAddress(cx, pc); 1.1210 + JS_ASSERT(baselineCallReturnAddr); 1.1211 + if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr")) 1.1212 + return false; 1.1213 + 1.1214 + // If actualArgc >= fun->nargs, then we are done. Otherwise, we need to push on 1.1215 + // a reconstructed rectifier frame. 1.1216 + if (actualArgc >= calleeFun->nargs()) 1.1217 + return true; 1.1218 + 1.1219 + // Push a reconstructed rectifier frame. 1.1220 + // +===============+ 1.1221 + // | UndefinedU | 1.1222 + // +---------------+ 1.1223 + // | ... | 1.1224 + // +---------------+ 1.1225 + // | Undefined0 | 1.1226 + // +---------------+ 1.1227 + // | ArgA | 1.1228 + // +---------------+ 1.1229 + // | ... | 1.1230 + // +---------------+ 1.1231 + // | Arg0 | 1.1232 + // +---------------+ 1.1233 + // | ThisV | 1.1234 + // +---------------+ 1.1235 + // | ActualArgC | 1.1236 + // +---------------+ 1.1237 + // | CalleeToken | 1.1238 + // +---------------+ 1.1239 + // | Descr(Rect) | 1.1240 + // +---------------+ 1.1241 + // | ReturnAddr | 1.1242 + // +===============+ 1.1243 + 1.1244 + IonSpew(IonSpew_BaselineBailouts, " [RECTIFIER FRAME]"); 1.1245 + 1.1246 + size_t startOfRectifierFrame = builder.framePushed(); 1.1247 + 1.1248 + // On x86-only, the frame pointer is saved again in the rectifier frame. 1.1249 +#if defined(JS_CODEGEN_X86) 1.1250 + if (!builder.writePtr(prevFramePtr, "PrevFramePtr-X86Only")) 1.1251 + return false; 1.1252 +#endif 1.1253 + 1.1254 + // Push undefined for missing arguments. 1.1255 + for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) { 1.1256 + if (!builder.writeValue(UndefinedValue(), "FillerVal")) 1.1257 + return false; 1.1258 + } 1.1259 + 1.1260 + // Copy arguments + thisv from BaselineStub frame. 1.1261 + if (!builder.subtract((actualArgc + 1) * sizeof(Value), "CopiedArgs")) 1.1262 + return false; 1.1263 + BufferPointer<uint8_t> stubArgsEnd = 1.1264 + builder.pointerAtStackOffset<uint8_t>(builder.framePushed() - endOfBaselineStubArgs); 1.1265 + IonSpew(IonSpew_BaselineBailouts, " MemCpy from %p", stubArgsEnd.get()); 1.1266 + memcpy(builder.pointerAtStackOffset<uint8_t>(0).get(), stubArgsEnd.get(), 1.1267 + (actualArgc + 1) * sizeof(Value)); 1.1268 + 1.1269 + // Calculate frame size for descriptor. 1.1270 + size_t rectifierFrameSize = builder.framePushed() - startOfRectifierFrame; 1.1271 + size_t rectifierFrameDescr = MakeFrameDescriptor((uint32_t) rectifierFrameSize, 1.1272 + JitFrame_Rectifier); 1.1273 + 1.1274 + // Push actualArgc 1.1275 + if (!builder.writeWord(actualArgc, "ActualArgc")) 1.1276 + return false; 1.1277 + 1.1278 + // Push calleeToken again. 1.1279 + if (!builder.writePtr(CalleeToToken(calleeFun), "CalleeToken")) 1.1280 + return false; 1.1281 + 1.1282 + // Push rectifier frame descriptor 1.1283 + if (!builder.writeWord(rectifierFrameDescr, "Descriptor")) 1.1284 + return false; 1.1285 + 1.1286 + // Push return address into the ArgumentsRectifier code, immediately after the ioncode 1.1287 + // call. 1.1288 + void *rectReturnAddr = cx->runtime()->jitRuntime()->getArgumentsRectifierReturnAddr(); 1.1289 + JS_ASSERT(rectReturnAddr); 1.1290 + if (!builder.writePtr(rectReturnAddr, "ReturnAddr")) 1.1291 + return false; 1.1292 + 1.1293 + return true; 1.1294 +} 1.1295 + 1.1296 +uint32_t 1.1297 +jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter, 1.1298 + bool invalidate, BaselineBailoutInfo **bailoutInfo, 1.1299 + const ExceptionBailoutInfo *excInfo) 1.1300 +{ 1.1301 + JS_ASSERT(bailoutInfo != nullptr); 1.1302 + JS_ASSERT(*bailoutInfo == nullptr); 1.1303 + 1.1304 + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); 1.1305 + TraceLogStopEvent(logger, TraceLogger::IonMonkey); 1.1306 + TraceLogStartEvent(logger, TraceLogger::Baseline); 1.1307 + 1.1308 + // The caller of the top frame must be one of the following: 1.1309 + // IonJS - Ion calling into Ion. 1.1310 + // BaselineStub - Baseline calling into Ion. 1.1311 + // Entry - Interpreter or other calling into Ion. 1.1312 + // Rectifier - Arguments rectifier calling into Ion. 1.1313 + JS_ASSERT(iter.isIonJS()); 1.1314 + FrameType prevFrameType = iter.prevType(); 1.1315 + JS_ASSERT(prevFrameType == JitFrame_IonJS || 1.1316 + prevFrameType == JitFrame_BaselineStub || 1.1317 + prevFrameType == JitFrame_Entry || 1.1318 + prevFrameType == JitFrame_Rectifier); 1.1319 + 1.1320 + // All incoming frames are going to look like this: 1.1321 + // 1.1322 + // +---------------+ 1.1323 + // | ... | 1.1324 + // +---------------+ 1.1325 + // | Args | 1.1326 + // | ... | 1.1327 + // +---------------+ 1.1328 + // | ThisV | 1.1329 + // +---------------+ 1.1330 + // | ActualArgC | 1.1331 + // +---------------+ 1.1332 + // | CalleeToken | 1.1333 + // +---------------+ 1.1334 + // | Descriptor | 1.1335 + // +---------------+ 1.1336 + // | ReturnAddr | 1.1337 + // +---------------+ 1.1338 + // | ||||| | <---- Overwrite starting here. 1.1339 + // | ||||| | 1.1340 + // | ||||| | 1.1341 + // +---------------+ 1.1342 + 1.1343 + IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)", 1.1344 + iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(), 1.1345 + (int) prevFrameType); 1.1346 + 1.1347 + bool catchingException; 1.1348 + bool propagatingExceptionForDebugMode; 1.1349 + if (excInfo) { 1.1350 + catchingException = excInfo->catchingException(); 1.1351 + propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode(); 1.1352 + 1.1353 + if (catchingException) 1.1354 + IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block"); 1.1355 + 1.1356 + if (propagatingExceptionForDebugMode) 1.1357 + IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode"); 1.1358 + } else { 1.1359 + catchingException = false; 1.1360 + propagatingExceptionForDebugMode = false; 1.1361 + } 1.1362 + 1.1363 + IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u", 1.1364 + iter.snapshotOffset(), iter.ionScript()->snapshotsListSize()); 1.1365 + 1.1366 + if (!excInfo) 1.1367 + iter.ionScript()->incNumBailouts(); 1.1368 + iter.script()->updateBaselineOrIonRaw(); 1.1369 + 1.1370 + // Allocate buffer to hold stack replacement data. 1.1371 + BaselineStackBuilder builder(iter, 1024); 1.1372 + if (!builder.init()) 1.1373 + return BAILOUT_RETURN_FATAL_ERROR; 1.1374 + IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame()); 1.1375 + 1.1376 + SnapshotIterator snapIter(iter); 1.1377 + 1.1378 + RootedFunction callee(cx, iter.maybeCallee()); 1.1379 + RootedScript scr(cx, iter.script()); 1.1380 + if (callee) { 1.1381 + IonSpew(IonSpew_BaselineBailouts, " Callee function (%s:%u)", 1.1382 + scr->filename(), scr->lineno()); 1.1383 + } else { 1.1384 + IonSpew(IonSpew_BaselineBailouts, " No callee!"); 1.1385 + } 1.1386 + 1.1387 + if (iter.isConstructing()) 1.1388 + IonSpew(IonSpew_BaselineBailouts, " Constructing!"); 1.1389 + else 1.1390 + IonSpew(IonSpew_BaselineBailouts, " Not constructing!"); 1.1391 + 1.1392 + IonSpew(IonSpew_BaselineBailouts, " Restoring frames:"); 1.1393 + size_t frameNo = 0; 1.1394 + 1.1395 + // Reconstruct baseline frames using the builder. 1.1396 + RootedScript caller(cx); 1.1397 + jsbytecode *callerPC = nullptr; 1.1398 + RootedFunction fun(cx, callee); 1.1399 + AutoValueVector startFrameFormals(cx); 1.1400 + 1.1401 + RootedScript topCaller(cx); 1.1402 + jsbytecode *topCallerPC = nullptr; 1.1403 + 1.1404 + while (true) { 1.1405 + MOZ_ASSERT(snapIter.instruction()->isResumePoint()); 1.1406 + 1.1407 + if (frameNo > 0) { 1.1408 + TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr)); 1.1409 + TraceLogStartEvent(logger, TraceLogger::Baseline); 1.1410 + } 1.1411 + 1.1412 + IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo); 1.1413 + 1.1414 + // If we are bailing out to a catch or finally block in this frame, 1.1415 + // pass excInfo to InitFromBailout and don't unpack any other frames. 1.1416 + bool handleException = (catchingException && excInfo->frameNo() == frameNo); 1.1417 + 1.1418 + // We also need to pass excInfo if we're bailing out in place for 1.1419 + // debug mode. 1.1420 + bool passExcInfo = handleException || propagatingExceptionForDebugMode; 1.1421 + 1.1422 + jsbytecode *callPC = nullptr; 1.1423 + RootedFunction nextCallee(cx, nullptr); 1.1424 + if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(), 1.1425 + snapIter, invalidate, builder, startFrameFormals, 1.1426 + &nextCallee, &callPC, passExcInfo ? excInfo : nullptr)) 1.1427 + { 1.1428 + return BAILOUT_RETURN_FATAL_ERROR; 1.1429 + } 1.1430 + 1.1431 + if (!snapIter.moreFrames()) { 1.1432 + JS_ASSERT(!callPC); 1.1433 + break; 1.1434 + } 1.1435 + 1.1436 + if (handleException) 1.1437 + break; 1.1438 + 1.1439 + JS_ASSERT(nextCallee); 1.1440 + JS_ASSERT(callPC); 1.1441 + caller = scr; 1.1442 + callerPC = callPC; 1.1443 + fun = nextCallee; 1.1444 + scr = fun->existingScriptForInlinedFunction(); 1.1445 + 1.1446 + // Save top caller info for adjusting SPS frames later. 1.1447 + if (!topCaller) { 1.1448 + JS_ASSERT(frameNo == 0); 1.1449 + topCaller = caller; 1.1450 + topCallerPC = callerPC; 1.1451 + } 1.1452 + 1.1453 + frameNo++; 1.1454 + 1.1455 + snapIter.nextInstruction(); 1.1456 + } 1.1457 + IonSpew(IonSpew_BaselineBailouts, " Done restoring frames"); 1.1458 + 1.1459 + // If there were multiple inline frames unpacked, and inline frame profiling 1.1460 + // is off, then the current top SPS frame is for the outermost caller, and 1.1461 + // has an uninitialized PC. Initialize it now. 1.1462 + if (frameNo > 0 && !js_JitOptions.profileInlineFrames) 1.1463 + cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC); 1.1464 + 1.1465 + BailoutKind bailoutKind = snapIter.bailoutKind(); 1.1466 + 1.1467 + if (!startFrameFormals.empty()) { 1.1468 + // Set the first frame's formals, see the comment in InitFromBailout. 1.1469 + Value *argv = builder.startFrame()->argv() + 1; // +1 to skip |this|. 1.1470 + mozilla::PodCopy(argv, startFrameFormals.begin(), startFrameFormals.length()); 1.1471 + } 1.1472 + 1.1473 + // Do stack check. 1.1474 + bool overRecursed = false; 1.1475 + BaselineBailoutInfo *info = builder.info(); 1.1476 + uint8_t *newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom); 1.1477 +#ifdef JS_ARM_SIMULATOR 1.1478 + if (Simulator::Current()->overRecursed(uintptr_t(newsp))) 1.1479 + overRecursed = true; 1.1480 +#else 1.1481 + JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true); 1.1482 +#endif 1.1483 + if (overRecursed) { 1.1484 + IonSpew(IonSpew_BaselineBailouts, " Overrecursion check failed!"); 1.1485 + return BAILOUT_RETURN_OVERRECURSED; 1.1486 + } 1.1487 + 1.1488 + // Take the reconstructed baseline stack so it doesn't get freed when builder destructs. 1.1489 + info = builder.takeBuffer(); 1.1490 + info->numFrames = frameNo + 1; 1.1491 + info->bailoutKind = bailoutKind; 1.1492 + *bailoutInfo = info; 1.1493 + return BAILOUT_RETURN_OK; 1.1494 +} 1.1495 + 1.1496 +static bool 1.1497 +HandleBoundsCheckFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript) 1.1498 +{ 1.1499 + IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d, inlined into %s:%d", 1.1500 + innerScript->filename(), innerScript->lineno(), 1.1501 + outerScript->filename(), outerScript->lineno()); 1.1502 + 1.1503 + JS_ASSERT(!outerScript->ionScript()->invalidated()); 1.1504 + 1.1505 + // TODO: Currently this mimic's Ion's handling of this case. Investigate setting 1.1506 + // the flag on innerScript as opposed to outerScript, and maybe invalidating both 1.1507 + // inner and outer scripts, instead of just the outer one. 1.1508 + if (!outerScript->failedBoundsCheck()) 1.1509 + outerScript->setFailedBoundsCheck(); 1.1510 + IonSpew(IonSpew_BaselineBailouts, "Invalidating due to bounds check failure"); 1.1511 + return Invalidate(cx, outerScript); 1.1512 +} 1.1513 + 1.1514 +static bool 1.1515 +HandleShapeGuardFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript) 1.1516 +{ 1.1517 + IonSpew(IonSpew_Bailouts, "Shape guard failure %s:%d, inlined into %s:%d", 1.1518 + innerScript->filename(), innerScript->lineno(), 1.1519 + outerScript->filename(), outerScript->lineno()); 1.1520 + 1.1521 + JS_ASSERT(!outerScript->ionScript()->invalidated()); 1.1522 + 1.1523 + // TODO: Currently this mimic's Ion's handling of this case. Investigate setting 1.1524 + // the flag on innerScript as opposed to outerScript, and maybe invalidating both 1.1525 + // inner and outer scripts, instead of just the outer one. 1.1526 + outerScript->setFailedShapeGuard(); 1.1527 + IonSpew(IonSpew_BaselineBailouts, "Invalidating due to shape guard failure"); 1.1528 + return Invalidate(cx, outerScript); 1.1529 +} 1.1530 + 1.1531 +static bool 1.1532 +HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerScript) 1.1533 +{ 1.1534 + IonSpew(IonSpew_Bailouts, "Baseline info failure %s:%d, inlined into %s:%d", 1.1535 + innerScript->filename(), innerScript->lineno(), 1.1536 + outerScript->filename(), outerScript->lineno()); 1.1537 + 1.1538 + JS_ASSERT(!outerScript->ionScript()->invalidated()); 1.1539 + 1.1540 + IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info"); 1.1541 + return Invalidate(cx, outerScript); 1.1542 +} 1.1543 + 1.1544 +static bool 1.1545 +CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth, 1.1546 + BaselineFrame *frame) 1.1547 +{ 1.1548 + RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth); 1.1549 + 1.1550 + // We might not have rematerialized a frame if the user never requested a 1.1551 + // Debugger.Frame for it. 1.1552 + if (!rematFrame) 1.1553 + return true; 1.1554 + 1.1555 + MOZ_ASSERT(rematFrame->script() == frame->script()); 1.1556 + MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs()); 1.1557 + 1.1558 + frame->setScopeChain(rematFrame->scopeChain()); 1.1559 + frame->thisValue() = rematFrame->thisValue(); 1.1560 + 1.1561 + for (unsigned i = 0; i < frame->numActualArgs(); i++) 1.1562 + frame->argv()[i] = rematFrame->argv()[i]; 1.1563 + 1.1564 + for (size_t i = 0; i < frame->script()->nfixed(); i++) 1.1565 + *frame->valueSlot(i) = rematFrame->locals()[i]; 1.1566 + 1.1567 + IonSpew(IonSpew_BaselineBailouts, 1.1568 + " Copied from rematerialized frame at (%p,%u)", 1.1569 + fp, inlineDepth); 1.1570 + 1.1571 + if (cx->compartment()->debugMode()) 1.1572 + return Debugger::handleIonBailout(cx, rematFrame, frame); 1.1573 + 1.1574 + return true; 1.1575 +} 1.1576 + 1.1577 +uint32_t 1.1578 +jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo) 1.1579 +{ 1.1580 + // The caller pushes R0 and R1 on the stack without rooting them. 1.1581 + // Since GC here is very unlikely just suppress it. 1.1582 + JSContext *cx = GetJSContextFromJitCode(); 1.1583 + js::gc::AutoSuppressGC suppressGC(cx); 1.1584 + 1.1585 + IonSpew(IonSpew_BaselineBailouts, " Done restoring frames"); 1.1586 + 1.1587 + // Check that we can get the current script's PC. 1.1588 +#ifdef DEBUG 1.1589 + jsbytecode *pc; 1.1590 + cx->currentScript(&pc); 1.1591 + IonSpew(IonSpew_BaselineBailouts, " Got pc=%p", pc); 1.1592 +#endif 1.1593 + 1.1594 + uint32_t numFrames = bailoutInfo->numFrames; 1.1595 + JS_ASSERT(numFrames > 0); 1.1596 + BailoutKind bailoutKind = bailoutInfo->bailoutKind; 1.1597 + 1.1598 + // Free the bailout buffer. 1.1599 + js_free(bailoutInfo); 1.1600 + bailoutInfo = nullptr; 1.1601 + 1.1602 + // Ensure the frame has a call object if it needs one. If the scope chain 1.1603 + // is nullptr, we will enter baseline code at the prologue so no need to do 1.1604 + // anything in that case. 1.1605 + BaselineFrame *topFrame = GetTopBaselineFrame(cx); 1.1606 + if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame)) 1.1607 + return false; 1.1608 + 1.1609 + // Create arguments objects for bailed out frames, to maintain the invariant 1.1610 + // that script->needsArgsObj() implies frame->hasArgsObj(). 1.1611 + RootedScript innerScript(cx, nullptr); 1.1612 + RootedScript outerScript(cx, nullptr); 1.1613 + 1.1614 + JS_ASSERT(cx->currentlyRunningInJit()); 1.1615 + JitFrameIterator iter(cx); 1.1616 + uint8_t *outerFp = nullptr; 1.1617 + 1.1618 + uint32_t frameno = 0; 1.1619 + while (frameno < numFrames) { 1.1620 + JS_ASSERT(!iter.isIonJS()); 1.1621 + 1.1622 + if (iter.isBaselineJS()) { 1.1623 + BaselineFrame *frame = iter.baselineFrame(); 1.1624 + MOZ_ASSERT(frame->script()->hasBaselineScript()); 1.1625 + 1.1626 + // If the frame doesn't even have a scope chain set yet, then it's resuming 1.1627 + // into the the prologue before the scope chain is initialized. Any 1.1628 + // necessary args object will also be initialized there. 1.1629 + if (frame->scopeChain() && frame->script()->needsArgsObj()) { 1.1630 + ArgumentsObject *argsObj; 1.1631 + if (frame->hasArgsObj()) { 1.1632 + argsObj = &frame->argsObj(); 1.1633 + } else { 1.1634 + argsObj = ArgumentsObject::createExpected(cx, frame); 1.1635 + if (!argsObj) 1.1636 + return false; 1.1637 + } 1.1638 + 1.1639 + // The arguments is a local binding and needsArgsObj does not 1.1640 + // check if it is clobbered. Ensure that the local binding 1.1641 + // restored during bailout before storing the arguments object 1.1642 + // to the slot. 1.1643 + RootedScript script(cx, frame->script()); 1.1644 + SetFrameArgumentsObject(cx, frame, script, argsObj); 1.1645 + } 1.1646 + 1.1647 + if (frameno == 0) 1.1648 + innerScript = frame->script(); 1.1649 + 1.1650 + if (frameno == numFrames - 1) { 1.1651 + outerScript = frame->script(); 1.1652 + outerFp = iter.fp(); 1.1653 + } 1.1654 + 1.1655 + frameno++; 1.1656 + } 1.1657 + 1.1658 + ++iter; 1.1659 + } 1.1660 + 1.1661 + MOZ_ASSERT(innerScript); 1.1662 + MOZ_ASSERT(outerScript); 1.1663 + MOZ_ASSERT(outerFp); 1.1664 + 1.1665 + // If we rematerialized Ion frames due to debug mode toggling, copy their 1.1666 + // values into the baseline frame. We need to do this even when debug mode 1.1667 + // is off, as we should respect the mutations made while debug mode was 1.1668 + // on. 1.1669 + JitActivation *act = cx->mainThread().activation()->asJit(); 1.1670 + if (act->hasRematerializedFrame(outerFp)) { 1.1671 + JitFrameIterator iter(cx); 1.1672 + size_t inlineDepth = numFrames; 1.1673 + while (inlineDepth > 0) { 1.1674 + if (iter.isBaselineJS() && 1.1675 + !CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth, 1.1676 + iter.baselineFrame())) 1.1677 + { 1.1678 + return false; 1.1679 + } 1.1680 + ++iter; 1.1681 + } 1.1682 + 1.1683 + // After copying from all the rematerialized frames, remove them from 1.1684 + // the table to keep the table up to date. 1.1685 + act->removeRematerializedFrame(outerFp); 1.1686 + } 1.1687 + 1.1688 + IonSpew(IonSpew_BaselineBailouts, 1.1689 + " Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)", 1.1690 + outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(), 1.1691 + innerScript->filename(), innerScript->lineno(), innerScript->getUseCount(), 1.1692 + (unsigned) bailoutKind); 1.1693 + 1.1694 + switch (bailoutKind) { 1.1695 + case Bailout_Normal: 1.1696 + // Do nothing. 1.1697 + break; 1.1698 + case Bailout_ArgumentCheck: 1.1699 + // Do nothing, bailout will resume before the argument monitor ICs. 1.1700 + break; 1.1701 + case Bailout_BoundsCheck: 1.1702 + if (!HandleBoundsCheckFailure(cx, outerScript, innerScript)) 1.1703 + return false; 1.1704 + break; 1.1705 + case Bailout_ShapeGuard: 1.1706 + if (!HandleShapeGuardFailure(cx, outerScript, innerScript)) 1.1707 + return false; 1.1708 + break; 1.1709 + case Bailout_BaselineInfo: 1.1710 + if (!HandleBaselineInfoBailout(cx, outerScript, innerScript)) 1.1711 + return false; 1.1712 + break; 1.1713 + case Bailout_IonExceptionDebugMode: 1.1714 + // Return false to resume in HandleException with reconstructed 1.1715 + // baseline frame. 1.1716 + return false; 1.1717 + default: 1.1718 + MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!"); 1.1719 + } 1.1720 + 1.1721 + if (!CheckFrequentBailouts(cx, outerScript)) 1.1722 + return false; 1.1723 + 1.1724 + return true; 1.1725 +}