diff options
Diffstat (limited to 'js/src/jit/BytecodeAnalysis.cpp')
-rw-r--r-- | js/src/jit/BytecodeAnalysis.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp new file mode 100644 index 000000000..f15420a7d --- /dev/null +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/BytecodeAnalysis.h" + +#include "jsopcode.h" +#include "jit/JitSpewer.h" +#include "jsopcodeinlines.h" +#include "jsscriptinlines.h" + +using namespace js; +using namespace js::jit; + +BytecodeAnalysis::BytecodeAnalysis(TempAllocator& alloc, JSScript* script) + : script_(script), + infos_(alloc), + usesEnvironmentChain_(false), + hasTryFinally_(false), + hasSetArg_(false) +{ +} + +// Bytecode range containing only catch or finally code. +struct CatchFinallyRange +{ + uint32_t start; // Inclusive. + uint32_t end; // Exclusive. + + CatchFinallyRange(uint32_t start, uint32_t end) + : start(start), end(end) + { + MOZ_ASSERT(end > start); + } + + bool contains(uint32_t offset) const { + return start <= offset && offset < end; + } +}; + +bool +BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn) +{ + if (!infos_.growByUninitialized(script_->length())) + return false; + + // Initialize the env chain slot if either the function needs some + // EnvironmentObject (like a CallObject) or the script uses the env + // chain. The latter case is handled below. + usesEnvironmentChain_ = script_->module() || script_->initialEnvironmentShape() || + (script_->functionDelazifying() && + script_->functionDelazifying()->needsSomeEnvironmentObject()); + + jsbytecode* end = script_->codeEnd(); + + // Clear all BytecodeInfo. + mozilla::PodZero(infos_.begin(), infos_.length()); + infos_[0].init(/*stackDepth=*/0); + + Vector<CatchFinallyRange, 0, JitAllocPolicy> catchFinallyRanges(alloc); + + jsbytecode* nextpc; + for (jsbytecode* pc = script_->code(); pc < end; pc = nextpc) { + JSOp op = JSOp(*pc); + nextpc = pc + GetBytecodeLength(pc); + unsigned offset = script_->pcToOffset(pc); + + JitSpew(JitSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s", + int(script_->pcToOffset(pc)), int(script_->length()), CodeName[op]); + + // If this bytecode info has not yet been initialized, it's not reachable. + if (!infos_[offset].initialized) + continue; + + unsigned stackDepth = infos_[offset].stackDepth; +#ifdef DEBUG + for (jsbytecode* chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++) + MOZ_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized); +#endif + + unsigned nuses = GetUseCount(script_, offset); + unsigned ndefs = GetDefCount(script_, offset); + + MOZ_ASSERT(stackDepth >= nuses); + stackDepth -= nuses; + stackDepth += ndefs; + + // If stack depth exceeds max allowed by analysis, fail fast. + MOZ_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH); + + switch (op) { + case JSOP_TABLESWITCH: { + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode* pc2 = pc + JUMP_OFFSET_LEN; + int32_t low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + int32_t high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + + infos_[defaultOffset].init(stackDepth); + infos_[defaultOffset].jumpTarget = true; + + for (int32_t i = low; i <= high; i++) { + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + if (targetOffset != offset) { + infos_[targetOffset].init(stackDepth); + infos_[targetOffset].jumpTarget = true; + } + pc2 += JUMP_OFFSET_LEN; + } + break; + } + + case JSOP_TRY: { + JSTryNote* tn = script_->trynotes()->vector; + JSTryNote* tnlimit = tn + script_->trynotes()->length; + for (; tn < tnlimit; tn++) { + unsigned startOffset = script_->mainOffset() + tn->start; + if (startOffset == offset + 1) { + unsigned catchOffset = startOffset + tn->length; + + if (tn->kind != JSTRY_FOR_IN) { + infos_[catchOffset].init(stackDepth); + infos_[catchOffset].jumpTarget = true; + } + } + } + + // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to + // jump over the catch/finally blocks. + jssrcnote* sn = GetSrcNote(gsn, script_, pc); + MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY); + + jsbytecode* endOfTry = pc + GetSrcNoteOffset(sn, 0); + MOZ_ASSERT(JSOp(*endOfTry) == JSOP_GOTO); + + jsbytecode* afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry); + MOZ_ASSERT(afterTry > endOfTry); + + // Pop CatchFinallyRanges that are no longer needed. + while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset) + catchFinallyRanges.popBack(); + + CatchFinallyRange range(script_->pcToOffset(endOfTry), script_->pcToOffset(afterTry)); + if (!catchFinallyRanges.append(range)) + return false; + break; + } + + case JSOP_LOOPENTRY: + for (size_t i = 0; i < catchFinallyRanges.length(); i++) { + if (catchFinallyRanges[i].contains(offset)) + infos_[offset].loopEntryInCatchOrFinally = true; + } + break; + + case JSOP_GETNAME: + case JSOP_BINDNAME: + case JSOP_BINDVAR: + case JSOP_SETNAME: + case JSOP_STRICTSETNAME: + case JSOP_DELNAME: + case JSOP_GETALIASEDVAR: + case JSOP_SETALIASEDVAR: + case JSOP_LAMBDA: + case JSOP_LAMBDA_ARROW: + case JSOP_DEFFUN: + case JSOP_DEFVAR: + usesEnvironmentChain_ = true; + break; + + case JSOP_GETGNAME: + case JSOP_SETGNAME: + case JSOP_STRICTSETGNAME: + if (script_->hasNonSyntacticScope()) + usesEnvironmentChain_ = true; + break; + + case JSOP_FINALLY: + hasTryFinally_ = true; + break; + + case JSOP_SETARG: + hasSetArg_ = true; + break; + + default: + break; + } + + bool jump = IsJumpOpcode(op); + if (jump) { + // Case instructions do not push the lvalue back when branching. + unsigned newStackDepth = stackDepth; + if (op == JSOP_CASE) + newStackDepth--; + + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc); + + // If this is a a backedge to an un-analyzed segment, analyze from there. + bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized; + + infos_[targetOffset].init(newStackDepth); + infos_[targetOffset].jumpTarget = true; + + if (jumpBack) + nextpc = script_->offsetToPC(targetOffset); + } + + // Handle any fallthrough from this opcode. + if (BytecodeFallsThrough(op)) { + jsbytecode* fallthrough = pc + GetBytecodeLength(pc); + MOZ_ASSERT(fallthrough < end); + unsigned fallthroughOffset = script_->pcToOffset(fallthrough); + + infos_[fallthroughOffset].init(stackDepth); + + // Treat the fallthrough of a branch instruction as a jump target. + if (jump) + infos_[fallthroughOffset].jumpTarget = true; + } + } + + return true; +} |