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: /* Definitions for javascript analysis. */ michael@0: michael@0: #ifndef jsanalyze_h michael@0: #define jsanalyze_h michael@0: michael@0: #include "jscompartment.h" michael@0: michael@0: namespace js { michael@0: namespace analyze { michael@0: michael@0: class Bytecode; michael@0: struct LifetimeVariable; michael@0: class SlotValue; michael@0: class SSAValue; michael@0: struct SSAValueInfo; michael@0: class SSAUseChain; michael@0: michael@0: // Common representation of slots between ScriptAnalysis, TypeScript, and in the michael@0: // case of TotalSlots, Ion. michael@0: static inline uint32_t ThisSlot() { michael@0: return 0; michael@0: } michael@0: static inline uint32_t ArgSlot(uint32_t arg) { michael@0: return 1 + arg; michael@0: } michael@0: static inline uint32_t LocalSlot(JSScript *script, uint32_t local) { michael@0: return 1 + local + michael@0: (script->functionNonDelazifying() ? script->functionNonDelazifying()->nargs() : 0); michael@0: } michael@0: static inline uint32_t TotalSlots(JSScript *script) { michael@0: return LocalSlot(script, 0) + script->nfixed(); michael@0: } michael@0: michael@0: // Analysis information about a script. FIXME: At this point, the entire michael@0: // purpose of this class is to compute JSScript::needsArgsObj, and to support michael@0: // isReachable() in order for jsinfer.cpp:FindPreviousInnerInitializer to get michael@0: // the previous opcode. For that purpose, it is completely overkill. michael@0: class ScriptAnalysis michael@0: { michael@0: friend class Bytecode; michael@0: michael@0: JSScript *script_; michael@0: michael@0: Bytecode **codeArray; michael@0: michael@0: uint32_t numSlots; michael@0: michael@0: bool *escapedSlots; michael@0: michael@0: #ifdef DEBUG michael@0: /* Whether the compartment was in debug mode when we performed the analysis. */ michael@0: bool originalDebugMode_: 1; michael@0: #endif michael@0: michael@0: /* --------- Bytecode analysis --------- */ michael@0: michael@0: bool canTrackVars:1; michael@0: bool argumentsContentsObserved_:1; michael@0: michael@0: /* --------- Lifetime analysis --------- */ michael@0: michael@0: LifetimeVariable *lifetimes; michael@0: michael@0: public: michael@0: ScriptAnalysis(JSScript *script) { michael@0: mozilla::PodZero(this); michael@0: this->script_ = script; michael@0: #ifdef DEBUG michael@0: this->originalDebugMode_ = script_->compartment()->debugMode(); michael@0: #endif michael@0: } michael@0: michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool analyzeBytecode(JSContext *cx); michael@0: michael@0: bool isReachable(const jsbytecode *pc) { return maybeCode(pc); } michael@0: michael@0: private: michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool analyzeSSA(JSContext *cx); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool analyzeLifetimes(JSContext *cx); michael@0: michael@0: /* Accessors for bytecode information. */ michael@0: Bytecode& getCode(uint32_t offset) { michael@0: JS_ASSERT(offset < script_->length()); michael@0: JS_ASSERT(codeArray[offset]); michael@0: return *codeArray[offset]; michael@0: } michael@0: Bytecode& getCode(const jsbytecode *pc) { return getCode(script_->pcToOffset(pc)); } michael@0: michael@0: Bytecode* maybeCode(uint32_t offset) { michael@0: JS_ASSERT(offset < script_->length()); michael@0: return codeArray[offset]; michael@0: } michael@0: Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(script_->pcToOffset(pc)); } michael@0: michael@0: inline bool jumpTarget(uint32_t offset); michael@0: inline bool jumpTarget(const jsbytecode *pc); michael@0: michael@0: inline const SSAValue &poppedValue(uint32_t offset, uint32_t which); michael@0: inline const SSAValue &poppedValue(const jsbytecode *pc, uint32_t which); michael@0: michael@0: inline const SlotValue *newValues(uint32_t offset); michael@0: inline const SlotValue *newValues(const jsbytecode *pc); michael@0: michael@0: inline bool trackUseChain(const SSAValue &v); michael@0: michael@0: /* michael@0: * Get the use chain for an SSA value. michael@0: */ michael@0: inline SSAUseChain *& useChain(const SSAValue &v); michael@0: michael@0: michael@0: /* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */ michael@0: inline jsbytecode *getCallPC(jsbytecode *pc); michael@0: michael@0: /* Accessors for local variable information. */ michael@0: michael@0: /* michael@0: * Escaping slots include all slots that can be accessed in ways other than michael@0: * through the corresponding LOCAL/ARG opcode. This includes all closed michael@0: * slots in the script, all slots in scripts which use eval or are in debug michael@0: * mode, and slots which are aliased by NAME or similar opcodes in the michael@0: * containing script (which does not imply the variable is closed). michael@0: */ michael@0: inline bool slotEscapes(uint32_t slot); michael@0: michael@0: /* michael@0: * Whether we distinguish different writes of this variable while doing michael@0: * SSA analysis. Escaping locals can be written in other scripts, and the michael@0: * presence of NAME opcodes which could alias local variables or arguments michael@0: * keeps us from tracking variable values at each point. michael@0: */ michael@0: inline bool trackSlot(uint32_t slot); michael@0: michael@0: inline const LifetimeVariable & liveness(uint32_t slot); michael@0: michael@0: void printSSA(JSContext *cx); michael@0: void printTypes(JSContext *cx); michael@0: michael@0: /* Bytecode helpers */ michael@0: MOZ_WARN_UNUSED_RESULT michael@0: inline bool addJump(JSContext *cx, unsigned offset, michael@0: unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop, michael@0: unsigned stackDepth); michael@0: michael@0: /* Lifetime helpers */ michael@0: MOZ_WARN_UNUSED_RESULT michael@0: inline bool addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, michael@0: LifetimeVariable **&saved, unsigned &savedCount); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: inline bool killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, michael@0: LifetimeVariable **&saved, unsigned &savedCount); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: inline bool extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end); michael@0: michael@0: inline void ensureVariable(LifetimeVariable &var, unsigned until); michael@0: michael@0: /* SSA helpers */ michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot, michael@0: Vector *pending); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool checkBranchTarget(JSContext *cx, uint32_t targetOffset, Vector &branchTargets, michael@0: SSAValueInfo *values, uint32_t stackDepth); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool checkExceptionTarget(JSContext *cx, uint32_t catchOffset, michael@0: Vector &exceptionTargets); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot, michael@0: const Vector &branchTargets, uint32_t currentOffset); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot, michael@0: const Vector &exceptionTargets); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values, michael@0: const Vector &exceptionTargets); michael@0: MOZ_WARN_UNUSED_RESULT michael@0: bool freezeNewValues(JSContext *cx, uint32_t offset); michael@0: michael@0: typedef Vector SeenVector; michael@0: bool needsArgsObj(JSContext *cx, SeenVector &seen, const SSAValue &v); michael@0: bool needsArgsObj(JSContext *cx, SeenVector &seen, SSAUseChain *use); michael@0: bool needsArgsObj(JSContext *cx); michael@0: michael@0: public: michael@0: #ifdef DEBUG michael@0: void assertMatchingDebugMode(); michael@0: void assertMatchingStackDepthAtOffset(uint32_t offset, uint32_t stackDepth); michael@0: #else michael@0: void assertMatchingDebugMode() { } michael@0: void assertMatchingStackDepthAtOffset(uint32_t offset, uint32_t stackDepth) { } michael@0: #endif michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: void PrintBytecode(JSContext *cx, HandleScript script, jsbytecode *pc); michael@0: #endif michael@0: michael@0: } /* namespace analyze */ michael@0: } /* namespace js */ michael@0: michael@0: #endif /* jsanalyze_h */