michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/BytecodeAnalysis.h" michael@0: michael@0: #include "jsopcode.h" michael@0: #include "jit/IonSpewer.h" michael@0: #include "jsopcodeinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: BytecodeAnalysis::BytecodeAnalysis(TempAllocator &alloc, JSScript *script) michael@0: : script_(script), michael@0: infos_(alloc), michael@0: usesScopeChain_(false), michael@0: hasTryFinally_(false), michael@0: hasSetArg_(false) michael@0: { michael@0: } michael@0: michael@0: // Bytecode range containing only catch or finally code. michael@0: struct CatchFinallyRange michael@0: { michael@0: uint32_t start; // Inclusive. michael@0: uint32_t end; // Exclusive. michael@0: michael@0: CatchFinallyRange(uint32_t start, uint32_t end) michael@0: : start(start), end(end) michael@0: { michael@0: JS_ASSERT(end > start); michael@0: } michael@0: michael@0: bool contains(uint32_t offset) const { michael@0: return start <= offset && offset < end; michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn) michael@0: { michael@0: if (!infos_.growByUninitialized(script_->length())) michael@0: return false; michael@0: michael@0: jsbytecode *end = script_->codeEnd(); michael@0: michael@0: // Clear all BytecodeInfo. michael@0: mozilla::PodZero(infos_.begin(), infos_.length()); michael@0: infos_[0].init(/*stackDepth=*/0); michael@0: michael@0: Vector catchFinallyRanges(alloc); michael@0: michael@0: for (jsbytecode *pc = script_->code(); pc < end; pc += GetBytecodeLength(pc)) { michael@0: JSOp op = JSOp(*pc); michael@0: unsigned offset = script_->pcToOffset(pc); michael@0: michael@0: IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s", michael@0: int(script_->pcToOffset(pc)), int(script_->length()), js_CodeName[op]); michael@0: michael@0: // If this bytecode info has not yet been initialized, it's not reachable. michael@0: if (!infos_[offset].initialized) michael@0: continue; michael@0: michael@0: michael@0: unsigned stackDepth = infos_[offset].stackDepth; michael@0: #ifdef DEBUG michael@0: for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++) michael@0: JS_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized); michael@0: #endif michael@0: michael@0: unsigned nuses = GetUseCount(script_, offset); michael@0: unsigned ndefs = GetDefCount(script_, offset); michael@0: michael@0: JS_ASSERT(stackDepth >= nuses); michael@0: stackDepth -= nuses; michael@0: stackDepth += ndefs; michael@0: michael@0: // If stack depth exceeds max allowed by analysis, fail fast. michael@0: JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH); michael@0: michael@0: switch (op) { michael@0: case JSOP_TABLESWITCH: { michael@0: unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); michael@0: jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; michael@0: int32_t low = GET_JUMP_OFFSET(pc2); michael@0: pc2 += JUMP_OFFSET_LEN; michael@0: int32_t high = GET_JUMP_OFFSET(pc2); michael@0: pc2 += JUMP_OFFSET_LEN; michael@0: michael@0: infos_[defaultOffset].init(stackDepth); michael@0: infos_[defaultOffset].jumpTarget = true; michael@0: michael@0: for (int32_t i = low; i <= high; i++) { michael@0: unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); michael@0: if (targetOffset != offset) { michael@0: infos_[targetOffset].init(stackDepth); michael@0: infos_[targetOffset].jumpTarget = true; michael@0: } michael@0: pc2 += JUMP_OFFSET_LEN; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case JSOP_TRY: { michael@0: JSTryNote *tn = script_->trynotes()->vector; michael@0: JSTryNote *tnlimit = tn + script_->trynotes()->length; michael@0: for (; tn < tnlimit; tn++) { michael@0: unsigned startOffset = script_->mainOffset() + tn->start; michael@0: if (startOffset == offset + 1) { michael@0: unsigned catchOffset = startOffset + tn->length; michael@0: michael@0: if (tn->kind != JSTRY_ITER) { michael@0: infos_[catchOffset].init(stackDepth); michael@0: infos_[catchOffset].jumpTarget = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to michael@0: // jump over the catch/finally blocks. michael@0: jssrcnote *sn = GetSrcNote(gsn, script_, pc); michael@0: JS_ASSERT(SN_TYPE(sn) == SRC_TRY); michael@0: michael@0: jsbytecode *endOfTry = pc + js_GetSrcNoteOffset(sn, 0); michael@0: JS_ASSERT(JSOp(*endOfTry) == JSOP_GOTO); michael@0: michael@0: jsbytecode *afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry); michael@0: JS_ASSERT(afterTry > endOfTry); michael@0: michael@0: // Pop CatchFinallyRanges that are no longer needed. michael@0: while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset) michael@0: catchFinallyRanges.popBack(); michael@0: michael@0: CatchFinallyRange range(script_->pcToOffset(endOfTry), script_->pcToOffset(afterTry)); michael@0: if (!catchFinallyRanges.append(range)) michael@0: return false; michael@0: break; michael@0: } michael@0: michael@0: case JSOP_LOOPENTRY: michael@0: for (size_t i = 0; i < catchFinallyRanges.length(); i++) { michael@0: if (catchFinallyRanges[i].contains(offset)) michael@0: infos_[offset].loopEntryInCatchOrFinally = true; michael@0: } michael@0: break; michael@0: michael@0: case JSOP_NAME: michael@0: case JSOP_BINDNAME: michael@0: case JSOP_SETNAME: michael@0: case JSOP_DELNAME: michael@0: case JSOP_GETALIASEDVAR: michael@0: case JSOP_SETALIASEDVAR: michael@0: case JSOP_LAMBDA: michael@0: case JSOP_LAMBDA_ARROW: michael@0: case JSOP_DEFFUN: michael@0: case JSOP_DEFVAR: michael@0: case JSOP_DEFCONST: michael@0: case JSOP_SETCONST: michael@0: usesScopeChain_ = true; michael@0: break; michael@0: michael@0: case JSOP_FINALLY: michael@0: hasTryFinally_ = true; michael@0: break; michael@0: michael@0: case JSOP_SETARG: michael@0: hasSetArg_ = true; michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: bool jump = IsJumpOpcode(op); michael@0: if (jump) { michael@0: // Case instructions do not push the lvalue back when branching. michael@0: unsigned newStackDepth = stackDepth; michael@0: if (op == JSOP_CASE) michael@0: newStackDepth--; michael@0: michael@0: unsigned targetOffset = offset + GET_JUMP_OFFSET(pc); michael@0: michael@0: // If this is a a backedge to an un-analyzed segment, analyze from there. michael@0: bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized; michael@0: michael@0: infos_[targetOffset].init(newStackDepth); michael@0: infos_[targetOffset].jumpTarget = true; michael@0: michael@0: if (jumpBack) michael@0: pc = script_->offsetToPC(targetOffset); michael@0: } michael@0: michael@0: // Handle any fallthrough from this opcode. michael@0: if (BytecodeFallsThrough(op)) { michael@0: jsbytecode *nextpc = pc + GetBytecodeLength(pc); michael@0: JS_ASSERT(nextpc < end); michael@0: unsigned nextOffset = script_->pcToOffset(nextpc); michael@0: michael@0: infos_[nextOffset].init(stackDepth); michael@0: michael@0: if (jump) michael@0: infos_[nextOffset].jumpFallthrough = true; michael@0: michael@0: // Treat the fallthrough of a branch instruction as a jump target. michael@0: if (jump) michael@0: infos_[nextOffset].jumpTarget = true; michael@0: else michael@0: infos_[nextOffset].fallthrough = true; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: }