js/src/jit/IonFrames.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/IonFrames-inl.h"
michael@0 8
michael@0 9 #include "jsfun.h"
michael@0 10 #include "jsobj.h"
michael@0 11 #include "jsscript.h"
michael@0 12
michael@0 13 #include "gc/Marking.h"
michael@0 14 #include "jit/BaselineDebugModeOSR.h"
michael@0 15 #include "jit/BaselineFrame.h"
michael@0 16 #include "jit/BaselineIC.h"
michael@0 17 #include "jit/BaselineJIT.h"
michael@0 18 #include "jit/Ion.h"
michael@0 19 #include "jit/IonMacroAssembler.h"
michael@0 20 #include "jit/IonSpewer.h"
michael@0 21 #include "jit/JitCompartment.h"
michael@0 22 #include "jit/ParallelFunctions.h"
michael@0 23 #include "jit/PcScriptCache.h"
michael@0 24 #include "jit/Recover.h"
michael@0 25 #include "jit/Safepoints.h"
michael@0 26 #include "jit/Snapshots.h"
michael@0 27 #include "jit/VMFunctions.h"
michael@0 28 #include "vm/ArgumentsObject.h"
michael@0 29 #include "vm/ForkJoin.h"
michael@0 30 #include "vm/Interpreter.h"
michael@0 31
michael@0 32 #include "jsscriptinlines.h"
michael@0 33 #include "jit/JitFrameIterator-inl.h"
michael@0 34 #include "vm/Probes-inl.h"
michael@0 35
michael@0 36 namespace js {
michael@0 37 namespace jit {
michael@0 38
michael@0 39 // Given a slot index, returns the offset, in bytes, of that slot from an
michael@0 40 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
michael@0 41 // the distance does depend on the size of the frame header.
michael@0 42 static inline int32_t
michael@0 43 OffsetOfFrameSlot(int32_t slot)
michael@0 44 {
michael@0 45 return -slot;
michael@0 46 }
michael@0 47
michael@0 48 static inline uintptr_t
michael@0 49 ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot)
michael@0 50 {
michael@0 51 return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot));
michael@0 52 }
michael@0 53
michael@0 54 static inline double
michael@0 55 ReadFrameDoubleSlot(IonJSFrameLayout *fp, int32_t slot)
michael@0 56 {
michael@0 57 return *(double *)((char *)fp + OffsetOfFrameSlot(slot));
michael@0 58 }
michael@0 59
michael@0 60 static inline float
michael@0 61 ReadFrameFloat32Slot(IonJSFrameLayout *fp, int32_t slot)
michael@0 62 {
michael@0 63 return *(float *)((char *)fp + OffsetOfFrameSlot(slot));
michael@0 64 }
michael@0 65
michael@0 66 static inline int32_t
michael@0 67 ReadFrameInt32Slot(IonJSFrameLayout *fp, int32_t slot)
michael@0 68 {
michael@0 69 return *(int32_t *)((char *)fp + OffsetOfFrameSlot(slot));
michael@0 70 }
michael@0 71
michael@0 72 static inline bool
michael@0 73 ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot)
michael@0 74 {
michael@0 75 return *(bool *)((char *)fp + OffsetOfFrameSlot(slot));
michael@0 76 }
michael@0 77
michael@0 78 JitFrameIterator::JitFrameIterator(JSContext *cx)
michael@0 79 : current_(cx->mainThread().ionTop),
michael@0 80 type_(JitFrame_Exit),
michael@0 81 returnAddressToFp_(nullptr),
michael@0 82 frameSize_(0),
michael@0 83 cachedSafepointIndex_(nullptr),
michael@0 84 activation_(nullptr),
michael@0 85 mode_(SequentialExecution)
michael@0 86 {
michael@0 87 }
michael@0 88
michael@0 89 JitFrameIterator::JitFrameIterator(const ActivationIterator &activations)
michael@0 90 : current_(activations.jitTop()),
michael@0 91 type_(JitFrame_Exit),
michael@0 92 returnAddressToFp_(nullptr),
michael@0 93 frameSize_(0),
michael@0 94 cachedSafepointIndex_(nullptr),
michael@0 95 activation_(activations->asJit()),
michael@0 96 mode_(SequentialExecution)
michael@0 97 {
michael@0 98 }
michael@0 99
michael@0 100 JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
michael@0 101 : current_((uint8_t *)fp),
michael@0 102 type_(JitFrame_IonJS),
michael@0 103 returnAddressToFp_(fp->returnAddress()),
michael@0 104 frameSize_(fp->prevFrameLocalSize()),
michael@0 105 mode_(mode)
michael@0 106 {
michael@0 107 }
michael@0 108
michael@0 109 bool
michael@0 110 JitFrameIterator::checkInvalidation() const
michael@0 111 {
michael@0 112 IonScript *dummy;
michael@0 113 return checkInvalidation(&dummy);
michael@0 114 }
michael@0 115
michael@0 116 bool
michael@0 117 JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
michael@0 118 {
michael@0 119 uint8_t *returnAddr = returnAddressToFp();
michael@0 120 JSScript *script = this->script();
michael@0 121 // N.B. the current IonScript is not the same as the frame's
michael@0 122 // IonScript if the frame has since been invalidated.
michael@0 123 bool invalidated;
michael@0 124 if (mode_ == ParallelExecution) {
michael@0 125 // Parallel execution does not have invalidating bailouts.
michael@0 126 invalidated = false;
michael@0 127 } else {
michael@0 128 invalidated = !script->hasIonScript() ||
michael@0 129 !script->ionScript()->containsReturnAddress(returnAddr);
michael@0 130 }
michael@0 131 if (!invalidated)
michael@0 132 return false;
michael@0 133
michael@0 134 int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1];
michael@0 135 uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset;
michael@0 136 IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset);
michael@0 137 JS_ASSERT(ionScript->containsReturnAddress(returnAddr));
michael@0 138 *ionScriptOut = ionScript;
michael@0 139 return true;
michael@0 140 }
michael@0 141
michael@0 142 CalleeToken
michael@0 143 JitFrameIterator::calleeToken() const
michael@0 144 {
michael@0 145 return ((IonJSFrameLayout *) current_)->calleeToken();
michael@0 146 }
michael@0 147
michael@0 148 JSFunction *
michael@0 149 JitFrameIterator::callee() const
michael@0 150 {
michael@0 151 JS_ASSERT(isScripted());
michael@0 152 JS_ASSERT(isFunctionFrame());
michael@0 153 return CalleeTokenToFunction(calleeToken());
michael@0 154 }
michael@0 155
michael@0 156 JSFunction *
michael@0 157 JitFrameIterator::maybeCallee() const
michael@0 158 {
michael@0 159 if (isScripted() && (isFunctionFrame()))
michael@0 160 return callee();
michael@0 161 return nullptr;
michael@0 162 }
michael@0 163
michael@0 164 bool
michael@0 165 JitFrameIterator::isNative() const
michael@0 166 {
michael@0 167 if (type_ != JitFrame_Exit || isFakeExitFrame())
michael@0 168 return false;
michael@0 169 return exitFrame()->footer()->jitCode() == nullptr;
michael@0 170 }
michael@0 171
michael@0 172 bool
michael@0 173 JitFrameIterator::isOOLNative() const
michael@0 174 {
michael@0 175 if (type_ != JitFrame_Exit)
michael@0 176 return false;
michael@0 177 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE;
michael@0 178 }
michael@0 179
michael@0 180 bool
michael@0 181 JitFrameIterator::isOOLPropertyOp() const
michael@0 182 {
michael@0 183 if (type_ != JitFrame_Exit)
michael@0 184 return false;
michael@0 185 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP;
michael@0 186 }
michael@0 187
michael@0 188 bool
michael@0 189 JitFrameIterator::isOOLProxy() const
michael@0 190 {
michael@0 191 if (type_ != JitFrame_Exit)
michael@0 192 return false;
michael@0 193 return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY;
michael@0 194 }
michael@0 195
michael@0 196 bool
michael@0 197 JitFrameIterator::isDOMExit() const
michael@0 198 {
michael@0 199 if (type_ != JitFrame_Exit)
michael@0 200 return false;
michael@0 201 return exitFrame()->isDomExit();
michael@0 202 }
michael@0 203
michael@0 204 bool
michael@0 205 JitFrameIterator::isFunctionFrame() const
michael@0 206 {
michael@0 207 return CalleeTokenIsFunction(calleeToken());
michael@0 208 }
michael@0 209
michael@0 210 JSScript *
michael@0 211 JitFrameIterator::script() const
michael@0 212 {
michael@0 213 JS_ASSERT(isScripted());
michael@0 214 if (isBaselineJS())
michael@0 215 return baselineFrame()->script();
michael@0 216 JSScript *script = ScriptFromCalleeToken(calleeToken());
michael@0 217 JS_ASSERT(script);
michael@0 218 return script;
michael@0 219 }
michael@0 220
michael@0 221 void
michael@0 222 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
michael@0 223 {
michael@0 224 JS_ASSERT(isBaselineJS());
michael@0 225 JSScript *script = this->script();
michael@0 226 if (scriptRes)
michael@0 227 *scriptRes = script;
michael@0 228 uint8_t *retAddr = returnAddressToFp();
michael@0 229
michael@0 230 // If we are in the middle of a recompile handler, get the real return
michael@0 231 // address as stashed in the RecompileInfo.
michael@0 232 if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo())
michael@0 233 retAddr = info->resumeAddr;
michael@0 234
michael@0 235 if (pcRes) {
michael@0 236 // If the return address is into the prologue entry address or just
michael@0 237 // after the debug prologue, then assume start of script.
michael@0 238 if (retAddr == script->baselineScript()->prologueEntryAddr() ||
michael@0 239 retAddr == script->baselineScript()->postDebugPrologueAddr())
michael@0 240 {
michael@0 241 *pcRes = script->code();
michael@0 242 return;
michael@0 243 }
michael@0 244
michael@0 245 // The return address _may_ be a return from a callVM or IC chain call done for
michael@0 246 // some op.
michael@0 247 ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr);
michael@0 248 if (icEntry) {
michael@0 249 *pcRes = icEntry->pc(script);
michael@0 250 return;
michael@0 251 }
michael@0 252
michael@0 253 // If not, the return address _must_ be the start address of an op, which can
michael@0 254 // be computed from the pc mapping table.
michael@0 255 *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr);
michael@0 256 }
michael@0 257 }
michael@0 258
michael@0 259 Value *
michael@0 260 JitFrameIterator::actualArgs() const
michael@0 261 {
michael@0 262 return jsFrame()->argv() + 1;
michael@0 263 }
michael@0 264
michael@0 265 static inline size_t
michael@0 266 SizeOfFramePrefix(FrameType type)
michael@0 267 {
michael@0 268 switch (type) {
michael@0 269 case JitFrame_Entry:
michael@0 270 return IonEntryFrameLayout::Size();
michael@0 271 case JitFrame_BaselineJS:
michael@0 272 case JitFrame_IonJS:
michael@0 273 case JitFrame_Unwound_IonJS:
michael@0 274 return IonJSFrameLayout::Size();
michael@0 275 case JitFrame_BaselineStub:
michael@0 276 return IonBaselineStubFrameLayout::Size();
michael@0 277 case JitFrame_Rectifier:
michael@0 278 return IonRectifierFrameLayout::Size();
michael@0 279 case JitFrame_Unwound_Rectifier:
michael@0 280 return IonUnwoundRectifierFrameLayout::Size();
michael@0 281 case JitFrame_Exit:
michael@0 282 return IonExitFrameLayout::Size();
michael@0 283 default:
michael@0 284 MOZ_ASSUME_UNREACHABLE("unknown frame type");
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 uint8_t *
michael@0 289 JitFrameIterator::prevFp() const
michael@0 290 {
michael@0 291 size_t currentSize = SizeOfFramePrefix(type_);
michael@0 292 // This quick fix must be removed as soon as bug 717297 land. This is
michael@0 293 // needed because the descriptor size of JS-to-JS frame which is just after
michael@0 294 // a Rectifier frame should not change. (cf EnsureExitFrame function)
michael@0 295 if (isFakeExitFrame()) {
michael@0 296 JS_ASSERT(SizeOfFramePrefix(JitFrame_BaselineJS) ==
michael@0 297 SizeOfFramePrefix(JitFrame_IonJS));
michael@0 298 currentSize = SizeOfFramePrefix(JitFrame_IonJS);
michael@0 299 }
michael@0 300 currentSize += current()->prevFrameLocalSize();
michael@0 301 return current_ + currentSize;
michael@0 302 }
michael@0 303
michael@0 304 JitFrameIterator &
michael@0 305 JitFrameIterator::operator++()
michael@0 306 {
michael@0 307 JS_ASSERT(type_ != JitFrame_Entry);
michael@0 308
michael@0 309 frameSize_ = prevFrameLocalSize();
michael@0 310 cachedSafepointIndex_ = nullptr;
michael@0 311
michael@0 312 // If the next frame is the entry frame, just exit. Don't update current_,
michael@0 313 // since the entry and first frames overlap.
michael@0 314 if (current()->prevType() == JitFrame_Entry) {
michael@0 315 type_ = JitFrame_Entry;
michael@0 316 return *this;
michael@0 317 }
michael@0 318
michael@0 319 // Note: prevFp() needs the current type, so set it after computing the
michael@0 320 // next frame.
michael@0 321 uint8_t *prev = prevFp();
michael@0 322 type_ = current()->prevType();
michael@0 323 if (type_ == JitFrame_Unwound_IonJS)
michael@0 324 type_ = JitFrame_IonJS;
michael@0 325 else if (type_ == JitFrame_Unwound_BaselineStub)
michael@0 326 type_ = JitFrame_BaselineStub;
michael@0 327 returnAddressToFp_ = current()->returnAddress();
michael@0 328 current_ = prev;
michael@0 329 return *this;
michael@0 330 }
michael@0 331
michael@0 332 uintptr_t *
michael@0 333 JitFrameIterator::spillBase() const
michael@0 334 {
michael@0 335 // Get the base address to where safepoint registers are spilled.
michael@0 336 // Out-of-line calls do not unwind the extra padding space used to
michael@0 337 // aggregate bailout tables, so we use frameSize instead of frameLocals,
michael@0 338 // which would only account for local stack slots.
michael@0 339 return reinterpret_cast<uintptr_t *>(fp() - ionScript()->frameSize());
michael@0 340 }
michael@0 341
michael@0 342 MachineState
michael@0 343 JitFrameIterator::machineState() const
michael@0 344 {
michael@0 345 SafepointReader reader(ionScript(), safepoint());
michael@0 346 uintptr_t *spill = spillBase();
michael@0 347
michael@0 348 MachineState machine;
michael@0 349 for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++)
michael@0 350 machine.setRegisterLocation(*iter, --spill);
michael@0 351
michael@0 352 double *floatSpill = reinterpret_cast<double *>(spill);
michael@0 353 for (FloatRegisterBackwardIterator iter(reader.allFloatSpills()); iter.more(); iter++)
michael@0 354 machine.setRegisterLocation(*iter, --floatSpill);
michael@0 355
michael@0 356 return machine;
michael@0 357 }
michael@0 358
michael@0 359 static void
michael@0 360 CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t localSlot)
michael@0 361 {
michael@0 362 SnapshotIterator si = frame.snapshotIterator();
michael@0 363
michael@0 364 // Skip stack slots until we reach the iterator object.
michael@0 365 uint32_t base = CountArgSlots(frame.script(), frame.maybeCallee()) + frame.script()->nfixed();
michael@0 366 uint32_t skipSlots = base + localSlot - 1;
michael@0 367
michael@0 368 for (unsigned i = 0; i < skipSlots; i++)
michael@0 369 si.skip();
michael@0 370
michael@0 371 Value v = si.read();
michael@0 372 RootedObject obj(cx, &v.toObject());
michael@0 373
michael@0 374 if (cx->isExceptionPending())
michael@0 375 UnwindIteratorForException(cx, obj);
michael@0 376 else
michael@0 377 UnwindIteratorForUncatchableException(cx, obj);
michael@0 378 }
michael@0 379
michael@0 380 static void
michael@0 381 HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe,
michael@0 382 bool *overrecursed)
michael@0 383 {
michael@0 384 RootedScript script(cx, frame.script());
michael@0 385 jsbytecode *pc = frame.pc();
michael@0 386
michael@0 387 bool bailedOutForDebugMode = false;
michael@0 388 if (cx->compartment()->debugMode()) {
michael@0 389 // If we have an exception from within Ion and the debugger is active,
michael@0 390 // we do the following:
michael@0 391 //
michael@0 392 // 1. Bailout to baseline to reconstruct a baseline frame.
michael@0 393 // 2. Resume immediately into the exception tail afterwards, and
michael@0 394 // handle the exception again with the top frame now a baseline
michael@0 395 // frame.
michael@0 396 //
michael@0 397 // An empty exception info denotes that we're propagating an Ion
michael@0 398 // exception due to debug mode, which BailoutIonToBaseline needs to
michael@0 399 // know. This is because we might not be able to fully reconstruct up
michael@0 400 // to the stack depth at the snapshot, as we could've thrown in the
michael@0 401 // middle of a call.
michael@0 402 ExceptionBailoutInfo propagateInfo;
michael@0 403 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed);
michael@0 404 bailedOutForDebugMode = retval == BAILOUT_RETURN_OK;
michael@0 405 }
michael@0 406
michael@0 407 if (!script->hasTrynotes())
michael@0 408 return;
michael@0 409
michael@0 410 JSTryNote *tn = script->trynotes()->vector;
michael@0 411 JSTryNote *tnEnd = tn + script->trynotes()->length;
michael@0 412
michael@0 413 uint32_t pcOffset = uint32_t(pc - script->main());
michael@0 414 for (; tn != tnEnd; ++tn) {
michael@0 415 if (pcOffset < tn->start)
michael@0 416 continue;
michael@0 417 if (pcOffset >= tn->start + tn->length)
michael@0 418 continue;
michael@0 419
michael@0 420 switch (tn->kind) {
michael@0 421 case JSTRY_ITER: {
michael@0 422 JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
michael@0 423 JS_ASSERT(tn->stackDepth > 0);
michael@0 424
michael@0 425 uint32_t localSlot = tn->stackDepth;
michael@0 426 CloseLiveIterator(cx, frame, localSlot);
michael@0 427 break;
michael@0 428 }
michael@0 429
michael@0 430 case JSTRY_LOOP:
michael@0 431 break;
michael@0 432
michael@0 433 case JSTRY_CATCH:
michael@0 434 if (cx->isExceptionPending() && !bailedOutForDebugMode) {
michael@0 435 // Ion can compile try-catch, but bailing out to catch
michael@0 436 // exceptions is slow. Reset the use count so that if we
michael@0 437 // catch many exceptions we won't Ion-compile the script.
michael@0 438 script->resetUseCount();
michael@0 439
michael@0 440 // Bailout at the start of the catch block.
michael@0 441 jsbytecode *catchPC = script->main() + tn->start + tn->length;
michael@0 442 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
michael@0 443 uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
michael@0 444 if (retval == BAILOUT_RETURN_OK)
michael@0 445 return;
michael@0 446
michael@0 447 // Error on bailout clears pending exception.
michael@0 448 MOZ_ASSERT(!cx->isExceptionPending());
michael@0 449 }
michael@0 450 break;
michael@0 451
michael@0 452 default:
michael@0 453 MOZ_ASSUME_UNREACHABLE("Unexpected try note");
michael@0 454 }
michael@0 455 }
michael@0 456 }
michael@0 457
michael@0 458 static void
michael@0 459 HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
michael@0 460 bool *calledDebugEpilogue)
michael@0 461 {
michael@0 462 JS_ASSERT(frame.isBaselineJS());
michael@0 463 JS_ASSERT(!*calledDebugEpilogue);
michael@0 464
michael@0 465 RootedScript script(cx);
michael@0 466 jsbytecode *pc;
michael@0 467 frame.baselineScriptAndPc(script.address(), &pc);
michael@0 468
michael@0 469 if (cx->isExceptionPending() && cx->compartment()->debugMode()) {
michael@0 470 BaselineFrame *baselineFrame = frame.baselineFrame();
michael@0 471 JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc);
michael@0 472 switch (status) {
michael@0 473 case JSTRAP_ERROR:
michael@0 474 // Uncatchable exception.
michael@0 475 JS_ASSERT(!cx->isExceptionPending());
michael@0 476 break;
michael@0 477
michael@0 478 case JSTRAP_CONTINUE:
michael@0 479 case JSTRAP_THROW:
michael@0 480 JS_ASSERT(cx->isExceptionPending());
michael@0 481 break;
michael@0 482
michael@0 483 case JSTRAP_RETURN:
michael@0 484 JS_ASSERT(baselineFrame->hasReturnValue());
michael@0 485 if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) {
michael@0 486 rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
michael@0 487 rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
michael@0 488 rfe->stackPointer = reinterpret_cast<uint8_t *>(baselineFrame);
michael@0 489 return;
michael@0 490 }
michael@0 491
michael@0 492 // DebugEpilogue threw an exception. Propagate to the caller frame.
michael@0 493 *calledDebugEpilogue = true;
michael@0 494 return;
michael@0 495
michael@0 496 default:
michael@0 497 MOZ_ASSUME_UNREACHABLE("Invalid trap status");
michael@0 498 }
michael@0 499 }
michael@0 500
michael@0 501 if (!script->hasTrynotes())
michael@0 502 return;
michael@0 503
michael@0 504 JSTryNote *tn = script->trynotes()->vector;
michael@0 505 JSTryNote *tnEnd = tn + script->trynotes()->length;
michael@0 506
michael@0 507 uint32_t pcOffset = uint32_t(pc - script->main());
michael@0 508 ScopeIter si(frame.baselineFrame(), pc, cx);
michael@0 509 for (; tn != tnEnd; ++tn) {
michael@0 510 if (pcOffset < tn->start)
michael@0 511 continue;
michael@0 512 if (pcOffset >= tn->start + tn->length)
michael@0 513 continue;
michael@0 514
michael@0 515 // Skip if the try note's stack depth exceeds the frame's stack depth.
michael@0 516 // See the big comment in TryNoteIter::settle for more info.
michael@0 517 JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed());
michael@0 518 size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed();
michael@0 519 if (tn->stackDepth > stackDepth)
michael@0 520 continue;
michael@0 521
michael@0 522 // Unwind scope chain (pop block objects).
michael@0 523 if (cx->isExceptionPending())
michael@0 524 UnwindScope(cx, si, script->main() + tn->start);
michael@0 525
michael@0 526 // Compute base pointer and stack pointer.
michael@0 527 rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
michael@0 528 rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() -
michael@0 529 (script->nfixed() + tn->stackDepth) * sizeof(Value);
michael@0 530
michael@0 531 switch (tn->kind) {
michael@0 532 case JSTRY_CATCH:
michael@0 533 if (cx->isExceptionPending()) {
michael@0 534 // Ion can compile try-catch, but bailing out to catch
michael@0 535 // exceptions is slow. Reset the use count so that if we
michael@0 536 // catch many exceptions we won't Ion-compile the script.
michael@0 537 script->resetUseCount();
michael@0 538
michael@0 539 // Resume at the start of the catch block.
michael@0 540 rfe->kind = ResumeFromException::RESUME_CATCH;
michael@0 541 jsbytecode *catchPC = script->main() + tn->start + tn->length;
michael@0 542 rfe->target = script->baselineScript()->nativeCodeForPC(script, catchPC);
michael@0 543 return;
michael@0 544 }
michael@0 545 break;
michael@0 546
michael@0 547 case JSTRY_FINALLY:
michael@0 548 if (cx->isExceptionPending()) {
michael@0 549 rfe->kind = ResumeFromException::RESUME_FINALLY;
michael@0 550 jsbytecode *finallyPC = script->main() + tn->start + tn->length;
michael@0 551 rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC);
michael@0 552 // Drop the exception instead of leaking cross compartment data.
michael@0 553 if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception)))
michael@0 554 rfe->exception = UndefinedValue();
michael@0 555 cx->clearPendingException();
michael@0 556 return;
michael@0 557 }
michael@0 558 break;
michael@0 559
michael@0 560 case JSTRY_ITER: {
michael@0 561 Value iterValue(* (Value *) rfe->stackPointer);
michael@0 562 RootedObject iterObject(cx, &iterValue.toObject());
michael@0 563 if (cx->isExceptionPending())
michael@0 564 UnwindIteratorForException(cx, iterObject);
michael@0 565 else
michael@0 566 UnwindIteratorForUncatchableException(cx, iterObject);
michael@0 567 break;
michael@0 568 }
michael@0 569
michael@0 570 case JSTRY_LOOP:
michael@0 571 break;
michael@0 572
michael@0 573 default:
michael@0 574 MOZ_ASSUME_UNREACHABLE("Invalid try note");
michael@0 575 }
michael@0 576 }
michael@0 577
michael@0 578 }
michael@0 579
michael@0 580 struct AutoDeleteDebugModeOSRInfo
michael@0 581 {
michael@0 582 BaselineFrame *frame;
michael@0 583 AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); }
michael@0 584 ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); }
michael@0 585 };
michael@0 586
michael@0 587 void
michael@0 588 HandleException(ResumeFromException *rfe)
michael@0 589 {
michael@0 590 JSContext *cx = GetJSContextFromJitCode();
michael@0 591
michael@0 592 rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
michael@0 593
michael@0 594 IonSpew(IonSpew_Invalidate, "handling exception");
michael@0 595
michael@0 596 // Clear any Ion return override that's been set.
michael@0 597 // This may happen if a callVM function causes an invalidation (setting the
michael@0 598 // override), and then fails, bypassing the bailout handlers that would
michael@0 599 // otherwise clear the return override.
michael@0 600 if (cx->runtime()->hasIonReturnOverride())
michael@0 601 cx->runtime()->takeIonReturnOverride();
michael@0 602
michael@0 603 JitFrameIterator iter(cx);
michael@0 604 while (!iter.isEntry()) {
michael@0 605 bool overrecursed = false;
michael@0 606 if (iter.isIonJS()) {
michael@0 607 // Search each inlined frame for live iterator objects, and close
michael@0 608 // them.
michael@0 609 InlineFrameIterator frames(cx, &iter);
michael@0 610
michael@0 611 // Invalidation state will be the same for all inlined scripts in the frame.
michael@0 612 IonScript *ionScript = nullptr;
michael@0 613 bool invalidated = iter.checkInvalidation(&ionScript);
michael@0 614
michael@0 615 for (;;) {
michael@0 616 HandleExceptionIon(cx, frames, rfe, &overrecursed);
michael@0 617
michael@0 618 if (rfe->kind == ResumeFromException::RESUME_BAILOUT) {
michael@0 619 if (invalidated)
michael@0 620 ionScript->decref(cx->runtime()->defaultFreeOp());
michael@0 621 return;
michael@0 622 }
michael@0 623
michael@0 624 JS_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME);
michael@0 625
michael@0 626 // Figure out whether SPS frame was pushed for this frame or not.
michael@0 627 // Even if profiler is enabled, the frame being popped might have
michael@0 628 // been entered prior to SPS being enabled, and thus not have
michael@0 629 // a pushed SPS frame.
michael@0 630 bool popSPSFrame = cx->runtime()->spsProfiler.enabled();
michael@0 631 if (invalidated)
michael@0 632 popSPSFrame = ionScript->hasSPSInstrumentation();
michael@0 633
michael@0 634 // If inline-frames are not profiled, then don't pop an SPS frame
michael@0 635 // for them.
michael@0 636 if (frames.more() && !js_JitOptions.profileInlineFrames)
michael@0 637 popSPSFrame = false;
michael@0 638
michael@0 639 // When profiling, each frame popped needs a notification that
michael@0 640 // the function has exited, so invoke the probe that a function
michael@0 641 // is exiting.
michael@0 642 JSScript *script = frames.script();
michael@0 643 probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
michael@0 644 if (!frames.more())
michael@0 645 break;
michael@0 646 ++frames;
michael@0 647 }
michael@0 648
michael@0 649 if (invalidated)
michael@0 650 ionScript->decref(cx->runtime()->defaultFreeOp());
michael@0 651
michael@0 652 } else if (iter.isBaselineJS()) {
michael@0 653 // It's invalid to call DebugEpilogue twice for the same frame.
michael@0 654 bool calledDebugEpilogue = false;
michael@0 655
michael@0 656 HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
michael@0 657
michael@0 658 // If we are propagating an exception through a frame with
michael@0 659 // on-stack recompile info, we should free the allocated
michael@0 660 // RecompileInfo struct before we leave this block, as we will not
michael@0 661 // be returning to the recompile handler.
michael@0 662 //
michael@0 663 // We cannot delete it immediately because of the call to
michael@0 664 // iter.baselineScriptAndPc below.
michael@0 665 AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame());
michael@0 666
michael@0 667 if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
michael@0 668 return;
michael@0 669
michael@0 670 // Unwind profiler pseudo-stack
michael@0 671 JSScript *script = iter.script();
michael@0 672 probes::ExitScript(cx, script, script->functionNonDelazifying(),
michael@0 673 iter.baselineFrame()->hasPushedSPSFrame());
michael@0 674 // After this point, any pushed SPS frame would have been popped if it needed
michael@0 675 // to be. Unset the flag here so that if we call DebugEpilogue below,
michael@0 676 // it doesn't try to pop the SPS frame again.
michael@0 677 iter.baselineFrame()->unsetPushedSPSFrame();
michael@0 678
michael@0 679 if (cx->compartment()->debugMode() && !calledDebugEpilogue) {
michael@0 680 // If DebugEpilogue returns |true|, we have to perform a forced
michael@0 681 // return, e.g. return frame->returnValue() to the caller.
michael@0 682 BaselineFrame *frame = iter.baselineFrame();
michael@0 683 RootedScript script(cx);
michael@0 684 jsbytecode *pc;
michael@0 685 iter.baselineScriptAndPc(script.address(), &pc);
michael@0 686 if (jit::DebugEpilogue(cx, frame, pc, false)) {
michael@0 687 JS_ASSERT(frame->hasReturnValue());
michael@0 688 rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
michael@0 689 rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;
michael@0 690 rfe->stackPointer = reinterpret_cast<uint8_t *>(frame);
michael@0 691 return;
michael@0 692 }
michael@0 693 }
michael@0 694 }
michael@0 695
michael@0 696 IonJSFrameLayout *current = iter.isScripted() ? iter.jsFrame() : nullptr;
michael@0 697
michael@0 698 ++iter;
michael@0 699
michael@0 700 if (current) {
michael@0 701 // Unwind the frame by updating ionTop. This is necessary so that
michael@0 702 // (1) debugger exception unwind and leave frame hooks don't see this
michael@0 703 // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does
michael@0 704 // not crash when accessing an IonScript that's destroyed by the
michael@0 705 // ionScript->decref call.
michael@0 706 EnsureExitFrame(current);
michael@0 707 cx->mainThread().ionTop = (uint8_t *)current;
michael@0 708 }
michael@0 709
michael@0 710 if (overrecursed) {
michael@0 711 // We hit an overrecursion error during bailout. Report it now.
michael@0 712 js_ReportOverRecursed(cx);
michael@0 713 }
michael@0 714 }
michael@0 715
michael@0 716 rfe->stackPointer = iter.fp();
michael@0 717 }
michael@0 718
michael@0 719 void
michael@0 720 HandleParallelFailure(ResumeFromException *rfe)
michael@0 721 {
michael@0 722 ForkJoinContext *cx = ForkJoinContext::current();
michael@0 723 JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
michael@0 724
michael@0 725 parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
michael@0 726
michael@0 727 while (!iter.isEntry()) {
michael@0 728 if (iter.isScripted()) {
michael@0 729 cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
michael@0 730 iter.script(), iter.script(), nullptr);
michael@0 731 break;
michael@0 732 }
michael@0 733 ++iter;
michael@0 734 }
michael@0 735
michael@0 736 while (!iter.isEntry()) {
michael@0 737 if (iter.isScripted())
michael@0 738 PropagateAbortPar(iter.script(), iter.script());
michael@0 739 ++iter;
michael@0 740 }
michael@0 741
michael@0 742 rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
michael@0 743 rfe->stackPointer = iter.fp();
michael@0 744 }
michael@0 745
michael@0 746 void
michael@0 747 EnsureExitFrame(IonCommonFrameLayout *frame)
michael@0 748 {
michael@0 749 if (frame->prevType() == JitFrame_Unwound_IonJS ||
michael@0 750 frame->prevType() == JitFrame_Unwound_BaselineStub ||
michael@0 751 frame->prevType() == JitFrame_Unwound_Rectifier)
michael@0 752 {
michael@0 753 // Already an exit frame, nothing to do.
michael@0 754 return;
michael@0 755 }
michael@0 756
michael@0 757 if (frame->prevType() == JitFrame_Entry) {
michael@0 758 // The previous frame type is the entry frame, so there's no actual
michael@0 759 // need for an exit frame.
michael@0 760 return;
michael@0 761 }
michael@0 762
michael@0 763 if (frame->prevType() == JitFrame_Rectifier) {
michael@0 764 // The rectifier code uses the frame descriptor to discard its stack,
michael@0 765 // so modifying its descriptor size here would be dangerous. Instead,
michael@0 766 // we change the frame type, and teach the stack walking code how to
michael@0 767 // deal with this edge case. bug 717297 would obviate the need
michael@0 768 frame->changePrevType(JitFrame_Unwound_Rectifier);
michael@0 769 return;
michael@0 770 }
michael@0 771
michael@0 772 if (frame->prevType() == JitFrame_BaselineStub) {
michael@0 773 frame->changePrevType(JitFrame_Unwound_BaselineStub);
michael@0 774 return;
michael@0 775 }
michael@0 776
michael@0 777 JS_ASSERT(frame->prevType() == JitFrame_IonJS);
michael@0 778 frame->changePrevType(JitFrame_Unwound_IonJS);
michael@0 779 }
michael@0 780
michael@0 781 CalleeToken
michael@0 782 MarkCalleeToken(JSTracer *trc, CalleeToken token)
michael@0 783 {
michael@0 784 switch (GetCalleeTokenTag(token)) {
michael@0 785 case CalleeToken_Function:
michael@0 786 {
michael@0 787 JSFunction *fun = CalleeTokenToFunction(token);
michael@0 788 MarkObjectRoot(trc, &fun, "ion-callee");
michael@0 789 return CalleeToToken(fun);
michael@0 790 }
michael@0 791 case CalleeToken_Script:
michael@0 792 {
michael@0 793 JSScript *script = CalleeTokenToScript(token);
michael@0 794 MarkScriptRoot(trc, &script, "ion-entry");
michael@0 795 return CalleeToToken(script);
michael@0 796 }
michael@0 797 default:
michael@0 798 MOZ_ASSUME_UNREACHABLE("unknown callee token type");
michael@0 799 }
michael@0 800 }
michael@0 801
michael@0 802 #ifdef JS_NUNBOX32
michael@0 803 static inline uintptr_t
michael@0 804 ReadAllocation(const JitFrameIterator &frame, const LAllocation *a)
michael@0 805 {
michael@0 806 if (a->isGeneralReg()) {
michael@0 807 Register reg = a->toGeneralReg()->reg();
michael@0 808 return frame.machineState().read(reg);
michael@0 809 }
michael@0 810 if (a->isStackSlot()) {
michael@0 811 uint32_t slot = a->toStackSlot()->slot();
michael@0 812 return *frame.jsFrame()->slotRef(slot);
michael@0 813 }
michael@0 814 uint32_t index = a->toArgument()->index();
michael@0 815 uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
michael@0 816 return *reinterpret_cast<uintptr_t *>(argv + index);
michael@0 817 }
michael@0 818 #endif
michael@0 819
michael@0 820 static void
michael@0 821 MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame)
michael@0 822 {
michael@0 823 IonJSFrameLayout *layout = frame.jsFrame();
michael@0 824 JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken()));
michael@0 825
michael@0 826 size_t nargs = frame.numActualArgs();
michael@0 827
michael@0 828 // Trace function arguments. Note + 1 for thisv.
michael@0 829 Value *argv = layout->argv();
michael@0 830 for (size_t i = 0; i < nargs + 1; i++)
michael@0 831 gc::MarkValueRoot(trc, &argv[i], "ion-argv");
michael@0 832 }
michael@0 833
michael@0 834 #ifdef JS_NUNBOX32
michael@0 835 static inline void
michael@0 836 WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value)
michael@0 837 {
michael@0 838 if (a->isGeneralReg()) {
michael@0 839 Register reg = a->toGeneralReg()->reg();
michael@0 840 frame.machineState().write(reg, value);
michael@0 841 return;
michael@0 842 }
michael@0 843 if (a->isStackSlot()) {
michael@0 844 uint32_t slot = a->toStackSlot()->slot();
michael@0 845 *frame.jsFrame()->slotRef(slot) = value;
michael@0 846 return;
michael@0 847 }
michael@0 848 uint32_t index = a->toArgument()->index();
michael@0 849 uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
michael@0 850 *reinterpret_cast<uintptr_t *>(argv + index) = value;
michael@0 851 }
michael@0 852 #endif
michael@0 853
michael@0 854 static void
michael@0 855 MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame)
michael@0 856 {
michael@0 857 IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp();
michael@0 858
michael@0 859 layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
michael@0 860
michael@0 861 IonScript *ionScript = nullptr;
michael@0 862 if (frame.checkInvalidation(&ionScript)) {
michael@0 863 // This frame has been invalidated, meaning that its IonScript is no
michael@0 864 // longer reachable through the callee token (JSFunction/JSScript->ion
michael@0 865 // is now nullptr or recompiled). Manually trace it here.
michael@0 866 IonScript::Trace(trc, ionScript);
michael@0 867 } else if (CalleeTokenIsFunction(layout->calleeToken())) {
michael@0 868 ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript();
michael@0 869 } else {
michael@0 870 ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript();
michael@0 871 }
michael@0 872
michael@0 873 if (CalleeTokenIsFunction(layout->calleeToken()))
michael@0 874 MarkActualArguments(trc, frame);
michael@0 875
michael@0 876 const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp());
michael@0 877
michael@0 878 SafepointReader safepoint(ionScript, si);
michael@0 879
michael@0 880 // Scan through slots which contain pointers (or on punboxing systems,
michael@0 881 // actual values).
michael@0 882 uint32_t slot;
michael@0 883 while (safepoint.getGcSlot(&slot)) {
michael@0 884 uintptr_t *ref = layout->slotRef(slot);
michael@0 885 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot");
michael@0 886 }
michael@0 887
michael@0 888 while (safepoint.getValueSlot(&slot)) {
michael@0 889 Value *v = (Value *)layout->slotRef(slot);
michael@0 890 gc::MarkValueRoot(trc, v, "ion-gc-slot");
michael@0 891 }
michael@0 892
michael@0 893 uintptr_t *spill = frame.spillBase();
michael@0 894 GeneralRegisterSet gcRegs = safepoint.gcSpills();
michael@0 895 GeneralRegisterSet valueRegs = safepoint.valueSpills();
michael@0 896 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) {
michael@0 897 --spill;
michael@0 898 if (gcRegs.has(*iter))
michael@0 899 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(spill), "ion-gc-spill");
michael@0 900 else if (valueRegs.has(*iter))
michael@0 901 gc::MarkValueRoot(trc, reinterpret_cast<Value *>(spill), "ion-value-spill");
michael@0 902 }
michael@0 903
michael@0 904 #ifdef JS_NUNBOX32
michael@0 905 LAllocation type, payload;
michael@0 906 while (safepoint.getNunboxSlot(&type, &payload)) {
michael@0 907 jsval_layout layout;
michael@0 908 layout.s.tag = (JSValueTag)ReadAllocation(frame, &type);
michael@0 909 layout.s.payload.uintptr = ReadAllocation(frame, &payload);
michael@0 910
michael@0 911 Value v = IMPL_TO_JSVAL(layout);
michael@0 912 gc::MarkValueRoot(trc, &v, "ion-torn-value");
michael@0 913
michael@0 914 if (v != IMPL_TO_JSVAL(layout)) {
michael@0 915 // GC moved the value, replace the stored payload.
michael@0 916 layout = JSVAL_TO_IMPL(v);
michael@0 917 WriteAllocation(frame, &payload, layout.s.payload.uintptr);
michael@0 918 }
michael@0 919 }
michael@0 920 #endif
michael@0 921 }
michael@0 922
michael@0 923 #ifdef JSGC_GENERATIONAL
michael@0 924 static void
michael@0 925 UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame)
michael@0 926 {
michael@0 927 // Minor GCs may move slots/elements allocated in the nursery. Update
michael@0 928 // any slots/elements pointers stored in this frame.
michael@0 929
michael@0 930 IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp();
michael@0 931
michael@0 932 IonScript *ionScript = nullptr;
michael@0 933 if (frame.checkInvalidation(&ionScript)) {
michael@0 934 // This frame has been invalidated, meaning that its IonScript is no
michael@0 935 // longer reachable through the callee token (JSFunction/JSScript->ion
michael@0 936 // is now nullptr or recompiled).
michael@0 937 } else if (CalleeTokenIsFunction(layout->calleeToken())) {
michael@0 938 ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript();
michael@0 939 } else {
michael@0 940 ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript();
michael@0 941 }
michael@0 942
michael@0 943 const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp());
michael@0 944 SafepointReader safepoint(ionScript, si);
michael@0 945
michael@0 946 GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills();
michael@0 947 uintptr_t *spill = frame.spillBase();
michael@0 948 for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) {
michael@0 949 --spill;
michael@0 950 if (slotsRegs.has(*iter))
michael@0 951 trc->runtime()->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill));
michael@0 952 }
michael@0 953
michael@0 954 // Skip to the right place in the safepoint
michael@0 955 uint32_t slot;
michael@0 956 while (safepoint.getGcSlot(&slot));
michael@0 957 while (safepoint.getValueSlot(&slot));
michael@0 958 #ifdef JS_NUNBOX32
michael@0 959 LAllocation type, payload;
michael@0 960 while (safepoint.getNunboxSlot(&type, &payload));
michael@0 961 #endif
michael@0 962
michael@0 963 while (safepoint.getSlotsOrElementsSlot(&slot)) {
michael@0 964 HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot));
michael@0 965 trc->runtime()->gcNursery.forwardBufferPointer(slots);
michael@0 966 }
michael@0 967 }
michael@0 968 #endif
michael@0 969
michael@0 970 static void
michael@0 971 MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
michael@0 972 {
michael@0 973 // Mark the ICStub pointer stored in the stub frame. This is necessary
michael@0 974 // so that we don't destroy the stub code after unlinking the stub.
michael@0 975
michael@0 976 JS_ASSERT(frame.type() == JitFrame_BaselineStub);
michael@0 977 IonBaselineStubFrameLayout *layout = (IonBaselineStubFrameLayout *)frame.fp();
michael@0 978
michael@0 979 if (ICStub *stub = layout->maybeStubPtr()) {
michael@0 980 JS_ASSERT(ICStub::CanMakeCalls(stub->kind()));
michael@0 981 stub->trace(trc);
michael@0 982 }
michael@0 983 }
michael@0 984
michael@0 985 void
michael@0 986 JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
michael@0 987 {
michael@0 988 JitFrameIterator frames(jitTop(), SequentialExecution);
michael@0 989
michael@0 990 if (frames.isFakeExitFrame()) {
michael@0 991 min = reinterpret_cast<uintptr_t *>(frames.fp());
michael@0 992 } else {
michael@0 993 IonExitFrameLayout *exitFrame = frames.exitFrame();
michael@0 994 IonExitFooterFrame *footer = exitFrame->footer();
michael@0 995 const VMFunction *f = footer->function();
michael@0 996 if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) {
michael@0 997 switch (f->outParamRootType) {
michael@0 998 case VMFunction::RootNone:
michael@0 999 MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type");
michael@0 1000 case VMFunction::RootObject:
michael@0 1001 case VMFunction::RootString:
michael@0 1002 case VMFunction::RootPropertyName:
michael@0 1003 case VMFunction::RootFunction:
michael@0 1004 case VMFunction::RootCell:
michael@0 1005 // These are all handles to GCThing pointers.
michael@0 1006 min = reinterpret_cast<uintptr_t *>(footer->outParam<void *>());
michael@0 1007 break;
michael@0 1008 case VMFunction::RootValue:
michael@0 1009 min = reinterpret_cast<uintptr_t *>(footer->outParam<Value>());
michael@0 1010 break;
michael@0 1011 }
michael@0 1012 } else {
michael@0 1013 min = reinterpret_cast<uintptr_t *>(footer);
michael@0 1014 }
michael@0 1015 }
michael@0 1016
michael@0 1017 while (!frames.done())
michael@0 1018 ++frames;
michael@0 1019
michael@0 1020 end = reinterpret_cast<uintptr_t *>(frames.prevFp());
michael@0 1021 }
michael@0 1022
michael@0 1023 static void
michael@0 1024 MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
michael@0 1025 {
michael@0 1026 // Ignore fake exit frames created by EnsureExitFrame.
michael@0 1027 if (frame.isFakeExitFrame())
michael@0 1028 return;
michael@0 1029
michael@0 1030 IonExitFooterFrame *footer = frame.exitFrame()->footer();
michael@0 1031
michael@0 1032 // Mark the code of the code handling the exit path. This is needed because
michael@0 1033 // invalidated script are no longer marked because data are erased by the
michael@0 1034 // invalidation and relocation data are no longer reliable. So the VM
michael@0 1035 // wrapper or the invalidation code may be GC if no JitCode keep reference
michael@0 1036 // on them.
michael@0 1037 JS_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1));
michael@0 1038
michael@0 1039 // This correspond to the case where we have build a fake exit frame in
michael@0 1040 // CodeGenerator.cpp which handle the case of a native function call. We
michael@0 1041 // need to mark the argument vector of the function call.
michael@0 1042 if (frame.isNative()) {
michael@0 1043 IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit();
michael@0 1044 size_t len = native->argc() + 2;
michael@0 1045 Value *vp = native->vp();
michael@0 1046 gc::MarkValueRootRange(trc, len, vp, "ion-native-args");
michael@0 1047 return;
michael@0 1048 }
michael@0 1049
michael@0 1050 if (frame.isOOLNative()) {
michael@0 1051 IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->oolNativeExit();
michael@0 1052 gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code");
michael@0 1053 gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp");
michael@0 1054 size_t len = oolnative->argc() + 1;
michael@0 1055 gc::MarkValueRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs");
michael@0 1056 return;
michael@0 1057 }
michael@0 1058
michael@0 1059 if (frame.isOOLPropertyOp()) {
michael@0 1060 IonOOLPropertyOpExitFrameLayout *oolgetter = frame.exitFrame()->oolPropertyOpExit();
michael@0 1061 gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code");
michael@0 1062 gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp");
michael@0 1063 gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id");
michael@0 1064 gc::MarkObjectRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj");
michael@0 1065 return;
michael@0 1066 }
michael@0 1067
michael@0 1068 if (frame.isOOLProxy()) {
michael@0 1069 IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->oolProxyExit();
michael@0 1070 gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code");
michael@0 1071 gc::MarkValueRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp");
michael@0 1072 gc::MarkIdRoot(trc, oolproxy->id(), "ion-ool-proxy-id");
michael@0 1073 gc::MarkObjectRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy");
michael@0 1074 gc::MarkObjectRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver");
michael@0 1075 return;
michael@0 1076 }
michael@0 1077
michael@0 1078 if (frame.isDOMExit()) {
michael@0 1079 IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit();
michael@0 1080 gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args");
michael@0 1081 if (dom->isMethodFrame()) {
michael@0 1082 IonDOMMethodExitFrameLayout *method =
michael@0 1083 reinterpret_cast<IonDOMMethodExitFrameLayout *>(dom);
michael@0 1084 size_t len = method->argc() + 2;
michael@0 1085 Value *vp = method->vp();
michael@0 1086 gc::MarkValueRootRange(trc, len, vp, "ion-dom-args");
michael@0 1087 } else {
michael@0 1088 gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args");
michael@0 1089 }
michael@0 1090 return;
michael@0 1091 }
michael@0 1092
michael@0 1093 MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code");
michael@0 1094
michael@0 1095 const VMFunction *f = footer->function();
michael@0 1096 if (f == nullptr)
michael@0 1097 return;
michael@0 1098
michael@0 1099 // Mark arguments of the VM wrapper.
michael@0 1100 uint8_t *argBase = frame.exitFrame()->argBase();
michael@0 1101 for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
michael@0 1102 switch (f->argRootType(explicitArg)) {
michael@0 1103 case VMFunction::RootNone:
michael@0 1104 break;
michael@0 1105 case VMFunction::RootObject: {
michael@0 1106 // Sometimes we can bake in HandleObjects to nullptr.
michael@0 1107 JSObject **pobj = reinterpret_cast<JSObject **>(argBase);
michael@0 1108 if (*pobj)
michael@0 1109 gc::MarkObjectRoot(trc, pobj, "ion-vm-args");
michael@0 1110 break;
michael@0 1111 }
michael@0 1112 case VMFunction::RootString:
michael@0 1113 case VMFunction::RootPropertyName:
michael@0 1114 gc::MarkStringRoot(trc, reinterpret_cast<JSString**>(argBase), "ion-vm-args");
michael@0 1115 break;
michael@0 1116 case VMFunction::RootFunction:
michael@0 1117 gc::MarkObjectRoot(trc, reinterpret_cast<JSFunction**>(argBase), "ion-vm-args");
michael@0 1118 break;
michael@0 1119 case VMFunction::RootValue:
michael@0 1120 gc::MarkValueRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args");
michael@0 1121 break;
michael@0 1122 case VMFunction::RootCell:
michael@0 1123 gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(argBase), "ion-vm-args");
michael@0 1124 break;
michael@0 1125 }
michael@0 1126
michael@0 1127 switch (f->argProperties(explicitArg)) {
michael@0 1128 case VMFunction::WordByValue:
michael@0 1129 case VMFunction::WordByRef:
michael@0 1130 argBase += sizeof(void *);
michael@0 1131 break;
michael@0 1132 case VMFunction::DoubleByValue:
michael@0 1133 case VMFunction::DoubleByRef:
michael@0 1134 argBase += 2 * sizeof(void *);
michael@0 1135 break;
michael@0 1136 }
michael@0 1137 }
michael@0 1138
michael@0 1139 if (f->outParam == Type_Handle) {
michael@0 1140 switch (f->outParamRootType) {
michael@0 1141 case VMFunction::RootNone:
michael@0 1142 MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type");
michael@0 1143 case VMFunction::RootObject:
michael@0 1144 gc::MarkObjectRoot(trc, footer->outParam<JSObject *>(), "ion-vm-out");
michael@0 1145 break;
michael@0 1146 case VMFunction::RootString:
michael@0 1147 case VMFunction::RootPropertyName:
michael@0 1148 gc::MarkStringRoot(trc, footer->outParam<JSString *>(), "ion-vm-out");
michael@0 1149 break;
michael@0 1150 case VMFunction::RootFunction:
michael@0 1151 gc::MarkObjectRoot(trc, footer->outParam<JSFunction *>(), "ion-vm-out");
michael@0 1152 break;
michael@0 1153 case VMFunction::RootValue:
michael@0 1154 gc::MarkValueRoot(trc, footer->outParam<Value>(), "ion-vm-outvp");
michael@0 1155 break;
michael@0 1156 case VMFunction::RootCell:
michael@0 1157 gc::MarkGCThingRoot(trc, footer->outParam<void *>(), "ion-vm-out");
michael@0 1158 break;
michael@0 1159 }
michael@0 1160 }
michael@0 1161 }
michael@0 1162
michael@0 1163 static void
michael@0 1164 MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame)
michael@0 1165 {
michael@0 1166 // Mark thisv.
michael@0 1167 //
michael@0 1168 // Baseline JIT code generated as part of the ICCall_Fallback stub may use
michael@0 1169 // it if we're calling a constructor that returns a primitive value.
michael@0 1170 IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp();
michael@0 1171 gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv");
michael@0 1172 }
michael@0 1173
michael@0 1174 static void
michael@0 1175 MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
michael@0 1176 {
michael@0 1177 JitActivation *activation = activations->asJit();
michael@0 1178
michael@0 1179 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 1180 if (js_JitOptions.checkOsiPointRegisters) {
michael@0 1181 // GC can modify spilled registers, breaking our register checks.
michael@0 1182 // To handle this, we disable these checks for the current VM call
michael@0 1183 // when a GC happens.
michael@0 1184 activation->setCheckRegs(false);
michael@0 1185 }
michael@0 1186 #endif
michael@0 1187
michael@0 1188 activation->markRematerializedFrames(trc);
michael@0 1189
michael@0 1190 for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
michael@0 1191 switch (frames.type()) {
michael@0 1192 case JitFrame_Exit:
michael@0 1193 MarkJitExitFrame(trc, frames);
michael@0 1194 break;
michael@0 1195 case JitFrame_BaselineJS:
michael@0 1196 frames.baselineFrame()->trace(trc, frames);
michael@0 1197 break;
michael@0 1198 case JitFrame_BaselineStub:
michael@0 1199 MarkBaselineStubFrame(trc, frames);
michael@0 1200 break;
michael@0 1201 case JitFrame_IonJS:
michael@0 1202 MarkIonJSFrame(trc, frames);
michael@0 1203 break;
michael@0 1204 case JitFrame_Unwound_IonJS:
michael@0 1205 MOZ_ASSUME_UNREACHABLE("invalid");
michael@0 1206 case JitFrame_Rectifier:
michael@0 1207 MarkRectifierFrame(trc, frames);
michael@0 1208 break;
michael@0 1209 case JitFrame_Unwound_Rectifier:
michael@0 1210 break;
michael@0 1211 default:
michael@0 1212 MOZ_ASSUME_UNREACHABLE("unexpected frame type");
michael@0 1213 }
michael@0 1214 }
michael@0 1215 }
michael@0 1216
michael@0 1217 void
michael@0 1218 MarkJitActivations(JSRuntime *rt, JSTracer *trc)
michael@0 1219 {
michael@0 1220 for (JitActivationIterator activations(rt); !activations.done(); ++activations)
michael@0 1221 MarkJitActivation(trc, activations);
michael@0 1222 }
michael@0 1223
michael@0 1224 #ifdef JSGC_GENERATIONAL
michael@0 1225 void
michael@0 1226 UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc)
michael@0 1227 {
michael@0 1228 JS_ASSERT(trc->runtime()->isHeapMinorCollecting());
michael@0 1229 for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
michael@0 1230 for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
michael@0 1231 if (frames.type() == JitFrame_IonJS)
michael@0 1232 UpdateIonJSFrameForMinorGC(trc, frames);
michael@0 1233 }
michael@0 1234 }
michael@0 1235 }
michael@0 1236 #endif
michael@0 1237
michael@0 1238 void
michael@0 1239 AutoTempAllocatorRooter::trace(JSTracer *trc)
michael@0 1240 {
michael@0 1241 for (CompilerRootNode *root = temp->rootList(); root != nullptr; root = root->next)
michael@0 1242 gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
michael@0 1243 }
michael@0 1244
michael@0 1245 void
michael@0 1246 GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
michael@0 1247 {
michael@0 1248 IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
michael@0 1249
michael@0 1250 JSRuntime *rt = cx->runtime();
michael@0 1251
michael@0 1252 // Recover the return address.
michael@0 1253 JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution);
michael@0 1254
michael@0 1255 // If the previous frame is a rectifier frame (maybe unwound),
michael@0 1256 // skip past it.
michael@0 1257 if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) {
michael@0 1258 ++it;
michael@0 1259 JS_ASSERT(it.prevType() == JitFrame_BaselineStub ||
michael@0 1260 it.prevType() == JitFrame_BaselineJS ||
michael@0 1261 it.prevType() == JitFrame_IonJS);
michael@0 1262 }
michael@0 1263
michael@0 1264 // If the previous frame is a stub frame, skip the exit frame so that
michael@0 1265 // returnAddress below gets the return address into the BaselineJS
michael@0 1266 // frame.
michael@0 1267 if (it.prevType() == JitFrame_BaselineStub || it.prevType() == JitFrame_Unwound_BaselineStub) {
michael@0 1268 ++it;
michael@0 1269 JS_ASSERT(it.prevType() == JitFrame_BaselineJS);
michael@0 1270 }
michael@0 1271
michael@0 1272 uint8_t *retAddr = it.returnAddress();
michael@0 1273 uint32_t hash = PcScriptCache::Hash(retAddr);
michael@0 1274 JS_ASSERT(retAddr != nullptr);
michael@0 1275
michael@0 1276 // Lazily initialize the cache. The allocation may safely fail and will not GC.
michael@0 1277 if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) {
michael@0 1278 rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
michael@0 1279 if (rt->ionPcScriptCache)
michael@0 1280 rt->ionPcScriptCache->clear(rt->gcNumber);
michael@0 1281 }
michael@0 1282
michael@0 1283 // Attempt to lookup address in cache.
michael@0 1284 if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes))
michael@0 1285 return;
michael@0 1286
michael@0 1287 // Lookup failed: undertake expensive process to recover the innermost inlined frame.
michael@0 1288 ++it; // Skip exit frame.
michael@0 1289 jsbytecode *pc = nullptr;
michael@0 1290
michael@0 1291 if (it.isIonJS()) {
michael@0 1292 InlineFrameIterator ifi(cx, &it);
michael@0 1293 *scriptRes = ifi.script();
michael@0 1294 pc = ifi.pc();
michael@0 1295 } else {
michael@0 1296 JS_ASSERT(it.isBaselineJS());
michael@0 1297 it.baselineScriptAndPc(scriptRes, &pc);
michael@0 1298 }
michael@0 1299
michael@0 1300 if (pcRes)
michael@0 1301 *pcRes = pc;
michael@0 1302
michael@0 1303 // Add entry to cache.
michael@0 1304 if (rt->ionPcScriptCache)
michael@0 1305 rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes);
michael@0 1306 }
michael@0 1307
michael@0 1308 void
michael@0 1309 OsiIndex::fixUpOffset(MacroAssembler &masm)
michael@0 1310 {
michael@0 1311 callPointDisplacement_ = masm.actualOffset(callPointDisplacement_);
michael@0 1312 }
michael@0 1313
michael@0 1314 uint32_t
michael@0 1315 OsiIndex::returnPointDisplacement() const
michael@0 1316 {
michael@0 1317 // In general, pointer arithmetic on code is bad, but in this case,
michael@0 1318 // getting the return address from a call instruction, stepping over pools
michael@0 1319 // would be wrong.
michael@0 1320 return callPointDisplacement_ + Assembler::patchWrite_NearCallSize();
michael@0 1321 }
michael@0 1322
michael@0 1323 SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
michael@0 1324 IonJSFrameLayout *fp, const MachineState &machine)
michael@0 1325 : snapshot_(ionScript->snapshots(),
michael@0 1326 snapshotOffset,
michael@0 1327 ionScript->snapshotsRVATableSize(),
michael@0 1328 ionScript->snapshotsListSize()),
michael@0 1329 recover_(snapshot_,
michael@0 1330 ionScript->recovers(),
michael@0 1331 ionScript->recoversSize()),
michael@0 1332 fp_(fp),
michael@0 1333 machine_(machine),
michael@0 1334 ionScript_(ionScript)
michael@0 1335 {
michael@0 1336 JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
michael@0 1337 }
michael@0 1338
michael@0 1339 SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
michael@0 1340 : snapshot_(iter.ionScript()->snapshots(),
michael@0 1341 iter.osiIndex()->snapshotOffset(),
michael@0 1342 iter.ionScript()->snapshotsRVATableSize(),
michael@0 1343 iter.ionScript()->snapshotsListSize()),
michael@0 1344 recover_(snapshot_,
michael@0 1345 iter.ionScript()->recovers(),
michael@0 1346 iter.ionScript()->recoversSize()),
michael@0 1347 fp_(iter.jsFrame()),
michael@0 1348 machine_(iter.machineState()),
michael@0 1349 ionScript_(iter.ionScript())
michael@0 1350 {
michael@0 1351 }
michael@0 1352
michael@0 1353 SnapshotIterator::SnapshotIterator()
michael@0 1354 : snapshot_(nullptr, 0, 0, 0),
michael@0 1355 recover_(snapshot_, nullptr, 0),
michael@0 1356 fp_(nullptr),
michael@0 1357 ionScript_(nullptr)
michael@0 1358 {
michael@0 1359 }
michael@0 1360
michael@0 1361 uintptr_t
michael@0 1362 SnapshotIterator::fromStack(int32_t offset) const
michael@0 1363 {
michael@0 1364 return ReadFrameSlot(fp_, offset);
michael@0 1365 }
michael@0 1366
michael@0 1367 static Value
michael@0 1368 FromObjectPayload(uintptr_t payload)
michael@0 1369 {
michael@0 1370 return ObjectValue(*reinterpret_cast<JSObject *>(payload));
michael@0 1371 }
michael@0 1372
michael@0 1373 static Value
michael@0 1374 FromStringPayload(uintptr_t payload)
michael@0 1375 {
michael@0 1376 return StringValue(reinterpret_cast<JSString *>(payload));
michael@0 1377 }
michael@0 1378
michael@0 1379 static Value
michael@0 1380 FromTypedPayload(JSValueType type, uintptr_t payload)
michael@0 1381 {
michael@0 1382 switch (type) {
michael@0 1383 case JSVAL_TYPE_INT32:
michael@0 1384 return Int32Value(payload);
michael@0 1385 case JSVAL_TYPE_BOOLEAN:
michael@0 1386 return BooleanValue(!!payload);
michael@0 1387 case JSVAL_TYPE_STRING:
michael@0 1388 return FromStringPayload(payload);
michael@0 1389 case JSVAL_TYPE_OBJECT:
michael@0 1390 return FromObjectPayload(payload);
michael@0 1391 default:
michael@0 1392 MOZ_ASSUME_UNREACHABLE("unexpected type - needs payload");
michael@0 1393 }
michael@0 1394 }
michael@0 1395
michael@0 1396 bool
michael@0 1397 SnapshotIterator::allocationReadable(const RValueAllocation &alloc)
michael@0 1398 {
michael@0 1399 switch (alloc.mode()) {
michael@0 1400 case RValueAllocation::DOUBLE_REG:
michael@0 1401 return hasRegister(alloc.fpuReg());
michael@0 1402
michael@0 1403 case RValueAllocation::TYPED_REG:
michael@0 1404 return hasRegister(alloc.reg2());
michael@0 1405
michael@0 1406 #if defined(JS_NUNBOX32)
michael@0 1407 case RValueAllocation::UNTYPED_REG_REG:
michael@0 1408 return hasRegister(alloc.reg()) && hasRegister(alloc.reg2());
michael@0 1409 case RValueAllocation::UNTYPED_REG_STACK:
michael@0 1410 return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2());
michael@0 1411 case RValueAllocation::UNTYPED_STACK_REG:
michael@0 1412 return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2());
michael@0 1413 case RValueAllocation::UNTYPED_STACK_STACK:
michael@0 1414 return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2());
michael@0 1415 #elif defined(JS_PUNBOX64)
michael@0 1416 case RValueAllocation::UNTYPED_REG:
michael@0 1417 return hasRegister(alloc.reg());
michael@0 1418 case RValueAllocation::UNTYPED_STACK:
michael@0 1419 return hasStack(alloc.stackOffset());
michael@0 1420 #endif
michael@0 1421
michael@0 1422 default:
michael@0 1423 return true;
michael@0 1424 }
michael@0 1425 }
michael@0 1426
michael@0 1427 Value
michael@0 1428 SnapshotIterator::allocationValue(const RValueAllocation &alloc)
michael@0 1429 {
michael@0 1430 switch (alloc.mode()) {
michael@0 1431 case RValueAllocation::CONSTANT:
michael@0 1432 return ionScript_->getConstant(alloc.index());
michael@0 1433
michael@0 1434 case RValueAllocation::CST_UNDEFINED:
michael@0 1435 return UndefinedValue();
michael@0 1436
michael@0 1437 case RValueAllocation::CST_NULL:
michael@0 1438 return NullValue();
michael@0 1439
michael@0 1440 case RValueAllocation::DOUBLE_REG:
michael@0 1441 return DoubleValue(fromRegister(alloc.fpuReg()));
michael@0 1442
michael@0 1443 case RValueAllocation::FLOAT32_REG:
michael@0 1444 {
michael@0 1445 union {
michael@0 1446 double d;
michael@0 1447 float f;
michael@0 1448 } pun;
michael@0 1449 pun.d = fromRegister(alloc.fpuReg());
michael@0 1450 // The register contains the encoding of a float32. We just read
michael@0 1451 // the bits without making any conversion.
michael@0 1452 return Float32Value(pun.f);
michael@0 1453 }
michael@0 1454
michael@0 1455 case RValueAllocation::FLOAT32_STACK:
michael@0 1456 return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset()));
michael@0 1457
michael@0 1458 case RValueAllocation::TYPED_REG:
michael@0 1459 return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2()));
michael@0 1460
michael@0 1461 case RValueAllocation::TYPED_STACK:
michael@0 1462 {
michael@0 1463 switch (alloc.knownType()) {
michael@0 1464 case JSVAL_TYPE_DOUBLE:
michael@0 1465 return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2()));
michael@0 1466 case JSVAL_TYPE_INT32:
michael@0 1467 return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2()));
michael@0 1468 case JSVAL_TYPE_BOOLEAN:
michael@0 1469 return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2()));
michael@0 1470 case JSVAL_TYPE_STRING:
michael@0 1471 return FromStringPayload(fromStack(alloc.stackOffset2()));
michael@0 1472 case JSVAL_TYPE_OBJECT:
michael@0 1473 return FromObjectPayload(fromStack(alloc.stackOffset2()));
michael@0 1474 default:
michael@0 1475 MOZ_ASSUME_UNREACHABLE("Unexpected type");
michael@0 1476 }
michael@0 1477 }
michael@0 1478
michael@0 1479 #if defined(JS_NUNBOX32)
michael@0 1480 case RValueAllocation::UNTYPED_REG_REG:
michael@0 1481 {
michael@0 1482 jsval_layout layout;
michael@0 1483 layout.s.tag = (JSValueTag) fromRegister(alloc.reg());
michael@0 1484 layout.s.payload.word = fromRegister(alloc.reg2());
michael@0 1485 return IMPL_TO_JSVAL(layout);
michael@0 1486 }
michael@0 1487
michael@0 1488 case RValueAllocation::UNTYPED_REG_STACK:
michael@0 1489 {
michael@0 1490 jsval_layout layout;
michael@0 1491 layout.s.tag = (JSValueTag) fromRegister(alloc.reg());
michael@0 1492 layout.s.payload.word = fromStack(alloc.stackOffset2());
michael@0 1493 return IMPL_TO_JSVAL(layout);
michael@0 1494 }
michael@0 1495
michael@0 1496 case RValueAllocation::UNTYPED_STACK_REG:
michael@0 1497 {
michael@0 1498 jsval_layout layout;
michael@0 1499 layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset());
michael@0 1500 layout.s.payload.word = fromRegister(alloc.reg2());
michael@0 1501 return IMPL_TO_JSVAL(layout);
michael@0 1502 }
michael@0 1503
michael@0 1504 case RValueAllocation::UNTYPED_STACK_STACK:
michael@0 1505 {
michael@0 1506 jsval_layout layout;
michael@0 1507 layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset());
michael@0 1508 layout.s.payload.word = fromStack(alloc.stackOffset2());
michael@0 1509 return IMPL_TO_JSVAL(layout);
michael@0 1510 }
michael@0 1511 #elif defined(JS_PUNBOX64)
michael@0 1512 case RValueAllocation::UNTYPED_REG:
michael@0 1513 {
michael@0 1514 jsval_layout layout;
michael@0 1515 layout.asBits = fromRegister(alloc.reg());
michael@0 1516 return IMPL_TO_JSVAL(layout);
michael@0 1517 }
michael@0 1518
michael@0 1519 case RValueAllocation::UNTYPED_STACK:
michael@0 1520 {
michael@0 1521 jsval_layout layout;
michael@0 1522 layout.asBits = fromStack(alloc.stackOffset());
michael@0 1523 return IMPL_TO_JSVAL(layout);
michael@0 1524 }
michael@0 1525 #endif
michael@0 1526
michael@0 1527 default:
michael@0 1528 MOZ_ASSUME_UNREACHABLE("huh?");
michael@0 1529 }
michael@0 1530 }
michael@0 1531
michael@0 1532 const RResumePoint *
michael@0 1533 SnapshotIterator::resumePoint() const
michael@0 1534 {
michael@0 1535 return instruction()->toResumePoint();
michael@0 1536 }
michael@0 1537
michael@0 1538 uint32_t
michael@0 1539 SnapshotIterator::numAllocations() const
michael@0 1540 {
michael@0 1541 return resumePoint()->numOperands();
michael@0 1542 }
michael@0 1543
michael@0 1544 uint32_t
michael@0 1545 SnapshotIterator::pcOffset() const
michael@0 1546 {
michael@0 1547 return resumePoint()->pcOffset();
michael@0 1548 }
michael@0 1549
michael@0 1550 void
michael@0 1551 SnapshotIterator::skipInstruction()
michael@0 1552 {
michael@0 1553 MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
michael@0 1554 size_t numOperands = instruction()->numOperands();
michael@0 1555 for (size_t i = 0; i < numOperands; i++)
michael@0 1556 skip();
michael@0 1557 nextInstruction();
michael@0 1558 }
michael@0 1559
michael@0 1560 void
michael@0 1561 SnapshotIterator::nextFrame()
michael@0 1562 {
michael@0 1563 nextInstruction();
michael@0 1564 while (!instruction()->isResumePoint())
michael@0 1565 skipInstruction();
michael@0 1566 }
michael@0 1567
michael@0 1568 IonScript *
michael@0 1569 JitFrameIterator::ionScript() const
michael@0 1570 {
michael@0 1571 JS_ASSERT(type() == JitFrame_IonJS);
michael@0 1572
michael@0 1573 IonScript *ionScript = nullptr;
michael@0 1574 if (checkInvalidation(&ionScript))
michael@0 1575 return ionScript;
michael@0 1576 switch (GetCalleeTokenTag(calleeToken())) {
michael@0 1577 case CalleeToken_Function:
michael@0 1578 case CalleeToken_Script:
michael@0 1579 return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript();
michael@0 1580 default:
michael@0 1581 MOZ_ASSUME_UNREACHABLE("unknown callee token type");
michael@0 1582 }
michael@0 1583 }
michael@0 1584
michael@0 1585 const SafepointIndex *
michael@0 1586 JitFrameIterator::safepoint() const
michael@0 1587 {
michael@0 1588 if (!cachedSafepointIndex_)
michael@0 1589 cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp());
michael@0 1590 return cachedSafepointIndex_;
michael@0 1591 }
michael@0 1592
michael@0 1593 const OsiIndex *
michael@0 1594 JitFrameIterator::osiIndex() const
michael@0 1595 {
michael@0 1596 SafepointReader reader(ionScript(), safepoint());
michael@0 1597 return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
michael@0 1598 }
michael@0 1599
michael@0 1600 template <AllowGC allowGC>
michael@0 1601 void
michael@0 1602 InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter)
michael@0 1603 {
michael@0 1604 frame_ = iter;
michael@0 1605 framesRead_ = 0;
michael@0 1606 frameCount_ = UINT32_MAX;
michael@0 1607
michael@0 1608 if (iter) {
michael@0 1609 start_ = SnapshotIterator(*iter);
michael@0 1610 findNextFrame();
michael@0 1611 }
michael@0 1612 }
michael@0 1613 template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter);
michael@0 1614 template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter);
michael@0 1615
michael@0 1616 template <AllowGC allowGC>
michael@0 1617 void
michael@0 1618 InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
michael@0 1619 {
michael@0 1620 JS_ASSERT(more());
michael@0 1621
michael@0 1622 si_ = start_;
michael@0 1623
michael@0 1624 // Read the initial frame out of the C stack.
michael@0 1625 callee_ = frame_->maybeCallee();
michael@0 1626 script_ = frame_->script();
michael@0 1627 MOZ_ASSERT(script_->hasBaselineScript());
michael@0 1628
michael@0 1629 // Settle on the outermost frame without evaluating any instructions before
michael@0 1630 // looking for a pc.
michael@0 1631 if (!si_.instruction()->isResumePoint())
michael@0 1632 si_.nextFrame();
michael@0 1633
michael@0 1634 pc_ = script_->offsetToPC(si_.pcOffset());
michael@0 1635 #ifdef DEBUG
michael@0 1636 numActualArgs_ = 0xbadbad;
michael@0 1637 #endif
michael@0 1638
michael@0 1639 // This unfortunately is O(n*m), because we must skip over outer frames
michael@0 1640 // before reading inner ones.
michael@0 1641
michael@0 1642 // The first time (frameCount_ == UINT32_MAX) we do not know the number of
michael@0 1643 // frames that we are going to inspect. So we are iterating until there is
michael@0 1644 // no more frames, to settle on the inner most frame and to count the number
michael@0 1645 // of frames.
michael@0 1646 size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX;
michael@0 1647
michael@0 1648 size_t i = 1;
michael@0 1649 for (; i <= remaining && si_.moreFrames(); i++) {
michael@0 1650 JS_ASSERT(IsIonInlinablePC(pc_));
michael@0 1651
michael@0 1652 // Recover the number of actual arguments from the script.
michael@0 1653 if (JSOp(*pc_) != JSOP_FUNAPPLY)
michael@0 1654 numActualArgs_ = GET_ARGC(pc_);
michael@0 1655 if (JSOp(*pc_) == JSOP_FUNCALL) {
michael@0 1656 JS_ASSERT(GET_ARGC(pc_) > 0);
michael@0 1657 numActualArgs_ = GET_ARGC(pc_) - 1;
michael@0 1658 } else if (IsGetPropPC(pc_)) {
michael@0 1659 numActualArgs_ = 0;
michael@0 1660 } else if (IsSetPropPC(pc_)) {
michael@0 1661 numActualArgs_ = 1;
michael@0 1662 }
michael@0 1663
michael@0 1664 JS_ASSERT(numActualArgs_ != 0xbadbad);
michael@0 1665
michael@0 1666 // Skip over non-argument slots, as well as |this|.
michael@0 1667 unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1;
michael@0 1668 for (unsigned j = 0; j < skipCount; j++)
michael@0 1669 si_.skip();
michael@0 1670
michael@0 1671 // The JSFunction is a constant, otherwise we would not have inlined it.
michael@0 1672 Value funval = si_.read();
michael@0 1673
michael@0 1674 // Skip extra value allocations.
michael@0 1675 while (si_.moreAllocations())
michael@0 1676 si_.skip();
michael@0 1677
michael@0 1678 si_.nextFrame();
michael@0 1679
michael@0 1680 callee_ = &funval.toObject().as<JSFunction>();
michael@0 1681
michael@0 1682 // Inlined functions may be clones that still point to the lazy script
michael@0 1683 // for the executed script, if they are clones. The actual script
michael@0 1684 // exists though, just make sure the function points to it.
michael@0 1685 script_ = callee_->existingScriptForInlinedFunction();
michael@0 1686 MOZ_ASSERT(script_->hasBaselineScript());
michael@0 1687
michael@0 1688 pc_ = script_->offsetToPC(si_.pcOffset());
michael@0 1689 }
michael@0 1690
michael@0 1691 // The first time we do not know the number of frames, we only settle on the
michael@0 1692 // last frame, and update the number of frames based on the number of
michael@0 1693 // iteration that we have done.
michael@0 1694 if (frameCount_ == UINT32_MAX) {
michael@0 1695 MOZ_ASSERT(!si_.moreFrames());
michael@0 1696 frameCount_ = i;
michael@0 1697 }
michael@0 1698
michael@0 1699 framesRead_++;
michael@0 1700 }
michael@0 1701 template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame();
michael@0 1702 template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame();
michael@0 1703
michael@0 1704 template <AllowGC allowGC>
michael@0 1705 bool
michael@0 1706 InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const
michael@0 1707 {
michael@0 1708 return !!callee_;
michael@0 1709 }
michael@0 1710 template bool InlineFrameIteratorMaybeGC<NoGC>::isFunctionFrame() const;
michael@0 1711 template bool InlineFrameIteratorMaybeGC<CanGC>::isFunctionFrame() const;
michael@0 1712
michael@0 1713 MachineState
michael@0 1714 MachineState::FromBailout(mozilla::Array<uintptr_t, Registers::Total> &regs,
michael@0 1715 mozilla::Array<double, FloatRegisters::Total> &fpregs)
michael@0 1716 {
michael@0 1717 MachineState machine;
michael@0 1718
michael@0 1719 for (unsigned i = 0; i < Registers::Total; i++)
michael@0 1720 machine.setRegisterLocation(Register::FromCode(i), &regs[i]);
michael@0 1721 for (unsigned i = 0; i < FloatRegisters::Total; i++)
michael@0 1722 machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]);
michael@0 1723
michael@0 1724 return machine;
michael@0 1725 }
michael@0 1726
michael@0 1727 template <AllowGC allowGC>
michael@0 1728 bool
michael@0 1729 InlineFrameIteratorMaybeGC<allowGC>::isConstructing() const
michael@0 1730 {
michael@0 1731 // Skip the current frame and look at the caller's.
michael@0 1732 if (more()) {
michael@0 1733 InlineFrameIteratorMaybeGC<allowGC> parent(GetJSContextFromJitCode(), this);
michael@0 1734 ++parent;
michael@0 1735
michael@0 1736 // Inlined Getters and Setters are never constructing.
michael@0 1737 if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc()))
michael@0 1738 return false;
michael@0 1739
michael@0 1740 // In the case of a JS frame, look up the pc from the snapshot.
michael@0 1741 JS_ASSERT(IsCallPC(parent.pc()));
michael@0 1742
michael@0 1743 return (JSOp)*parent.pc() == JSOP_NEW;
michael@0 1744 }
michael@0 1745
michael@0 1746 return frame_->isConstructing();
michael@0 1747 }
michael@0 1748 template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const;
michael@0 1749 template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const;
michael@0 1750
michael@0 1751 bool
michael@0 1752 JitFrameIterator::isConstructing() const
michael@0 1753 {
michael@0 1754 JitFrameIterator parent(*this);
michael@0 1755
michael@0 1756 // Skip the current frame and look at the caller's.
michael@0 1757 do {
michael@0 1758 ++parent;
michael@0 1759 } while (!parent.done() && !parent.isScripted());
michael@0 1760
michael@0 1761 if (parent.isIonJS()) {
michael@0 1762 // In the case of a JS frame, look up the pc from the snapshot.
michael@0 1763 InlineFrameIterator inlinedParent(GetJSContextFromJitCode(), &parent);
michael@0 1764
michael@0 1765 //Inlined Getters and Setters are never constructing.
michael@0 1766 if (IsGetPropPC(inlinedParent.pc()) || IsSetPropPC(inlinedParent.pc()))
michael@0 1767 return false;
michael@0 1768
michael@0 1769 JS_ASSERT(IsCallPC(inlinedParent.pc()));
michael@0 1770
michael@0 1771 return (JSOp)*inlinedParent.pc() == JSOP_NEW;
michael@0 1772 }
michael@0 1773
michael@0 1774 if (parent.isBaselineJS()) {
michael@0 1775 jsbytecode *pc;
michael@0 1776 parent.baselineScriptAndPc(nullptr, &pc);
michael@0 1777
michael@0 1778 // Inlined Getters and Setters are never constructing.
michael@0 1779 // Baseline may call getters from [GET|SET]PROP or [GET|SET]ELEM ops.
michael@0 1780 if (IsGetPropPC(pc) || IsSetPropPC(pc) || IsGetElemPC(pc) || IsSetElemPC(pc))
michael@0 1781 return false;
michael@0 1782
michael@0 1783 JS_ASSERT(IsCallPC(pc));
michael@0 1784
michael@0 1785 return JSOp(*pc) == JSOP_NEW;
michael@0 1786 }
michael@0 1787
michael@0 1788 JS_ASSERT(parent.done());
michael@0 1789 return activation_->firstFrameIsConstructing();
michael@0 1790 }
michael@0 1791
michael@0 1792 unsigned
michael@0 1793 JitFrameIterator::numActualArgs() const
michael@0 1794 {
michael@0 1795 if (isScripted())
michael@0 1796 return jsFrame()->numActualArgs();
michael@0 1797
michael@0 1798 JS_ASSERT(isNative());
michael@0 1799 return exitFrame()->nativeExit()->argc();
michael@0 1800 }
michael@0 1801
michael@0 1802 void
michael@0 1803 SnapshotIterator::warnUnreadableAllocation()
michael@0 1804 {
michael@0 1805 fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n");
michael@0 1806 }
michael@0 1807
michael@0 1808 struct DumpOp {
michael@0 1809 DumpOp(unsigned int i) : i_(i) {}
michael@0 1810
michael@0 1811 unsigned int i_;
michael@0 1812 void operator()(const Value& v) {
michael@0 1813 fprintf(stderr, " actual (arg %d): ", i_);
michael@0 1814 #ifdef DEBUG
michael@0 1815 js_DumpValue(v);
michael@0 1816 #else
michael@0 1817 fprintf(stderr, "?\n");
michael@0 1818 #endif
michael@0 1819 i_++;
michael@0 1820 }
michael@0 1821 };
michael@0 1822
michael@0 1823 void
michael@0 1824 JitFrameIterator::dumpBaseline() const
michael@0 1825 {
michael@0 1826 JS_ASSERT(isBaselineJS());
michael@0 1827
michael@0 1828 fprintf(stderr, " JS Baseline frame\n");
michael@0 1829 if (isFunctionFrame()) {
michael@0 1830 fprintf(stderr, " callee fun: ");
michael@0 1831 #ifdef DEBUG
michael@0 1832 js_DumpObject(callee());
michael@0 1833 #else
michael@0 1834 fprintf(stderr, "?\n");
michael@0 1835 #endif
michael@0 1836 } else {
michael@0 1837 fprintf(stderr, " global frame, no callee\n");
michael@0 1838 }
michael@0 1839
michael@0 1840 fprintf(stderr, " file %s line %u\n",
michael@0 1841 script()->filename(), (unsigned) script()->lineno());
michael@0 1842
michael@0 1843 JSContext *cx = GetJSContextFromJitCode();
michael@0 1844 RootedScript script(cx);
michael@0 1845 jsbytecode *pc;
michael@0 1846 baselineScriptAndPc(script.address(), &pc);
michael@0 1847
michael@0 1848 fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void *)script, pc, uint32_t(script->pcToOffset(pc)));
michael@0 1849 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
michael@0 1850
michael@0 1851 fprintf(stderr, " actual args: %d\n", numActualArgs());
michael@0 1852
michael@0 1853 BaselineFrame *frame = baselineFrame();
michael@0 1854
michael@0 1855 for (unsigned i = 0; i < frame->numValueSlots(); i++) {
michael@0 1856 fprintf(stderr, " slot %u: ", i);
michael@0 1857 #ifdef DEBUG
michael@0 1858 Value *v = frame->valueSlot(i);
michael@0 1859 js_DumpValue(*v);
michael@0 1860 #else
michael@0 1861 fprintf(stderr, "?\n");
michael@0 1862 #endif
michael@0 1863 }
michael@0 1864 }
michael@0 1865
michael@0 1866 template <AllowGC allowGC>
michael@0 1867 void
michael@0 1868 InlineFrameIteratorMaybeGC<allowGC>::dump() const
michael@0 1869 {
michael@0 1870 if (more())
michael@0 1871 fprintf(stderr, " JS frame (inlined)\n");
michael@0 1872 else
michael@0 1873 fprintf(stderr, " JS frame\n");
michael@0 1874
michael@0 1875 bool isFunction = false;
michael@0 1876 if (isFunctionFrame()) {
michael@0 1877 isFunction = true;
michael@0 1878 fprintf(stderr, " callee fun: ");
michael@0 1879 #ifdef DEBUG
michael@0 1880 js_DumpObject(callee());
michael@0 1881 #else
michael@0 1882 fprintf(stderr, "?\n");
michael@0 1883 #endif
michael@0 1884 } else {
michael@0 1885 fprintf(stderr, " global frame, no callee\n");
michael@0 1886 }
michael@0 1887
michael@0 1888 fprintf(stderr, " file %s line %u\n",
michael@0 1889 script()->filename(), (unsigned) script()->lineno());
michael@0 1890
michael@0 1891 fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc());
michael@0 1892 fprintf(stderr, " current op: %s\n", js_CodeName[*pc()]);
michael@0 1893
michael@0 1894 if (!more()) {
michael@0 1895 numActualArgs();
michael@0 1896 }
michael@0 1897
michael@0 1898 SnapshotIterator si = snapshotIterator();
michael@0 1899 fprintf(stderr, " slots: %u\n", si.numAllocations() - 1);
michael@0 1900 for (unsigned i = 0; i < si.numAllocations() - 1; i++) {
michael@0 1901 if (isFunction) {
michael@0 1902 if (i == 0)
michael@0 1903 fprintf(stderr, " scope chain: ");
michael@0 1904 else if (i == 1)
michael@0 1905 fprintf(stderr, " this: ");
michael@0 1906 else if (i - 2 < callee()->nargs())
michael@0 1907 fprintf(stderr, " formal (arg %d): ", i - 2);
michael@0 1908 else {
michael@0 1909 if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) {
michael@0 1910 DumpOp d(callee()->nargs());
michael@0 1911 unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown);
michael@0 1912 }
michael@0 1913
michael@0 1914 fprintf(stderr, " slot %d: ", int(i - 2 - callee()->nargs()));
michael@0 1915 }
michael@0 1916 } else
michael@0 1917 fprintf(stderr, " slot %u: ", i);
michael@0 1918 #ifdef DEBUG
michael@0 1919 js_DumpValue(si.maybeRead());
michael@0 1920 #else
michael@0 1921 fprintf(stderr, "?\n");
michael@0 1922 #endif
michael@0 1923 }
michael@0 1924
michael@0 1925 fputc('\n', stderr);
michael@0 1926 }
michael@0 1927 template void InlineFrameIteratorMaybeGC<NoGC>::dump() const;
michael@0 1928 template void InlineFrameIteratorMaybeGC<CanGC>::dump() const;
michael@0 1929
michael@0 1930 void
michael@0 1931 JitFrameIterator::dump() const
michael@0 1932 {
michael@0 1933 switch (type_) {
michael@0 1934 case JitFrame_Entry:
michael@0 1935 fprintf(stderr, " Entry frame\n");
michael@0 1936 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
michael@0 1937 break;
michael@0 1938 case JitFrame_BaselineJS:
michael@0 1939 dumpBaseline();
michael@0 1940 break;
michael@0 1941 case JitFrame_BaselineStub:
michael@0 1942 case JitFrame_Unwound_BaselineStub:
michael@0 1943 fprintf(stderr, " Baseline stub frame\n");
michael@0 1944 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
michael@0 1945 break;
michael@0 1946 case JitFrame_IonJS:
michael@0 1947 {
michael@0 1948 InlineFrameIterator frames(GetJSContextFromJitCode(), this);
michael@0 1949 for (;;) {
michael@0 1950 frames.dump();
michael@0 1951 if (!frames.more())
michael@0 1952 break;
michael@0 1953 ++frames;
michael@0 1954 }
michael@0 1955 break;
michael@0 1956 }
michael@0 1957 case JitFrame_Rectifier:
michael@0 1958 case JitFrame_Unwound_Rectifier:
michael@0 1959 fprintf(stderr, " Rectifier frame\n");
michael@0 1960 fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
michael@0 1961 break;
michael@0 1962 case JitFrame_Unwound_IonJS:
michael@0 1963 fprintf(stderr, "Warning! Unwound JS frames are not observable.\n");
michael@0 1964 break;
michael@0 1965 case JitFrame_Exit:
michael@0 1966 break;
michael@0 1967 };
michael@0 1968 fputc('\n', stderr);
michael@0 1969 }
michael@0 1970
michael@0 1971 IonJSFrameLayout *
michael@0 1972 InvalidationBailoutStack::fp() const
michael@0 1973 {
michael@0 1974 return (IonJSFrameLayout *) (sp() + ionScript_->frameSize());
michael@0 1975 }
michael@0 1976
michael@0 1977 void
michael@0 1978 InvalidationBailoutStack::checkInvariants() const
michael@0 1979 {
michael@0 1980 #ifdef DEBUG
michael@0 1981 IonJSFrameLayout *frame = fp();
michael@0 1982 CalleeToken token = frame->calleeToken();
michael@0 1983 JS_ASSERT(token);
michael@0 1984
michael@0 1985 uint8_t *rawBase = ionScript()->method()->raw();
michael@0 1986 uint8_t *rawLimit = rawBase + ionScript()->method()->instructionsSize();
michael@0 1987 uint8_t *osiPoint = osiPointReturnAddress();
michael@0 1988 JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit);
michael@0 1989 #endif
michael@0 1990 }
michael@0 1991
michael@0 1992 } // namespace jit
michael@0 1993 } // namespace js

mercurial