Sat, 03 Jan 2015 20:18:00 +0100
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 | } |