1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/IonFrames.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1993 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/IonFrames-inl.h" 1.11 + 1.12 +#include "jsfun.h" 1.13 +#include "jsobj.h" 1.14 +#include "jsscript.h" 1.15 + 1.16 +#include "gc/Marking.h" 1.17 +#include "jit/BaselineDebugModeOSR.h" 1.18 +#include "jit/BaselineFrame.h" 1.19 +#include "jit/BaselineIC.h" 1.20 +#include "jit/BaselineJIT.h" 1.21 +#include "jit/Ion.h" 1.22 +#include "jit/IonMacroAssembler.h" 1.23 +#include "jit/IonSpewer.h" 1.24 +#include "jit/JitCompartment.h" 1.25 +#include "jit/ParallelFunctions.h" 1.26 +#include "jit/PcScriptCache.h" 1.27 +#include "jit/Recover.h" 1.28 +#include "jit/Safepoints.h" 1.29 +#include "jit/Snapshots.h" 1.30 +#include "jit/VMFunctions.h" 1.31 +#include "vm/ArgumentsObject.h" 1.32 +#include "vm/ForkJoin.h" 1.33 +#include "vm/Interpreter.h" 1.34 + 1.35 +#include "jsscriptinlines.h" 1.36 +#include "jit/JitFrameIterator-inl.h" 1.37 +#include "vm/Probes-inl.h" 1.38 + 1.39 +namespace js { 1.40 +namespace jit { 1.41 + 1.42 +// Given a slot index, returns the offset, in bytes, of that slot from an 1.43 +// IonJSFrameLayout. Slot distances are uniform across architectures, however, 1.44 +// the distance does depend on the size of the frame header. 1.45 +static inline int32_t 1.46 +OffsetOfFrameSlot(int32_t slot) 1.47 +{ 1.48 + return -slot; 1.49 +} 1.50 + 1.51 +static inline uintptr_t 1.52 +ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot) 1.53 +{ 1.54 + return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot)); 1.55 +} 1.56 + 1.57 +static inline double 1.58 +ReadFrameDoubleSlot(IonJSFrameLayout *fp, int32_t slot) 1.59 +{ 1.60 + return *(double *)((char *)fp + OffsetOfFrameSlot(slot)); 1.61 +} 1.62 + 1.63 +static inline float 1.64 +ReadFrameFloat32Slot(IonJSFrameLayout *fp, int32_t slot) 1.65 +{ 1.66 + return *(float *)((char *)fp + OffsetOfFrameSlot(slot)); 1.67 +} 1.68 + 1.69 +static inline int32_t 1.70 +ReadFrameInt32Slot(IonJSFrameLayout *fp, int32_t slot) 1.71 +{ 1.72 + return *(int32_t *)((char *)fp + OffsetOfFrameSlot(slot)); 1.73 +} 1.74 + 1.75 +static inline bool 1.76 +ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot) 1.77 +{ 1.78 + return *(bool *)((char *)fp + OffsetOfFrameSlot(slot)); 1.79 +} 1.80 + 1.81 +JitFrameIterator::JitFrameIterator(JSContext *cx) 1.82 + : current_(cx->mainThread().ionTop), 1.83 + type_(JitFrame_Exit), 1.84 + returnAddressToFp_(nullptr), 1.85 + frameSize_(0), 1.86 + cachedSafepointIndex_(nullptr), 1.87 + activation_(nullptr), 1.88 + mode_(SequentialExecution) 1.89 +{ 1.90 +} 1.91 + 1.92 +JitFrameIterator::JitFrameIterator(const ActivationIterator &activations) 1.93 + : current_(activations.jitTop()), 1.94 + type_(JitFrame_Exit), 1.95 + returnAddressToFp_(nullptr), 1.96 + frameSize_(0), 1.97 + cachedSafepointIndex_(nullptr), 1.98 + activation_(activations->asJit()), 1.99 + mode_(SequentialExecution) 1.100 +{ 1.101 +} 1.102 + 1.103 +JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode) 1.104 + : current_((uint8_t *)fp), 1.105 + type_(JitFrame_IonJS), 1.106 + returnAddressToFp_(fp->returnAddress()), 1.107 + frameSize_(fp->prevFrameLocalSize()), 1.108 + mode_(mode) 1.109 +{ 1.110 +} 1.111 + 1.112 +bool 1.113 +JitFrameIterator::checkInvalidation() const 1.114 +{ 1.115 + IonScript *dummy; 1.116 + return checkInvalidation(&dummy); 1.117 +} 1.118 + 1.119 +bool 1.120 +JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const 1.121 +{ 1.122 + uint8_t *returnAddr = returnAddressToFp(); 1.123 + JSScript *script = this->script(); 1.124 + // N.B. the current IonScript is not the same as the frame's 1.125 + // IonScript if the frame has since been invalidated. 1.126 + bool invalidated; 1.127 + if (mode_ == ParallelExecution) { 1.128 + // Parallel execution does not have invalidating bailouts. 1.129 + invalidated = false; 1.130 + } else { 1.131 + invalidated = !script->hasIonScript() || 1.132 + !script->ionScript()->containsReturnAddress(returnAddr); 1.133 + } 1.134 + if (!invalidated) 1.135 + return false; 1.136 + 1.137 + int32_t invalidationDataOffset = ((int32_t *) returnAddr)[-1]; 1.138 + uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset; 1.139 + IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset); 1.140 + JS_ASSERT(ionScript->containsReturnAddress(returnAddr)); 1.141 + *ionScriptOut = ionScript; 1.142 + return true; 1.143 +} 1.144 + 1.145 +CalleeToken 1.146 +JitFrameIterator::calleeToken() const 1.147 +{ 1.148 + return ((IonJSFrameLayout *) current_)->calleeToken(); 1.149 +} 1.150 + 1.151 +JSFunction * 1.152 +JitFrameIterator::callee() const 1.153 +{ 1.154 + JS_ASSERT(isScripted()); 1.155 + JS_ASSERT(isFunctionFrame()); 1.156 + return CalleeTokenToFunction(calleeToken()); 1.157 +} 1.158 + 1.159 +JSFunction * 1.160 +JitFrameIterator::maybeCallee() const 1.161 +{ 1.162 + if (isScripted() && (isFunctionFrame())) 1.163 + return callee(); 1.164 + return nullptr; 1.165 +} 1.166 + 1.167 +bool 1.168 +JitFrameIterator::isNative() const 1.169 +{ 1.170 + if (type_ != JitFrame_Exit || isFakeExitFrame()) 1.171 + return false; 1.172 + return exitFrame()->footer()->jitCode() == nullptr; 1.173 +} 1.174 + 1.175 +bool 1.176 +JitFrameIterator::isOOLNative() const 1.177 +{ 1.178 + if (type_ != JitFrame_Exit) 1.179 + return false; 1.180 + return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE; 1.181 +} 1.182 + 1.183 +bool 1.184 +JitFrameIterator::isOOLPropertyOp() const 1.185 +{ 1.186 + if (type_ != JitFrame_Exit) 1.187 + return false; 1.188 + return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP; 1.189 +} 1.190 + 1.191 +bool 1.192 +JitFrameIterator::isOOLProxy() const 1.193 +{ 1.194 + if (type_ != JitFrame_Exit) 1.195 + return false; 1.196 + return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY; 1.197 +} 1.198 + 1.199 +bool 1.200 +JitFrameIterator::isDOMExit() const 1.201 +{ 1.202 + if (type_ != JitFrame_Exit) 1.203 + return false; 1.204 + return exitFrame()->isDomExit(); 1.205 +} 1.206 + 1.207 +bool 1.208 +JitFrameIterator::isFunctionFrame() const 1.209 +{ 1.210 + return CalleeTokenIsFunction(calleeToken()); 1.211 +} 1.212 + 1.213 +JSScript * 1.214 +JitFrameIterator::script() const 1.215 +{ 1.216 + JS_ASSERT(isScripted()); 1.217 + if (isBaselineJS()) 1.218 + return baselineFrame()->script(); 1.219 + JSScript *script = ScriptFromCalleeToken(calleeToken()); 1.220 + JS_ASSERT(script); 1.221 + return script; 1.222 +} 1.223 + 1.224 +void 1.225 +JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const 1.226 +{ 1.227 + JS_ASSERT(isBaselineJS()); 1.228 + JSScript *script = this->script(); 1.229 + if (scriptRes) 1.230 + *scriptRes = script; 1.231 + uint8_t *retAddr = returnAddressToFp(); 1.232 + 1.233 + // If we are in the middle of a recompile handler, get the real return 1.234 + // address as stashed in the RecompileInfo. 1.235 + if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo()) 1.236 + retAddr = info->resumeAddr; 1.237 + 1.238 + if (pcRes) { 1.239 + // If the return address is into the prologue entry address or just 1.240 + // after the debug prologue, then assume start of script. 1.241 + if (retAddr == script->baselineScript()->prologueEntryAddr() || 1.242 + retAddr == script->baselineScript()->postDebugPrologueAddr()) 1.243 + { 1.244 + *pcRes = script->code(); 1.245 + return; 1.246 + } 1.247 + 1.248 + // The return address _may_ be a return from a callVM or IC chain call done for 1.249 + // some op. 1.250 + ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr); 1.251 + if (icEntry) { 1.252 + *pcRes = icEntry->pc(script); 1.253 + return; 1.254 + } 1.255 + 1.256 + // If not, the return address _must_ be the start address of an op, which can 1.257 + // be computed from the pc mapping table. 1.258 + *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr); 1.259 + } 1.260 +} 1.261 + 1.262 +Value * 1.263 +JitFrameIterator::actualArgs() const 1.264 +{ 1.265 + return jsFrame()->argv() + 1; 1.266 +} 1.267 + 1.268 +static inline size_t 1.269 +SizeOfFramePrefix(FrameType type) 1.270 +{ 1.271 + switch (type) { 1.272 + case JitFrame_Entry: 1.273 + return IonEntryFrameLayout::Size(); 1.274 + case JitFrame_BaselineJS: 1.275 + case JitFrame_IonJS: 1.276 + case JitFrame_Unwound_IonJS: 1.277 + return IonJSFrameLayout::Size(); 1.278 + case JitFrame_BaselineStub: 1.279 + return IonBaselineStubFrameLayout::Size(); 1.280 + case JitFrame_Rectifier: 1.281 + return IonRectifierFrameLayout::Size(); 1.282 + case JitFrame_Unwound_Rectifier: 1.283 + return IonUnwoundRectifierFrameLayout::Size(); 1.284 + case JitFrame_Exit: 1.285 + return IonExitFrameLayout::Size(); 1.286 + default: 1.287 + MOZ_ASSUME_UNREACHABLE("unknown frame type"); 1.288 + } 1.289 +} 1.290 + 1.291 +uint8_t * 1.292 +JitFrameIterator::prevFp() const 1.293 +{ 1.294 + size_t currentSize = SizeOfFramePrefix(type_); 1.295 + // This quick fix must be removed as soon as bug 717297 land. This is 1.296 + // needed because the descriptor size of JS-to-JS frame which is just after 1.297 + // a Rectifier frame should not change. (cf EnsureExitFrame function) 1.298 + if (isFakeExitFrame()) { 1.299 + JS_ASSERT(SizeOfFramePrefix(JitFrame_BaselineJS) == 1.300 + SizeOfFramePrefix(JitFrame_IonJS)); 1.301 + currentSize = SizeOfFramePrefix(JitFrame_IonJS); 1.302 + } 1.303 + currentSize += current()->prevFrameLocalSize(); 1.304 + return current_ + currentSize; 1.305 +} 1.306 + 1.307 +JitFrameIterator & 1.308 +JitFrameIterator::operator++() 1.309 +{ 1.310 + JS_ASSERT(type_ != JitFrame_Entry); 1.311 + 1.312 + frameSize_ = prevFrameLocalSize(); 1.313 + cachedSafepointIndex_ = nullptr; 1.314 + 1.315 + // If the next frame is the entry frame, just exit. Don't update current_, 1.316 + // since the entry and first frames overlap. 1.317 + if (current()->prevType() == JitFrame_Entry) { 1.318 + type_ = JitFrame_Entry; 1.319 + return *this; 1.320 + } 1.321 + 1.322 + // Note: prevFp() needs the current type, so set it after computing the 1.323 + // next frame. 1.324 + uint8_t *prev = prevFp(); 1.325 + type_ = current()->prevType(); 1.326 + if (type_ == JitFrame_Unwound_IonJS) 1.327 + type_ = JitFrame_IonJS; 1.328 + else if (type_ == JitFrame_Unwound_BaselineStub) 1.329 + type_ = JitFrame_BaselineStub; 1.330 + returnAddressToFp_ = current()->returnAddress(); 1.331 + current_ = prev; 1.332 + return *this; 1.333 +} 1.334 + 1.335 +uintptr_t * 1.336 +JitFrameIterator::spillBase() const 1.337 +{ 1.338 + // Get the base address to where safepoint registers are spilled. 1.339 + // Out-of-line calls do not unwind the extra padding space used to 1.340 + // aggregate bailout tables, so we use frameSize instead of frameLocals, 1.341 + // which would only account for local stack slots. 1.342 + return reinterpret_cast<uintptr_t *>(fp() - ionScript()->frameSize()); 1.343 +} 1.344 + 1.345 +MachineState 1.346 +JitFrameIterator::machineState() const 1.347 +{ 1.348 + SafepointReader reader(ionScript(), safepoint()); 1.349 + uintptr_t *spill = spillBase(); 1.350 + 1.351 + MachineState machine; 1.352 + for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++) 1.353 + machine.setRegisterLocation(*iter, --spill); 1.354 + 1.355 + double *floatSpill = reinterpret_cast<double *>(spill); 1.356 + for (FloatRegisterBackwardIterator iter(reader.allFloatSpills()); iter.more(); iter++) 1.357 + machine.setRegisterLocation(*iter, --floatSpill); 1.358 + 1.359 + return machine; 1.360 +} 1.361 + 1.362 +static void 1.363 +CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t localSlot) 1.364 +{ 1.365 + SnapshotIterator si = frame.snapshotIterator(); 1.366 + 1.367 + // Skip stack slots until we reach the iterator object. 1.368 + uint32_t base = CountArgSlots(frame.script(), frame.maybeCallee()) + frame.script()->nfixed(); 1.369 + uint32_t skipSlots = base + localSlot - 1; 1.370 + 1.371 + for (unsigned i = 0; i < skipSlots; i++) 1.372 + si.skip(); 1.373 + 1.374 + Value v = si.read(); 1.375 + RootedObject obj(cx, &v.toObject()); 1.376 + 1.377 + if (cx->isExceptionPending()) 1.378 + UnwindIteratorForException(cx, obj); 1.379 + else 1.380 + UnwindIteratorForUncatchableException(cx, obj); 1.381 +} 1.382 + 1.383 +static void 1.384 +HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, 1.385 + bool *overrecursed) 1.386 +{ 1.387 + RootedScript script(cx, frame.script()); 1.388 + jsbytecode *pc = frame.pc(); 1.389 + 1.390 + bool bailedOutForDebugMode = false; 1.391 + if (cx->compartment()->debugMode()) { 1.392 + // If we have an exception from within Ion and the debugger is active, 1.393 + // we do the following: 1.394 + // 1.395 + // 1. Bailout to baseline to reconstruct a baseline frame. 1.396 + // 2. Resume immediately into the exception tail afterwards, and 1.397 + // handle the exception again with the top frame now a baseline 1.398 + // frame. 1.399 + // 1.400 + // An empty exception info denotes that we're propagating an Ion 1.401 + // exception due to debug mode, which BailoutIonToBaseline needs to 1.402 + // know. This is because we might not be able to fully reconstruct up 1.403 + // to the stack depth at the snapshot, as we could've thrown in the 1.404 + // middle of a call. 1.405 + ExceptionBailoutInfo propagateInfo; 1.406 + uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); 1.407 + bailedOutForDebugMode = retval == BAILOUT_RETURN_OK; 1.408 + } 1.409 + 1.410 + if (!script->hasTrynotes()) 1.411 + return; 1.412 + 1.413 + JSTryNote *tn = script->trynotes()->vector; 1.414 + JSTryNote *tnEnd = tn + script->trynotes()->length; 1.415 + 1.416 + uint32_t pcOffset = uint32_t(pc - script->main()); 1.417 + for (; tn != tnEnd; ++tn) { 1.418 + if (pcOffset < tn->start) 1.419 + continue; 1.420 + if (pcOffset >= tn->start + tn->length) 1.421 + continue; 1.422 + 1.423 + switch (tn->kind) { 1.424 + case JSTRY_ITER: { 1.425 + JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER); 1.426 + JS_ASSERT(tn->stackDepth > 0); 1.427 + 1.428 + uint32_t localSlot = tn->stackDepth; 1.429 + CloseLiveIterator(cx, frame, localSlot); 1.430 + break; 1.431 + } 1.432 + 1.433 + case JSTRY_LOOP: 1.434 + break; 1.435 + 1.436 + case JSTRY_CATCH: 1.437 + if (cx->isExceptionPending() && !bailedOutForDebugMode) { 1.438 + // Ion can compile try-catch, but bailing out to catch 1.439 + // exceptions is slow. Reset the use count so that if we 1.440 + // catch many exceptions we won't Ion-compile the script. 1.441 + script->resetUseCount(); 1.442 + 1.443 + // Bailout at the start of the catch block. 1.444 + jsbytecode *catchPC = script->main() + tn->start + tn->length; 1.445 + ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth); 1.446 + uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed); 1.447 + if (retval == BAILOUT_RETURN_OK) 1.448 + return; 1.449 + 1.450 + // Error on bailout clears pending exception. 1.451 + MOZ_ASSERT(!cx->isExceptionPending()); 1.452 + } 1.453 + break; 1.454 + 1.455 + default: 1.456 + MOZ_ASSUME_UNREACHABLE("Unexpected try note"); 1.457 + } 1.458 + } 1.459 +} 1.460 + 1.461 +static void 1.462 +HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe, 1.463 + bool *calledDebugEpilogue) 1.464 +{ 1.465 + JS_ASSERT(frame.isBaselineJS()); 1.466 + JS_ASSERT(!*calledDebugEpilogue); 1.467 + 1.468 + RootedScript script(cx); 1.469 + jsbytecode *pc; 1.470 + frame.baselineScriptAndPc(script.address(), &pc); 1.471 + 1.472 + if (cx->isExceptionPending() && cx->compartment()->debugMode()) { 1.473 + BaselineFrame *baselineFrame = frame.baselineFrame(); 1.474 + JSTrapStatus status = DebugExceptionUnwind(cx, baselineFrame, pc); 1.475 + switch (status) { 1.476 + case JSTRAP_ERROR: 1.477 + // Uncatchable exception. 1.478 + JS_ASSERT(!cx->isExceptionPending()); 1.479 + break; 1.480 + 1.481 + case JSTRAP_CONTINUE: 1.482 + case JSTRAP_THROW: 1.483 + JS_ASSERT(cx->isExceptionPending()); 1.484 + break; 1.485 + 1.486 + case JSTRAP_RETURN: 1.487 + JS_ASSERT(baselineFrame->hasReturnValue()); 1.488 + if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) { 1.489 + rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; 1.490 + rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; 1.491 + rfe->stackPointer = reinterpret_cast<uint8_t *>(baselineFrame); 1.492 + return; 1.493 + } 1.494 + 1.495 + // DebugEpilogue threw an exception. Propagate to the caller frame. 1.496 + *calledDebugEpilogue = true; 1.497 + return; 1.498 + 1.499 + default: 1.500 + MOZ_ASSUME_UNREACHABLE("Invalid trap status"); 1.501 + } 1.502 + } 1.503 + 1.504 + if (!script->hasTrynotes()) 1.505 + return; 1.506 + 1.507 + JSTryNote *tn = script->trynotes()->vector; 1.508 + JSTryNote *tnEnd = tn + script->trynotes()->length; 1.509 + 1.510 + uint32_t pcOffset = uint32_t(pc - script->main()); 1.511 + ScopeIter si(frame.baselineFrame(), pc, cx); 1.512 + for (; tn != tnEnd; ++tn) { 1.513 + if (pcOffset < tn->start) 1.514 + continue; 1.515 + if (pcOffset >= tn->start + tn->length) 1.516 + continue; 1.517 + 1.518 + // Skip if the try note's stack depth exceeds the frame's stack depth. 1.519 + // See the big comment in TryNoteIter::settle for more info. 1.520 + JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed()); 1.521 + size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed(); 1.522 + if (tn->stackDepth > stackDepth) 1.523 + continue; 1.524 + 1.525 + // Unwind scope chain (pop block objects). 1.526 + if (cx->isExceptionPending()) 1.527 + UnwindScope(cx, si, script->main() + tn->start); 1.528 + 1.529 + // Compute base pointer and stack pointer. 1.530 + rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset; 1.531 + rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() - 1.532 + (script->nfixed() + tn->stackDepth) * sizeof(Value); 1.533 + 1.534 + switch (tn->kind) { 1.535 + case JSTRY_CATCH: 1.536 + if (cx->isExceptionPending()) { 1.537 + // Ion can compile try-catch, but bailing out to catch 1.538 + // exceptions is slow. Reset the use count so that if we 1.539 + // catch many exceptions we won't Ion-compile the script. 1.540 + script->resetUseCount(); 1.541 + 1.542 + // Resume at the start of the catch block. 1.543 + rfe->kind = ResumeFromException::RESUME_CATCH; 1.544 + jsbytecode *catchPC = script->main() + tn->start + tn->length; 1.545 + rfe->target = script->baselineScript()->nativeCodeForPC(script, catchPC); 1.546 + return; 1.547 + } 1.548 + break; 1.549 + 1.550 + case JSTRY_FINALLY: 1.551 + if (cx->isExceptionPending()) { 1.552 + rfe->kind = ResumeFromException::RESUME_FINALLY; 1.553 + jsbytecode *finallyPC = script->main() + tn->start + tn->length; 1.554 + rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC); 1.555 + // Drop the exception instead of leaking cross compartment data. 1.556 + if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception))) 1.557 + rfe->exception = UndefinedValue(); 1.558 + cx->clearPendingException(); 1.559 + return; 1.560 + } 1.561 + break; 1.562 + 1.563 + case JSTRY_ITER: { 1.564 + Value iterValue(* (Value *) rfe->stackPointer); 1.565 + RootedObject iterObject(cx, &iterValue.toObject()); 1.566 + if (cx->isExceptionPending()) 1.567 + UnwindIteratorForException(cx, iterObject); 1.568 + else 1.569 + UnwindIteratorForUncatchableException(cx, iterObject); 1.570 + break; 1.571 + } 1.572 + 1.573 + case JSTRY_LOOP: 1.574 + break; 1.575 + 1.576 + default: 1.577 + MOZ_ASSUME_UNREACHABLE("Invalid try note"); 1.578 + } 1.579 + } 1.580 + 1.581 +} 1.582 + 1.583 +struct AutoDeleteDebugModeOSRInfo 1.584 +{ 1.585 + BaselineFrame *frame; 1.586 + AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); } 1.587 + ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); } 1.588 +}; 1.589 + 1.590 +void 1.591 +HandleException(ResumeFromException *rfe) 1.592 +{ 1.593 + JSContext *cx = GetJSContextFromJitCode(); 1.594 + 1.595 + rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; 1.596 + 1.597 + IonSpew(IonSpew_Invalidate, "handling exception"); 1.598 + 1.599 + // Clear any Ion return override that's been set. 1.600 + // This may happen if a callVM function causes an invalidation (setting the 1.601 + // override), and then fails, bypassing the bailout handlers that would 1.602 + // otherwise clear the return override. 1.603 + if (cx->runtime()->hasIonReturnOverride()) 1.604 + cx->runtime()->takeIonReturnOverride(); 1.605 + 1.606 + JitFrameIterator iter(cx); 1.607 + while (!iter.isEntry()) { 1.608 + bool overrecursed = false; 1.609 + if (iter.isIonJS()) { 1.610 + // Search each inlined frame for live iterator objects, and close 1.611 + // them. 1.612 + InlineFrameIterator frames(cx, &iter); 1.613 + 1.614 + // Invalidation state will be the same for all inlined scripts in the frame. 1.615 + IonScript *ionScript = nullptr; 1.616 + bool invalidated = iter.checkInvalidation(&ionScript); 1.617 + 1.618 + for (;;) { 1.619 + HandleExceptionIon(cx, frames, rfe, &overrecursed); 1.620 + 1.621 + if (rfe->kind == ResumeFromException::RESUME_BAILOUT) { 1.622 + if (invalidated) 1.623 + ionScript->decref(cx->runtime()->defaultFreeOp()); 1.624 + return; 1.625 + } 1.626 + 1.627 + JS_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME); 1.628 + 1.629 + // Figure out whether SPS frame was pushed for this frame or not. 1.630 + // Even if profiler is enabled, the frame being popped might have 1.631 + // been entered prior to SPS being enabled, and thus not have 1.632 + // a pushed SPS frame. 1.633 + bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); 1.634 + if (invalidated) 1.635 + popSPSFrame = ionScript->hasSPSInstrumentation(); 1.636 + 1.637 + // If inline-frames are not profiled, then don't pop an SPS frame 1.638 + // for them. 1.639 + if (frames.more() && !js_JitOptions.profileInlineFrames) 1.640 + popSPSFrame = false; 1.641 + 1.642 + // When profiling, each frame popped needs a notification that 1.643 + // the function has exited, so invoke the probe that a function 1.644 + // is exiting. 1.645 + JSScript *script = frames.script(); 1.646 + probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); 1.647 + if (!frames.more()) 1.648 + break; 1.649 + ++frames; 1.650 + } 1.651 + 1.652 + if (invalidated) 1.653 + ionScript->decref(cx->runtime()->defaultFreeOp()); 1.654 + 1.655 + } else if (iter.isBaselineJS()) { 1.656 + // It's invalid to call DebugEpilogue twice for the same frame. 1.657 + bool calledDebugEpilogue = false; 1.658 + 1.659 + HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue); 1.660 + 1.661 + // If we are propagating an exception through a frame with 1.662 + // on-stack recompile info, we should free the allocated 1.663 + // RecompileInfo struct before we leave this block, as we will not 1.664 + // be returning to the recompile handler. 1.665 + // 1.666 + // We cannot delete it immediately because of the call to 1.667 + // iter.baselineScriptAndPc below. 1.668 + AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame()); 1.669 + 1.670 + if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) 1.671 + return; 1.672 + 1.673 + // Unwind profiler pseudo-stack 1.674 + JSScript *script = iter.script(); 1.675 + probes::ExitScript(cx, script, script->functionNonDelazifying(), 1.676 + iter.baselineFrame()->hasPushedSPSFrame()); 1.677 + // After this point, any pushed SPS frame would have been popped if it needed 1.678 + // to be. Unset the flag here so that if we call DebugEpilogue below, 1.679 + // it doesn't try to pop the SPS frame again. 1.680 + iter.baselineFrame()->unsetPushedSPSFrame(); 1.681 + 1.682 + if (cx->compartment()->debugMode() && !calledDebugEpilogue) { 1.683 + // If DebugEpilogue returns |true|, we have to perform a forced 1.684 + // return, e.g. return frame->returnValue() to the caller. 1.685 + BaselineFrame *frame = iter.baselineFrame(); 1.686 + RootedScript script(cx); 1.687 + jsbytecode *pc; 1.688 + iter.baselineScriptAndPc(script.address(), &pc); 1.689 + if (jit::DebugEpilogue(cx, frame, pc, false)) { 1.690 + JS_ASSERT(frame->hasReturnValue()); 1.691 + rfe->kind = ResumeFromException::RESUME_FORCED_RETURN; 1.692 + rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset; 1.693 + rfe->stackPointer = reinterpret_cast<uint8_t *>(frame); 1.694 + return; 1.695 + } 1.696 + } 1.697 + } 1.698 + 1.699 + IonJSFrameLayout *current = iter.isScripted() ? iter.jsFrame() : nullptr; 1.700 + 1.701 + ++iter; 1.702 + 1.703 + if (current) { 1.704 + // Unwind the frame by updating ionTop. This is necessary so that 1.705 + // (1) debugger exception unwind and leave frame hooks don't see this 1.706 + // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does 1.707 + // not crash when accessing an IonScript that's destroyed by the 1.708 + // ionScript->decref call. 1.709 + EnsureExitFrame(current); 1.710 + cx->mainThread().ionTop = (uint8_t *)current; 1.711 + } 1.712 + 1.713 + if (overrecursed) { 1.714 + // We hit an overrecursion error during bailout. Report it now. 1.715 + js_ReportOverRecursed(cx); 1.716 + } 1.717 + } 1.718 + 1.719 + rfe->stackPointer = iter.fp(); 1.720 +} 1.721 + 1.722 +void 1.723 +HandleParallelFailure(ResumeFromException *rfe) 1.724 +{ 1.725 + ForkJoinContext *cx = ForkJoinContext::current(); 1.726 + JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution); 1.727 + 1.728 + parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry"); 1.729 + 1.730 + while (!iter.isEntry()) { 1.731 + if (iter.isScripted()) { 1.732 + cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM, 1.733 + iter.script(), iter.script(), nullptr); 1.734 + break; 1.735 + } 1.736 + ++iter; 1.737 + } 1.738 + 1.739 + while (!iter.isEntry()) { 1.740 + if (iter.isScripted()) 1.741 + PropagateAbortPar(iter.script(), iter.script()); 1.742 + ++iter; 1.743 + } 1.744 + 1.745 + rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; 1.746 + rfe->stackPointer = iter.fp(); 1.747 +} 1.748 + 1.749 +void 1.750 +EnsureExitFrame(IonCommonFrameLayout *frame) 1.751 +{ 1.752 + if (frame->prevType() == JitFrame_Unwound_IonJS || 1.753 + frame->prevType() == JitFrame_Unwound_BaselineStub || 1.754 + frame->prevType() == JitFrame_Unwound_Rectifier) 1.755 + { 1.756 + // Already an exit frame, nothing to do. 1.757 + return; 1.758 + } 1.759 + 1.760 + if (frame->prevType() == JitFrame_Entry) { 1.761 + // The previous frame type is the entry frame, so there's no actual 1.762 + // need for an exit frame. 1.763 + return; 1.764 + } 1.765 + 1.766 + if (frame->prevType() == JitFrame_Rectifier) { 1.767 + // The rectifier code uses the frame descriptor to discard its stack, 1.768 + // so modifying its descriptor size here would be dangerous. Instead, 1.769 + // we change the frame type, and teach the stack walking code how to 1.770 + // deal with this edge case. bug 717297 would obviate the need 1.771 + frame->changePrevType(JitFrame_Unwound_Rectifier); 1.772 + return; 1.773 + } 1.774 + 1.775 + if (frame->prevType() == JitFrame_BaselineStub) { 1.776 + frame->changePrevType(JitFrame_Unwound_BaselineStub); 1.777 + return; 1.778 + } 1.779 + 1.780 + JS_ASSERT(frame->prevType() == JitFrame_IonJS); 1.781 + frame->changePrevType(JitFrame_Unwound_IonJS); 1.782 +} 1.783 + 1.784 +CalleeToken 1.785 +MarkCalleeToken(JSTracer *trc, CalleeToken token) 1.786 +{ 1.787 + switch (GetCalleeTokenTag(token)) { 1.788 + case CalleeToken_Function: 1.789 + { 1.790 + JSFunction *fun = CalleeTokenToFunction(token); 1.791 + MarkObjectRoot(trc, &fun, "ion-callee"); 1.792 + return CalleeToToken(fun); 1.793 + } 1.794 + case CalleeToken_Script: 1.795 + { 1.796 + JSScript *script = CalleeTokenToScript(token); 1.797 + MarkScriptRoot(trc, &script, "ion-entry"); 1.798 + return CalleeToToken(script); 1.799 + } 1.800 + default: 1.801 + MOZ_ASSUME_UNREACHABLE("unknown callee token type"); 1.802 + } 1.803 +} 1.804 + 1.805 +#ifdef JS_NUNBOX32 1.806 +static inline uintptr_t 1.807 +ReadAllocation(const JitFrameIterator &frame, const LAllocation *a) 1.808 +{ 1.809 + if (a->isGeneralReg()) { 1.810 + Register reg = a->toGeneralReg()->reg(); 1.811 + return frame.machineState().read(reg); 1.812 + } 1.813 + if (a->isStackSlot()) { 1.814 + uint32_t slot = a->toStackSlot()->slot(); 1.815 + return *frame.jsFrame()->slotRef(slot); 1.816 + } 1.817 + uint32_t index = a->toArgument()->index(); 1.818 + uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv()); 1.819 + return *reinterpret_cast<uintptr_t *>(argv + index); 1.820 +} 1.821 +#endif 1.822 + 1.823 +static void 1.824 +MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame) 1.825 +{ 1.826 + IonJSFrameLayout *layout = frame.jsFrame(); 1.827 + JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken())); 1.828 + 1.829 + size_t nargs = frame.numActualArgs(); 1.830 + 1.831 + // Trace function arguments. Note + 1 for thisv. 1.832 + Value *argv = layout->argv(); 1.833 + for (size_t i = 0; i < nargs + 1; i++) 1.834 + gc::MarkValueRoot(trc, &argv[i], "ion-argv"); 1.835 +} 1.836 + 1.837 +#ifdef JS_NUNBOX32 1.838 +static inline void 1.839 +WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value) 1.840 +{ 1.841 + if (a->isGeneralReg()) { 1.842 + Register reg = a->toGeneralReg()->reg(); 1.843 + frame.machineState().write(reg, value); 1.844 + return; 1.845 + } 1.846 + if (a->isStackSlot()) { 1.847 + uint32_t slot = a->toStackSlot()->slot(); 1.848 + *frame.jsFrame()->slotRef(slot) = value; 1.849 + return; 1.850 + } 1.851 + uint32_t index = a->toArgument()->index(); 1.852 + uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv()); 1.853 + *reinterpret_cast<uintptr_t *>(argv + index) = value; 1.854 +} 1.855 +#endif 1.856 + 1.857 +static void 1.858 +MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame) 1.859 +{ 1.860 + IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); 1.861 + 1.862 + layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken())); 1.863 + 1.864 + IonScript *ionScript = nullptr; 1.865 + if (frame.checkInvalidation(&ionScript)) { 1.866 + // This frame has been invalidated, meaning that its IonScript is no 1.867 + // longer reachable through the callee token (JSFunction/JSScript->ion 1.868 + // is now nullptr or recompiled). Manually trace it here. 1.869 + IonScript::Trace(trc, ionScript); 1.870 + } else if (CalleeTokenIsFunction(layout->calleeToken())) { 1.871 + ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); 1.872 + } else { 1.873 + ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); 1.874 + } 1.875 + 1.876 + if (CalleeTokenIsFunction(layout->calleeToken())) 1.877 + MarkActualArguments(trc, frame); 1.878 + 1.879 + const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); 1.880 + 1.881 + SafepointReader safepoint(ionScript, si); 1.882 + 1.883 + // Scan through slots which contain pointers (or on punboxing systems, 1.884 + // actual values). 1.885 + uint32_t slot; 1.886 + while (safepoint.getGcSlot(&slot)) { 1.887 + uintptr_t *ref = layout->slotRef(slot); 1.888 + gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot"); 1.889 + } 1.890 + 1.891 + while (safepoint.getValueSlot(&slot)) { 1.892 + Value *v = (Value *)layout->slotRef(slot); 1.893 + gc::MarkValueRoot(trc, v, "ion-gc-slot"); 1.894 + } 1.895 + 1.896 + uintptr_t *spill = frame.spillBase(); 1.897 + GeneralRegisterSet gcRegs = safepoint.gcSpills(); 1.898 + GeneralRegisterSet valueRegs = safepoint.valueSpills(); 1.899 + for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { 1.900 + --spill; 1.901 + if (gcRegs.has(*iter)) 1.902 + gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(spill), "ion-gc-spill"); 1.903 + else if (valueRegs.has(*iter)) 1.904 + gc::MarkValueRoot(trc, reinterpret_cast<Value *>(spill), "ion-value-spill"); 1.905 + } 1.906 + 1.907 +#ifdef JS_NUNBOX32 1.908 + LAllocation type, payload; 1.909 + while (safepoint.getNunboxSlot(&type, &payload)) { 1.910 + jsval_layout layout; 1.911 + layout.s.tag = (JSValueTag)ReadAllocation(frame, &type); 1.912 + layout.s.payload.uintptr = ReadAllocation(frame, &payload); 1.913 + 1.914 + Value v = IMPL_TO_JSVAL(layout); 1.915 + gc::MarkValueRoot(trc, &v, "ion-torn-value"); 1.916 + 1.917 + if (v != IMPL_TO_JSVAL(layout)) { 1.918 + // GC moved the value, replace the stored payload. 1.919 + layout = JSVAL_TO_IMPL(v); 1.920 + WriteAllocation(frame, &payload, layout.s.payload.uintptr); 1.921 + } 1.922 + } 1.923 +#endif 1.924 +} 1.925 + 1.926 +#ifdef JSGC_GENERATIONAL 1.927 +static void 1.928 +UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame) 1.929 +{ 1.930 + // Minor GCs may move slots/elements allocated in the nursery. Update 1.931 + // any slots/elements pointers stored in this frame. 1.932 + 1.933 + IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); 1.934 + 1.935 + IonScript *ionScript = nullptr; 1.936 + if (frame.checkInvalidation(&ionScript)) { 1.937 + // This frame has been invalidated, meaning that its IonScript is no 1.938 + // longer reachable through the callee token (JSFunction/JSScript->ion 1.939 + // is now nullptr or recompiled). 1.940 + } else if (CalleeTokenIsFunction(layout->calleeToken())) { 1.941 + ionScript = CalleeTokenToFunction(layout->calleeToken())->nonLazyScript()->ionScript(); 1.942 + } else { 1.943 + ionScript = CalleeTokenToScript(layout->calleeToken())->ionScript(); 1.944 + } 1.945 + 1.946 + const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); 1.947 + SafepointReader safepoint(ionScript, si); 1.948 + 1.949 + GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills(); 1.950 + uintptr_t *spill = frame.spillBase(); 1.951 + for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) { 1.952 + --spill; 1.953 + if (slotsRegs.has(*iter)) 1.954 + trc->runtime()->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill)); 1.955 + } 1.956 + 1.957 + // Skip to the right place in the safepoint 1.958 + uint32_t slot; 1.959 + while (safepoint.getGcSlot(&slot)); 1.960 + while (safepoint.getValueSlot(&slot)); 1.961 +#ifdef JS_NUNBOX32 1.962 + LAllocation type, payload; 1.963 + while (safepoint.getNunboxSlot(&type, &payload)); 1.964 +#endif 1.965 + 1.966 + while (safepoint.getSlotsOrElementsSlot(&slot)) { 1.967 + HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot)); 1.968 + trc->runtime()->gcNursery.forwardBufferPointer(slots); 1.969 + } 1.970 +} 1.971 +#endif 1.972 + 1.973 +static void 1.974 +MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame) 1.975 +{ 1.976 + // Mark the ICStub pointer stored in the stub frame. This is necessary 1.977 + // so that we don't destroy the stub code after unlinking the stub. 1.978 + 1.979 + JS_ASSERT(frame.type() == JitFrame_BaselineStub); 1.980 + IonBaselineStubFrameLayout *layout = (IonBaselineStubFrameLayout *)frame.fp(); 1.981 + 1.982 + if (ICStub *stub = layout->maybeStubPtr()) { 1.983 + JS_ASSERT(ICStub::CanMakeCalls(stub->kind())); 1.984 + stub->trace(trc); 1.985 + } 1.986 +} 1.987 + 1.988 +void 1.989 +JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end) 1.990 +{ 1.991 + JitFrameIterator frames(jitTop(), SequentialExecution); 1.992 + 1.993 + if (frames.isFakeExitFrame()) { 1.994 + min = reinterpret_cast<uintptr_t *>(frames.fp()); 1.995 + } else { 1.996 + IonExitFrameLayout *exitFrame = frames.exitFrame(); 1.997 + IonExitFooterFrame *footer = exitFrame->footer(); 1.998 + const VMFunction *f = footer->function(); 1.999 + if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) { 1.1000 + switch (f->outParamRootType) { 1.1001 + case VMFunction::RootNone: 1.1002 + MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); 1.1003 + case VMFunction::RootObject: 1.1004 + case VMFunction::RootString: 1.1005 + case VMFunction::RootPropertyName: 1.1006 + case VMFunction::RootFunction: 1.1007 + case VMFunction::RootCell: 1.1008 + // These are all handles to GCThing pointers. 1.1009 + min = reinterpret_cast<uintptr_t *>(footer->outParam<void *>()); 1.1010 + break; 1.1011 + case VMFunction::RootValue: 1.1012 + min = reinterpret_cast<uintptr_t *>(footer->outParam<Value>()); 1.1013 + break; 1.1014 + } 1.1015 + } else { 1.1016 + min = reinterpret_cast<uintptr_t *>(footer); 1.1017 + } 1.1018 + } 1.1019 + 1.1020 + while (!frames.done()) 1.1021 + ++frames; 1.1022 + 1.1023 + end = reinterpret_cast<uintptr_t *>(frames.prevFp()); 1.1024 +} 1.1025 + 1.1026 +static void 1.1027 +MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame) 1.1028 +{ 1.1029 + // Ignore fake exit frames created by EnsureExitFrame. 1.1030 + if (frame.isFakeExitFrame()) 1.1031 + return; 1.1032 + 1.1033 + IonExitFooterFrame *footer = frame.exitFrame()->footer(); 1.1034 + 1.1035 + // Mark the code of the code handling the exit path. This is needed because 1.1036 + // invalidated script are no longer marked because data are erased by the 1.1037 + // invalidation and relocation data are no longer reliable. So the VM 1.1038 + // wrapper or the invalidation code may be GC if no JitCode keep reference 1.1039 + // on them. 1.1040 + JS_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1)); 1.1041 + 1.1042 + // This correspond to the case where we have build a fake exit frame in 1.1043 + // CodeGenerator.cpp which handle the case of a native function call. We 1.1044 + // need to mark the argument vector of the function call. 1.1045 + if (frame.isNative()) { 1.1046 + IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit(); 1.1047 + size_t len = native->argc() + 2; 1.1048 + Value *vp = native->vp(); 1.1049 + gc::MarkValueRootRange(trc, len, vp, "ion-native-args"); 1.1050 + return; 1.1051 + } 1.1052 + 1.1053 + if (frame.isOOLNative()) { 1.1054 + IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->oolNativeExit(); 1.1055 + gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code"); 1.1056 + gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp"); 1.1057 + size_t len = oolnative->argc() + 1; 1.1058 + gc::MarkValueRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs"); 1.1059 + return; 1.1060 + } 1.1061 + 1.1062 + if (frame.isOOLPropertyOp()) { 1.1063 + IonOOLPropertyOpExitFrameLayout *oolgetter = frame.exitFrame()->oolPropertyOpExit(); 1.1064 + gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code"); 1.1065 + gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp"); 1.1066 + gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id"); 1.1067 + gc::MarkObjectRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj"); 1.1068 + return; 1.1069 + } 1.1070 + 1.1071 + if (frame.isOOLProxy()) { 1.1072 + IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->oolProxyExit(); 1.1073 + gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code"); 1.1074 + gc::MarkValueRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp"); 1.1075 + gc::MarkIdRoot(trc, oolproxy->id(), "ion-ool-proxy-id"); 1.1076 + gc::MarkObjectRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy"); 1.1077 + gc::MarkObjectRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver"); 1.1078 + return; 1.1079 + } 1.1080 + 1.1081 + if (frame.isDOMExit()) { 1.1082 + IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit(); 1.1083 + gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args"); 1.1084 + if (dom->isMethodFrame()) { 1.1085 + IonDOMMethodExitFrameLayout *method = 1.1086 + reinterpret_cast<IonDOMMethodExitFrameLayout *>(dom); 1.1087 + size_t len = method->argc() + 2; 1.1088 + Value *vp = method->vp(); 1.1089 + gc::MarkValueRootRange(trc, len, vp, "ion-dom-args"); 1.1090 + } else { 1.1091 + gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args"); 1.1092 + } 1.1093 + return; 1.1094 + } 1.1095 + 1.1096 + MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code"); 1.1097 + 1.1098 + const VMFunction *f = footer->function(); 1.1099 + if (f == nullptr) 1.1100 + return; 1.1101 + 1.1102 + // Mark arguments of the VM wrapper. 1.1103 + uint8_t *argBase = frame.exitFrame()->argBase(); 1.1104 + for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) { 1.1105 + switch (f->argRootType(explicitArg)) { 1.1106 + case VMFunction::RootNone: 1.1107 + break; 1.1108 + case VMFunction::RootObject: { 1.1109 + // Sometimes we can bake in HandleObjects to nullptr. 1.1110 + JSObject **pobj = reinterpret_cast<JSObject **>(argBase); 1.1111 + if (*pobj) 1.1112 + gc::MarkObjectRoot(trc, pobj, "ion-vm-args"); 1.1113 + break; 1.1114 + } 1.1115 + case VMFunction::RootString: 1.1116 + case VMFunction::RootPropertyName: 1.1117 + gc::MarkStringRoot(trc, reinterpret_cast<JSString**>(argBase), "ion-vm-args"); 1.1118 + break; 1.1119 + case VMFunction::RootFunction: 1.1120 + gc::MarkObjectRoot(trc, reinterpret_cast<JSFunction**>(argBase), "ion-vm-args"); 1.1121 + break; 1.1122 + case VMFunction::RootValue: 1.1123 + gc::MarkValueRoot(trc, reinterpret_cast<Value*>(argBase), "ion-vm-args"); 1.1124 + break; 1.1125 + case VMFunction::RootCell: 1.1126 + gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(argBase), "ion-vm-args"); 1.1127 + break; 1.1128 + } 1.1129 + 1.1130 + switch (f->argProperties(explicitArg)) { 1.1131 + case VMFunction::WordByValue: 1.1132 + case VMFunction::WordByRef: 1.1133 + argBase += sizeof(void *); 1.1134 + break; 1.1135 + case VMFunction::DoubleByValue: 1.1136 + case VMFunction::DoubleByRef: 1.1137 + argBase += 2 * sizeof(void *); 1.1138 + break; 1.1139 + } 1.1140 + } 1.1141 + 1.1142 + if (f->outParam == Type_Handle) { 1.1143 + switch (f->outParamRootType) { 1.1144 + case VMFunction::RootNone: 1.1145 + MOZ_ASSUME_UNREACHABLE("Handle outparam must have root type"); 1.1146 + case VMFunction::RootObject: 1.1147 + gc::MarkObjectRoot(trc, footer->outParam<JSObject *>(), "ion-vm-out"); 1.1148 + break; 1.1149 + case VMFunction::RootString: 1.1150 + case VMFunction::RootPropertyName: 1.1151 + gc::MarkStringRoot(trc, footer->outParam<JSString *>(), "ion-vm-out"); 1.1152 + break; 1.1153 + case VMFunction::RootFunction: 1.1154 + gc::MarkObjectRoot(trc, footer->outParam<JSFunction *>(), "ion-vm-out"); 1.1155 + break; 1.1156 + case VMFunction::RootValue: 1.1157 + gc::MarkValueRoot(trc, footer->outParam<Value>(), "ion-vm-outvp"); 1.1158 + break; 1.1159 + case VMFunction::RootCell: 1.1160 + gc::MarkGCThingRoot(trc, footer->outParam<void *>(), "ion-vm-out"); 1.1161 + break; 1.1162 + } 1.1163 + } 1.1164 +} 1.1165 + 1.1166 +static void 1.1167 +MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame) 1.1168 +{ 1.1169 + // Mark thisv. 1.1170 + // 1.1171 + // Baseline JIT code generated as part of the ICCall_Fallback stub may use 1.1172 + // it if we're calling a constructor that returns a primitive value. 1.1173 + IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp(); 1.1174 + gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv"); 1.1175 +} 1.1176 + 1.1177 +static void 1.1178 +MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations) 1.1179 +{ 1.1180 + JitActivation *activation = activations->asJit(); 1.1181 + 1.1182 +#ifdef CHECK_OSIPOINT_REGISTERS 1.1183 + if (js_JitOptions.checkOsiPointRegisters) { 1.1184 + // GC can modify spilled registers, breaking our register checks. 1.1185 + // To handle this, we disable these checks for the current VM call 1.1186 + // when a GC happens. 1.1187 + activation->setCheckRegs(false); 1.1188 + } 1.1189 +#endif 1.1190 + 1.1191 + activation->markRematerializedFrames(trc); 1.1192 + 1.1193 + for (JitFrameIterator frames(activations); !frames.done(); ++frames) { 1.1194 + switch (frames.type()) { 1.1195 + case JitFrame_Exit: 1.1196 + MarkJitExitFrame(trc, frames); 1.1197 + break; 1.1198 + case JitFrame_BaselineJS: 1.1199 + frames.baselineFrame()->trace(trc, frames); 1.1200 + break; 1.1201 + case JitFrame_BaselineStub: 1.1202 + MarkBaselineStubFrame(trc, frames); 1.1203 + break; 1.1204 + case JitFrame_IonJS: 1.1205 + MarkIonJSFrame(trc, frames); 1.1206 + break; 1.1207 + case JitFrame_Unwound_IonJS: 1.1208 + MOZ_ASSUME_UNREACHABLE("invalid"); 1.1209 + case JitFrame_Rectifier: 1.1210 + MarkRectifierFrame(trc, frames); 1.1211 + break; 1.1212 + case JitFrame_Unwound_Rectifier: 1.1213 + break; 1.1214 + default: 1.1215 + MOZ_ASSUME_UNREACHABLE("unexpected frame type"); 1.1216 + } 1.1217 + } 1.1218 +} 1.1219 + 1.1220 +void 1.1221 +MarkJitActivations(JSRuntime *rt, JSTracer *trc) 1.1222 +{ 1.1223 + for (JitActivationIterator activations(rt); !activations.done(); ++activations) 1.1224 + MarkJitActivation(trc, activations); 1.1225 +} 1.1226 + 1.1227 +#ifdef JSGC_GENERATIONAL 1.1228 +void 1.1229 +UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc) 1.1230 +{ 1.1231 + JS_ASSERT(trc->runtime()->isHeapMinorCollecting()); 1.1232 + for (JitActivationIterator activations(rt); !activations.done(); ++activations) { 1.1233 + for (JitFrameIterator frames(activations); !frames.done(); ++frames) { 1.1234 + if (frames.type() == JitFrame_IonJS) 1.1235 + UpdateIonJSFrameForMinorGC(trc, frames); 1.1236 + } 1.1237 + } 1.1238 +} 1.1239 +#endif 1.1240 + 1.1241 +void 1.1242 +AutoTempAllocatorRooter::trace(JSTracer *trc) 1.1243 +{ 1.1244 + for (CompilerRootNode *root = temp->rootList(); root != nullptr; root = root->next) 1.1245 + gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root"); 1.1246 +} 1.1247 + 1.1248 +void 1.1249 +GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes) 1.1250 +{ 1.1251 + IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame."); 1.1252 + 1.1253 + JSRuntime *rt = cx->runtime(); 1.1254 + 1.1255 + // Recover the return address. 1.1256 + JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution); 1.1257 + 1.1258 + // If the previous frame is a rectifier frame (maybe unwound), 1.1259 + // skip past it. 1.1260 + if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) { 1.1261 + ++it; 1.1262 + JS_ASSERT(it.prevType() == JitFrame_BaselineStub || 1.1263 + it.prevType() == JitFrame_BaselineJS || 1.1264 + it.prevType() == JitFrame_IonJS); 1.1265 + } 1.1266 + 1.1267 + // If the previous frame is a stub frame, skip the exit frame so that 1.1268 + // returnAddress below gets the return address into the BaselineJS 1.1269 + // frame. 1.1270 + if (it.prevType() == JitFrame_BaselineStub || it.prevType() == JitFrame_Unwound_BaselineStub) { 1.1271 + ++it; 1.1272 + JS_ASSERT(it.prevType() == JitFrame_BaselineJS); 1.1273 + } 1.1274 + 1.1275 + uint8_t *retAddr = it.returnAddress(); 1.1276 + uint32_t hash = PcScriptCache::Hash(retAddr); 1.1277 + JS_ASSERT(retAddr != nullptr); 1.1278 + 1.1279 + // Lazily initialize the cache. The allocation may safely fail and will not GC. 1.1280 + if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) { 1.1281 + rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache)); 1.1282 + if (rt->ionPcScriptCache) 1.1283 + rt->ionPcScriptCache->clear(rt->gcNumber); 1.1284 + } 1.1285 + 1.1286 + // Attempt to lookup address in cache. 1.1287 + if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes)) 1.1288 + return; 1.1289 + 1.1290 + // Lookup failed: undertake expensive process to recover the innermost inlined frame. 1.1291 + ++it; // Skip exit frame. 1.1292 + jsbytecode *pc = nullptr; 1.1293 + 1.1294 + if (it.isIonJS()) { 1.1295 + InlineFrameIterator ifi(cx, &it); 1.1296 + *scriptRes = ifi.script(); 1.1297 + pc = ifi.pc(); 1.1298 + } else { 1.1299 + JS_ASSERT(it.isBaselineJS()); 1.1300 + it.baselineScriptAndPc(scriptRes, &pc); 1.1301 + } 1.1302 + 1.1303 + if (pcRes) 1.1304 + *pcRes = pc; 1.1305 + 1.1306 + // Add entry to cache. 1.1307 + if (rt->ionPcScriptCache) 1.1308 + rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes); 1.1309 +} 1.1310 + 1.1311 +void 1.1312 +OsiIndex::fixUpOffset(MacroAssembler &masm) 1.1313 +{ 1.1314 + callPointDisplacement_ = masm.actualOffset(callPointDisplacement_); 1.1315 +} 1.1316 + 1.1317 +uint32_t 1.1318 +OsiIndex::returnPointDisplacement() const 1.1319 +{ 1.1320 + // In general, pointer arithmetic on code is bad, but in this case, 1.1321 + // getting the return address from a call instruction, stepping over pools 1.1322 + // would be wrong. 1.1323 + return callPointDisplacement_ + Assembler::patchWrite_NearCallSize(); 1.1324 +} 1.1325 + 1.1326 +SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset, 1.1327 + IonJSFrameLayout *fp, const MachineState &machine) 1.1328 + : snapshot_(ionScript->snapshots(), 1.1329 + snapshotOffset, 1.1330 + ionScript->snapshotsRVATableSize(), 1.1331 + ionScript->snapshotsListSize()), 1.1332 + recover_(snapshot_, 1.1333 + ionScript->recovers(), 1.1334 + ionScript->recoversSize()), 1.1335 + fp_(fp), 1.1336 + machine_(machine), 1.1337 + ionScript_(ionScript) 1.1338 +{ 1.1339 + JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize()); 1.1340 +} 1.1341 + 1.1342 +SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter) 1.1343 + : snapshot_(iter.ionScript()->snapshots(), 1.1344 + iter.osiIndex()->snapshotOffset(), 1.1345 + iter.ionScript()->snapshotsRVATableSize(), 1.1346 + iter.ionScript()->snapshotsListSize()), 1.1347 + recover_(snapshot_, 1.1348 + iter.ionScript()->recovers(), 1.1349 + iter.ionScript()->recoversSize()), 1.1350 + fp_(iter.jsFrame()), 1.1351 + machine_(iter.machineState()), 1.1352 + ionScript_(iter.ionScript()) 1.1353 +{ 1.1354 +} 1.1355 + 1.1356 +SnapshotIterator::SnapshotIterator() 1.1357 + : snapshot_(nullptr, 0, 0, 0), 1.1358 + recover_(snapshot_, nullptr, 0), 1.1359 + fp_(nullptr), 1.1360 + ionScript_(nullptr) 1.1361 +{ 1.1362 +} 1.1363 + 1.1364 +uintptr_t 1.1365 +SnapshotIterator::fromStack(int32_t offset) const 1.1366 +{ 1.1367 + return ReadFrameSlot(fp_, offset); 1.1368 +} 1.1369 + 1.1370 +static Value 1.1371 +FromObjectPayload(uintptr_t payload) 1.1372 +{ 1.1373 + return ObjectValue(*reinterpret_cast<JSObject *>(payload)); 1.1374 +} 1.1375 + 1.1376 +static Value 1.1377 +FromStringPayload(uintptr_t payload) 1.1378 +{ 1.1379 + return StringValue(reinterpret_cast<JSString *>(payload)); 1.1380 +} 1.1381 + 1.1382 +static Value 1.1383 +FromTypedPayload(JSValueType type, uintptr_t payload) 1.1384 +{ 1.1385 + switch (type) { 1.1386 + case JSVAL_TYPE_INT32: 1.1387 + return Int32Value(payload); 1.1388 + case JSVAL_TYPE_BOOLEAN: 1.1389 + return BooleanValue(!!payload); 1.1390 + case JSVAL_TYPE_STRING: 1.1391 + return FromStringPayload(payload); 1.1392 + case JSVAL_TYPE_OBJECT: 1.1393 + return FromObjectPayload(payload); 1.1394 + default: 1.1395 + MOZ_ASSUME_UNREACHABLE("unexpected type - needs payload"); 1.1396 + } 1.1397 +} 1.1398 + 1.1399 +bool 1.1400 +SnapshotIterator::allocationReadable(const RValueAllocation &alloc) 1.1401 +{ 1.1402 + switch (alloc.mode()) { 1.1403 + case RValueAllocation::DOUBLE_REG: 1.1404 + return hasRegister(alloc.fpuReg()); 1.1405 + 1.1406 + case RValueAllocation::TYPED_REG: 1.1407 + return hasRegister(alloc.reg2()); 1.1408 + 1.1409 +#if defined(JS_NUNBOX32) 1.1410 + case RValueAllocation::UNTYPED_REG_REG: 1.1411 + return hasRegister(alloc.reg()) && hasRegister(alloc.reg2()); 1.1412 + case RValueAllocation::UNTYPED_REG_STACK: 1.1413 + return hasRegister(alloc.reg()) && hasStack(alloc.stackOffset2()); 1.1414 + case RValueAllocation::UNTYPED_STACK_REG: 1.1415 + return hasStack(alloc.stackOffset()) && hasRegister(alloc.reg2()); 1.1416 + case RValueAllocation::UNTYPED_STACK_STACK: 1.1417 + return hasStack(alloc.stackOffset()) && hasStack(alloc.stackOffset2()); 1.1418 +#elif defined(JS_PUNBOX64) 1.1419 + case RValueAllocation::UNTYPED_REG: 1.1420 + return hasRegister(alloc.reg()); 1.1421 + case RValueAllocation::UNTYPED_STACK: 1.1422 + return hasStack(alloc.stackOffset()); 1.1423 +#endif 1.1424 + 1.1425 + default: 1.1426 + return true; 1.1427 + } 1.1428 +} 1.1429 + 1.1430 +Value 1.1431 +SnapshotIterator::allocationValue(const RValueAllocation &alloc) 1.1432 +{ 1.1433 + switch (alloc.mode()) { 1.1434 + case RValueAllocation::CONSTANT: 1.1435 + return ionScript_->getConstant(alloc.index()); 1.1436 + 1.1437 + case RValueAllocation::CST_UNDEFINED: 1.1438 + return UndefinedValue(); 1.1439 + 1.1440 + case RValueAllocation::CST_NULL: 1.1441 + return NullValue(); 1.1442 + 1.1443 + case RValueAllocation::DOUBLE_REG: 1.1444 + return DoubleValue(fromRegister(alloc.fpuReg())); 1.1445 + 1.1446 + case RValueAllocation::FLOAT32_REG: 1.1447 + { 1.1448 + union { 1.1449 + double d; 1.1450 + float f; 1.1451 + } pun; 1.1452 + pun.d = fromRegister(alloc.fpuReg()); 1.1453 + // The register contains the encoding of a float32. We just read 1.1454 + // the bits without making any conversion. 1.1455 + return Float32Value(pun.f); 1.1456 + } 1.1457 + 1.1458 + case RValueAllocation::FLOAT32_STACK: 1.1459 + return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset())); 1.1460 + 1.1461 + case RValueAllocation::TYPED_REG: 1.1462 + return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2())); 1.1463 + 1.1464 + case RValueAllocation::TYPED_STACK: 1.1465 + { 1.1466 + switch (alloc.knownType()) { 1.1467 + case JSVAL_TYPE_DOUBLE: 1.1468 + return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2())); 1.1469 + case JSVAL_TYPE_INT32: 1.1470 + return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2())); 1.1471 + case JSVAL_TYPE_BOOLEAN: 1.1472 + return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2())); 1.1473 + case JSVAL_TYPE_STRING: 1.1474 + return FromStringPayload(fromStack(alloc.stackOffset2())); 1.1475 + case JSVAL_TYPE_OBJECT: 1.1476 + return FromObjectPayload(fromStack(alloc.stackOffset2())); 1.1477 + default: 1.1478 + MOZ_ASSUME_UNREACHABLE("Unexpected type"); 1.1479 + } 1.1480 + } 1.1481 + 1.1482 +#if defined(JS_NUNBOX32) 1.1483 + case RValueAllocation::UNTYPED_REG_REG: 1.1484 + { 1.1485 + jsval_layout layout; 1.1486 + layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); 1.1487 + layout.s.payload.word = fromRegister(alloc.reg2()); 1.1488 + return IMPL_TO_JSVAL(layout); 1.1489 + } 1.1490 + 1.1491 + case RValueAllocation::UNTYPED_REG_STACK: 1.1492 + { 1.1493 + jsval_layout layout; 1.1494 + layout.s.tag = (JSValueTag) fromRegister(alloc.reg()); 1.1495 + layout.s.payload.word = fromStack(alloc.stackOffset2()); 1.1496 + return IMPL_TO_JSVAL(layout); 1.1497 + } 1.1498 + 1.1499 + case RValueAllocation::UNTYPED_STACK_REG: 1.1500 + { 1.1501 + jsval_layout layout; 1.1502 + layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); 1.1503 + layout.s.payload.word = fromRegister(alloc.reg2()); 1.1504 + return IMPL_TO_JSVAL(layout); 1.1505 + } 1.1506 + 1.1507 + case RValueAllocation::UNTYPED_STACK_STACK: 1.1508 + { 1.1509 + jsval_layout layout; 1.1510 + layout.s.tag = (JSValueTag) fromStack(alloc.stackOffset()); 1.1511 + layout.s.payload.word = fromStack(alloc.stackOffset2()); 1.1512 + return IMPL_TO_JSVAL(layout); 1.1513 + } 1.1514 +#elif defined(JS_PUNBOX64) 1.1515 + case RValueAllocation::UNTYPED_REG: 1.1516 + { 1.1517 + jsval_layout layout; 1.1518 + layout.asBits = fromRegister(alloc.reg()); 1.1519 + return IMPL_TO_JSVAL(layout); 1.1520 + } 1.1521 + 1.1522 + case RValueAllocation::UNTYPED_STACK: 1.1523 + { 1.1524 + jsval_layout layout; 1.1525 + layout.asBits = fromStack(alloc.stackOffset()); 1.1526 + return IMPL_TO_JSVAL(layout); 1.1527 + } 1.1528 +#endif 1.1529 + 1.1530 + default: 1.1531 + MOZ_ASSUME_UNREACHABLE("huh?"); 1.1532 + } 1.1533 +} 1.1534 + 1.1535 +const RResumePoint * 1.1536 +SnapshotIterator::resumePoint() const 1.1537 +{ 1.1538 + return instruction()->toResumePoint(); 1.1539 +} 1.1540 + 1.1541 +uint32_t 1.1542 +SnapshotIterator::numAllocations() const 1.1543 +{ 1.1544 + return resumePoint()->numOperands(); 1.1545 +} 1.1546 + 1.1547 +uint32_t 1.1548 +SnapshotIterator::pcOffset() const 1.1549 +{ 1.1550 + return resumePoint()->pcOffset(); 1.1551 +} 1.1552 + 1.1553 +void 1.1554 +SnapshotIterator::skipInstruction() 1.1555 +{ 1.1556 + MOZ_ASSERT(snapshot_.numAllocationsRead() == 0); 1.1557 + size_t numOperands = instruction()->numOperands(); 1.1558 + for (size_t i = 0; i < numOperands; i++) 1.1559 + skip(); 1.1560 + nextInstruction(); 1.1561 +} 1.1562 + 1.1563 +void 1.1564 +SnapshotIterator::nextFrame() 1.1565 +{ 1.1566 + nextInstruction(); 1.1567 + while (!instruction()->isResumePoint()) 1.1568 + skipInstruction(); 1.1569 +} 1.1570 + 1.1571 +IonScript * 1.1572 +JitFrameIterator::ionScript() const 1.1573 +{ 1.1574 + JS_ASSERT(type() == JitFrame_IonJS); 1.1575 + 1.1576 + IonScript *ionScript = nullptr; 1.1577 + if (checkInvalidation(&ionScript)) 1.1578 + return ionScript; 1.1579 + switch (GetCalleeTokenTag(calleeToken())) { 1.1580 + case CalleeToken_Function: 1.1581 + case CalleeToken_Script: 1.1582 + return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript(); 1.1583 + default: 1.1584 + MOZ_ASSUME_UNREACHABLE("unknown callee token type"); 1.1585 + } 1.1586 +} 1.1587 + 1.1588 +const SafepointIndex * 1.1589 +JitFrameIterator::safepoint() const 1.1590 +{ 1.1591 + if (!cachedSafepointIndex_) 1.1592 + cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp()); 1.1593 + return cachedSafepointIndex_; 1.1594 +} 1.1595 + 1.1596 +const OsiIndex * 1.1597 +JitFrameIterator::osiIndex() const 1.1598 +{ 1.1599 + SafepointReader reader(ionScript(), safepoint()); 1.1600 + return ionScript()->getOsiIndex(reader.osiReturnPointOffset()); 1.1601 +} 1.1602 + 1.1603 +template <AllowGC allowGC> 1.1604 +void 1.1605 +InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter) 1.1606 +{ 1.1607 + frame_ = iter; 1.1608 + framesRead_ = 0; 1.1609 + frameCount_ = UINT32_MAX; 1.1610 + 1.1611 + if (iter) { 1.1612 + start_ = SnapshotIterator(*iter); 1.1613 + findNextFrame(); 1.1614 + } 1.1615 +} 1.1616 +template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter); 1.1617 +template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter); 1.1618 + 1.1619 +template <AllowGC allowGC> 1.1620 +void 1.1621 +InlineFrameIteratorMaybeGC<allowGC>::findNextFrame() 1.1622 +{ 1.1623 + JS_ASSERT(more()); 1.1624 + 1.1625 + si_ = start_; 1.1626 + 1.1627 + // Read the initial frame out of the C stack. 1.1628 + callee_ = frame_->maybeCallee(); 1.1629 + script_ = frame_->script(); 1.1630 + MOZ_ASSERT(script_->hasBaselineScript()); 1.1631 + 1.1632 + // Settle on the outermost frame without evaluating any instructions before 1.1633 + // looking for a pc. 1.1634 + if (!si_.instruction()->isResumePoint()) 1.1635 + si_.nextFrame(); 1.1636 + 1.1637 + pc_ = script_->offsetToPC(si_.pcOffset()); 1.1638 +#ifdef DEBUG 1.1639 + numActualArgs_ = 0xbadbad; 1.1640 +#endif 1.1641 + 1.1642 + // This unfortunately is O(n*m), because we must skip over outer frames 1.1643 + // before reading inner ones. 1.1644 + 1.1645 + // The first time (frameCount_ == UINT32_MAX) we do not know the number of 1.1646 + // frames that we are going to inspect. So we are iterating until there is 1.1647 + // no more frames, to settle on the inner most frame and to count the number 1.1648 + // of frames. 1.1649 + size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX; 1.1650 + 1.1651 + size_t i = 1; 1.1652 + for (; i <= remaining && si_.moreFrames(); i++) { 1.1653 + JS_ASSERT(IsIonInlinablePC(pc_)); 1.1654 + 1.1655 + // Recover the number of actual arguments from the script. 1.1656 + if (JSOp(*pc_) != JSOP_FUNAPPLY) 1.1657 + numActualArgs_ = GET_ARGC(pc_); 1.1658 + if (JSOp(*pc_) == JSOP_FUNCALL) { 1.1659 + JS_ASSERT(GET_ARGC(pc_) > 0); 1.1660 + numActualArgs_ = GET_ARGC(pc_) - 1; 1.1661 + } else if (IsGetPropPC(pc_)) { 1.1662 + numActualArgs_ = 0; 1.1663 + } else if (IsSetPropPC(pc_)) { 1.1664 + numActualArgs_ = 1; 1.1665 + } 1.1666 + 1.1667 + JS_ASSERT(numActualArgs_ != 0xbadbad); 1.1668 + 1.1669 + // Skip over non-argument slots, as well as |this|. 1.1670 + unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1; 1.1671 + for (unsigned j = 0; j < skipCount; j++) 1.1672 + si_.skip(); 1.1673 + 1.1674 + // The JSFunction is a constant, otherwise we would not have inlined it. 1.1675 + Value funval = si_.read(); 1.1676 + 1.1677 + // Skip extra value allocations. 1.1678 + while (si_.moreAllocations()) 1.1679 + si_.skip(); 1.1680 + 1.1681 + si_.nextFrame(); 1.1682 + 1.1683 + callee_ = &funval.toObject().as<JSFunction>(); 1.1684 + 1.1685 + // Inlined functions may be clones that still point to the lazy script 1.1686 + // for the executed script, if they are clones. The actual script 1.1687 + // exists though, just make sure the function points to it. 1.1688 + script_ = callee_->existingScriptForInlinedFunction(); 1.1689 + MOZ_ASSERT(script_->hasBaselineScript()); 1.1690 + 1.1691 + pc_ = script_->offsetToPC(si_.pcOffset()); 1.1692 + } 1.1693 + 1.1694 + // The first time we do not know the number of frames, we only settle on the 1.1695 + // last frame, and update the number of frames based on the number of 1.1696 + // iteration that we have done. 1.1697 + if (frameCount_ == UINT32_MAX) { 1.1698 + MOZ_ASSERT(!si_.moreFrames()); 1.1699 + frameCount_ = i; 1.1700 + } 1.1701 + 1.1702 + framesRead_++; 1.1703 +} 1.1704 +template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame(); 1.1705 +template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame(); 1.1706 + 1.1707 +template <AllowGC allowGC> 1.1708 +bool 1.1709 +InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const 1.1710 +{ 1.1711 + return !!callee_; 1.1712 +} 1.1713 +template bool InlineFrameIteratorMaybeGC<NoGC>::isFunctionFrame() const; 1.1714 +template bool InlineFrameIteratorMaybeGC<CanGC>::isFunctionFrame() const; 1.1715 + 1.1716 +MachineState 1.1717 +MachineState::FromBailout(mozilla::Array<uintptr_t, Registers::Total> ®s, 1.1718 + mozilla::Array<double, FloatRegisters::Total> &fpregs) 1.1719 +{ 1.1720 + MachineState machine; 1.1721 + 1.1722 + for (unsigned i = 0; i < Registers::Total; i++) 1.1723 + machine.setRegisterLocation(Register::FromCode(i), ®s[i]); 1.1724 + for (unsigned i = 0; i < FloatRegisters::Total; i++) 1.1725 + machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]); 1.1726 + 1.1727 + return machine; 1.1728 +} 1.1729 + 1.1730 +template <AllowGC allowGC> 1.1731 +bool 1.1732 +InlineFrameIteratorMaybeGC<allowGC>::isConstructing() const 1.1733 +{ 1.1734 + // Skip the current frame and look at the caller's. 1.1735 + if (more()) { 1.1736 + InlineFrameIteratorMaybeGC<allowGC> parent(GetJSContextFromJitCode(), this); 1.1737 + ++parent; 1.1738 + 1.1739 + // Inlined Getters and Setters are never constructing. 1.1740 + if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc())) 1.1741 + return false; 1.1742 + 1.1743 + // In the case of a JS frame, look up the pc from the snapshot. 1.1744 + JS_ASSERT(IsCallPC(parent.pc())); 1.1745 + 1.1746 + return (JSOp)*parent.pc() == JSOP_NEW; 1.1747 + } 1.1748 + 1.1749 + return frame_->isConstructing(); 1.1750 +} 1.1751 +template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const; 1.1752 +template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const; 1.1753 + 1.1754 +bool 1.1755 +JitFrameIterator::isConstructing() const 1.1756 +{ 1.1757 + JitFrameIterator parent(*this); 1.1758 + 1.1759 + // Skip the current frame and look at the caller's. 1.1760 + do { 1.1761 + ++parent; 1.1762 + } while (!parent.done() && !parent.isScripted()); 1.1763 + 1.1764 + if (parent.isIonJS()) { 1.1765 + // In the case of a JS frame, look up the pc from the snapshot. 1.1766 + InlineFrameIterator inlinedParent(GetJSContextFromJitCode(), &parent); 1.1767 + 1.1768 + //Inlined Getters and Setters are never constructing. 1.1769 + if (IsGetPropPC(inlinedParent.pc()) || IsSetPropPC(inlinedParent.pc())) 1.1770 + return false; 1.1771 + 1.1772 + JS_ASSERT(IsCallPC(inlinedParent.pc())); 1.1773 + 1.1774 + return (JSOp)*inlinedParent.pc() == JSOP_NEW; 1.1775 + } 1.1776 + 1.1777 + if (parent.isBaselineJS()) { 1.1778 + jsbytecode *pc; 1.1779 + parent.baselineScriptAndPc(nullptr, &pc); 1.1780 + 1.1781 + // Inlined Getters and Setters are never constructing. 1.1782 + // Baseline may call getters from [GET|SET]PROP or [GET|SET]ELEM ops. 1.1783 + if (IsGetPropPC(pc) || IsSetPropPC(pc) || IsGetElemPC(pc) || IsSetElemPC(pc)) 1.1784 + return false; 1.1785 + 1.1786 + JS_ASSERT(IsCallPC(pc)); 1.1787 + 1.1788 + return JSOp(*pc) == JSOP_NEW; 1.1789 + } 1.1790 + 1.1791 + JS_ASSERT(parent.done()); 1.1792 + return activation_->firstFrameIsConstructing(); 1.1793 +} 1.1794 + 1.1795 +unsigned 1.1796 +JitFrameIterator::numActualArgs() const 1.1797 +{ 1.1798 + if (isScripted()) 1.1799 + return jsFrame()->numActualArgs(); 1.1800 + 1.1801 + JS_ASSERT(isNative()); 1.1802 + return exitFrame()->nativeExit()->argc(); 1.1803 +} 1.1804 + 1.1805 +void 1.1806 +SnapshotIterator::warnUnreadableAllocation() 1.1807 +{ 1.1808 + fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n"); 1.1809 +} 1.1810 + 1.1811 +struct DumpOp { 1.1812 + DumpOp(unsigned int i) : i_(i) {} 1.1813 + 1.1814 + unsigned int i_; 1.1815 + void operator()(const Value& v) { 1.1816 + fprintf(stderr, " actual (arg %d): ", i_); 1.1817 +#ifdef DEBUG 1.1818 + js_DumpValue(v); 1.1819 +#else 1.1820 + fprintf(stderr, "?\n"); 1.1821 +#endif 1.1822 + i_++; 1.1823 + } 1.1824 +}; 1.1825 + 1.1826 +void 1.1827 +JitFrameIterator::dumpBaseline() const 1.1828 +{ 1.1829 + JS_ASSERT(isBaselineJS()); 1.1830 + 1.1831 + fprintf(stderr, " JS Baseline frame\n"); 1.1832 + if (isFunctionFrame()) { 1.1833 + fprintf(stderr, " callee fun: "); 1.1834 +#ifdef DEBUG 1.1835 + js_DumpObject(callee()); 1.1836 +#else 1.1837 + fprintf(stderr, "?\n"); 1.1838 +#endif 1.1839 + } else { 1.1840 + fprintf(stderr, " global frame, no callee\n"); 1.1841 + } 1.1842 + 1.1843 + fprintf(stderr, " file %s line %u\n", 1.1844 + script()->filename(), (unsigned) script()->lineno()); 1.1845 + 1.1846 + JSContext *cx = GetJSContextFromJitCode(); 1.1847 + RootedScript script(cx); 1.1848 + jsbytecode *pc; 1.1849 + baselineScriptAndPc(script.address(), &pc); 1.1850 + 1.1851 + fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void *)script, pc, uint32_t(script->pcToOffset(pc))); 1.1852 + fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); 1.1853 + 1.1854 + fprintf(stderr, " actual args: %d\n", numActualArgs()); 1.1855 + 1.1856 + BaselineFrame *frame = baselineFrame(); 1.1857 + 1.1858 + for (unsigned i = 0; i < frame->numValueSlots(); i++) { 1.1859 + fprintf(stderr, " slot %u: ", i); 1.1860 +#ifdef DEBUG 1.1861 + Value *v = frame->valueSlot(i); 1.1862 + js_DumpValue(*v); 1.1863 +#else 1.1864 + fprintf(stderr, "?\n"); 1.1865 +#endif 1.1866 + } 1.1867 +} 1.1868 + 1.1869 +template <AllowGC allowGC> 1.1870 +void 1.1871 +InlineFrameIteratorMaybeGC<allowGC>::dump() const 1.1872 +{ 1.1873 + if (more()) 1.1874 + fprintf(stderr, " JS frame (inlined)\n"); 1.1875 + else 1.1876 + fprintf(stderr, " JS frame\n"); 1.1877 + 1.1878 + bool isFunction = false; 1.1879 + if (isFunctionFrame()) { 1.1880 + isFunction = true; 1.1881 + fprintf(stderr, " callee fun: "); 1.1882 +#ifdef DEBUG 1.1883 + js_DumpObject(callee()); 1.1884 +#else 1.1885 + fprintf(stderr, "?\n"); 1.1886 +#endif 1.1887 + } else { 1.1888 + fprintf(stderr, " global frame, no callee\n"); 1.1889 + } 1.1890 + 1.1891 + fprintf(stderr, " file %s line %u\n", 1.1892 + script()->filename(), (unsigned) script()->lineno()); 1.1893 + 1.1894 + fprintf(stderr, " script = %p, pc = %p\n", (void*) script(), pc()); 1.1895 + fprintf(stderr, " current op: %s\n", js_CodeName[*pc()]); 1.1896 + 1.1897 + if (!more()) { 1.1898 + numActualArgs(); 1.1899 + } 1.1900 + 1.1901 + SnapshotIterator si = snapshotIterator(); 1.1902 + fprintf(stderr, " slots: %u\n", si.numAllocations() - 1); 1.1903 + for (unsigned i = 0; i < si.numAllocations() - 1; i++) { 1.1904 + if (isFunction) { 1.1905 + if (i == 0) 1.1906 + fprintf(stderr, " scope chain: "); 1.1907 + else if (i == 1) 1.1908 + fprintf(stderr, " this: "); 1.1909 + else if (i - 2 < callee()->nargs()) 1.1910 + fprintf(stderr, " formal (arg %d): ", i - 2); 1.1911 + else { 1.1912 + if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) { 1.1913 + DumpOp d(callee()->nargs()); 1.1914 + unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown); 1.1915 + } 1.1916 + 1.1917 + fprintf(stderr, " slot %d: ", int(i - 2 - callee()->nargs())); 1.1918 + } 1.1919 + } else 1.1920 + fprintf(stderr, " slot %u: ", i); 1.1921 +#ifdef DEBUG 1.1922 + js_DumpValue(si.maybeRead()); 1.1923 +#else 1.1924 + fprintf(stderr, "?\n"); 1.1925 +#endif 1.1926 + } 1.1927 + 1.1928 + fputc('\n', stderr); 1.1929 +} 1.1930 +template void InlineFrameIteratorMaybeGC<NoGC>::dump() const; 1.1931 +template void InlineFrameIteratorMaybeGC<CanGC>::dump() const; 1.1932 + 1.1933 +void 1.1934 +JitFrameIterator::dump() const 1.1935 +{ 1.1936 + switch (type_) { 1.1937 + case JitFrame_Entry: 1.1938 + fprintf(stderr, " Entry frame\n"); 1.1939 + fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 1.1940 + break; 1.1941 + case JitFrame_BaselineJS: 1.1942 + dumpBaseline(); 1.1943 + break; 1.1944 + case JitFrame_BaselineStub: 1.1945 + case JitFrame_Unwound_BaselineStub: 1.1946 + fprintf(stderr, " Baseline stub frame\n"); 1.1947 + fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 1.1948 + break; 1.1949 + case JitFrame_IonJS: 1.1950 + { 1.1951 + InlineFrameIterator frames(GetJSContextFromJitCode(), this); 1.1952 + for (;;) { 1.1953 + frames.dump(); 1.1954 + if (!frames.more()) 1.1955 + break; 1.1956 + ++frames; 1.1957 + } 1.1958 + break; 1.1959 + } 1.1960 + case JitFrame_Rectifier: 1.1961 + case JitFrame_Unwound_Rectifier: 1.1962 + fprintf(stderr, " Rectifier frame\n"); 1.1963 + fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); 1.1964 + break; 1.1965 + case JitFrame_Unwound_IonJS: 1.1966 + fprintf(stderr, "Warning! Unwound JS frames are not observable.\n"); 1.1967 + break; 1.1968 + case JitFrame_Exit: 1.1969 + break; 1.1970 + }; 1.1971 + fputc('\n', stderr); 1.1972 +} 1.1973 + 1.1974 +IonJSFrameLayout * 1.1975 +InvalidationBailoutStack::fp() const 1.1976 +{ 1.1977 + return (IonJSFrameLayout *) (sp() + ionScript_->frameSize()); 1.1978 +} 1.1979 + 1.1980 +void 1.1981 +InvalidationBailoutStack::checkInvariants() const 1.1982 +{ 1.1983 +#ifdef DEBUG 1.1984 + IonJSFrameLayout *frame = fp(); 1.1985 + CalleeToken token = frame->calleeToken(); 1.1986 + JS_ASSERT(token); 1.1987 + 1.1988 + uint8_t *rawBase = ionScript()->method()->raw(); 1.1989 + uint8_t *rawLimit = rawBase + ionScript()->method()->instructionsSize(); 1.1990 + uint8_t *osiPoint = osiPointReturnAddress(); 1.1991 + JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit); 1.1992 +#endif 1.1993 +} 1.1994 + 1.1995 +} // namespace jit 1.1996 +} // namespace js