summaryrefslogtreecommitdiffstats
path: root/js/src/jit/BytecodeAnalysis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/BytecodeAnalysis.cpp')
-rw-r--r--js/src/jit/BytecodeAnalysis.cpp227
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;
+}