#include "CodeBlockHash.h"
#include "CodeSpecializationKind.h"
+#include "JSFunction.h"
#include "ValueRecovery.h"
#include "WriteBarrier.h"
#include <wtf/BitVector.h>
+#include <wtf/HashMap.h>
#include <wtf/PrintStream.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
struct InlineCallFrame;
class ExecState;
-class ExecutableBase;
+class ScriptExecutable;
class JSFunction;
struct CodeOrigin {
- static const unsigned maximumBytecodeIndex = (1u << 29) - 1;
+ static const unsigned invalidBytecodeIndex = UINT_MAX;
- // Bytecode offset that you'd use to re-execute this instruction.
- unsigned bytecodeIndex : 29;
- // Bytecode offset corresponding to the opcode that gives the result (needed to handle
- // op_call/op_call_put_result and op_method_check/op_get_by_id).
- unsigned valueProfileOffset : 3;
+ // Bytecode offset that you'd use to re-execute this instruction, and the
+ // bytecode index of the bytecode instruction that produces some result that
+ // you're interested in (used for mapping Nodes whose values you're using
+ // to bytecode instructions that have the appropriate value profile).
+ unsigned bytecodeIndex;
InlineCallFrame* inlineCallFrame;
CodeOrigin()
- : bytecodeIndex(maximumBytecodeIndex)
- , valueProfileOffset(0)
+ : bytecodeIndex(invalidBytecodeIndex)
, inlineCallFrame(0)
{
}
- explicit CodeOrigin(unsigned bytecodeIndex, InlineCallFrame* inlineCallFrame = 0, unsigned valueProfileOffset = 0)
+ CodeOrigin(WTF::HashTableDeletedValueType)
+ : bytecodeIndex(invalidBytecodeIndex)
+ , inlineCallFrame(deletedMarker())
+ {
+ }
+
+ explicit CodeOrigin(unsigned bytecodeIndex, InlineCallFrame* inlineCallFrame = 0)
: bytecodeIndex(bytecodeIndex)
- , valueProfileOffset(valueProfileOffset)
, inlineCallFrame(inlineCallFrame)
{
- RELEASE_ASSERT(bytecodeIndex <= maximumBytecodeIndex);
- RELEASE_ASSERT(valueProfileOffset < (1u << 3));
+ ASSERT(bytecodeIndex < invalidBytecodeIndex);
}
- bool isSet() const { return bytecodeIndex != maximumBytecodeIndex; }
+ bool isSet() const { return bytecodeIndex != invalidBytecodeIndex; }
+ bool operator!() const { return !isSet(); }
- unsigned bytecodeIndexForValueProfile() const
+ bool isHashTableDeletedValue() const
{
- return bytecodeIndex + valueProfileOffset;
+ return bytecodeIndex == invalidBytecodeIndex && !!inlineCallFrame;
}
// The inline depth is the depth of the inline stack, so 1 = not inlined,
// If the code origin corresponds to inlined code, gives you the heap object that
// would have owned the code if it had not been inlined. Otherwise returns 0.
- ExecutableBase* codeOriginOwner() const;
+ ScriptExecutable* codeOriginOwner() const;
- unsigned stackOffset() const;
+ int stackOffset() const;
static unsigned inlineDepthForCallFrame(InlineCallFrame*);
+ unsigned hash() const;
bool operator==(const CodeOrigin& other) const;
-
bool operator!=(const CodeOrigin& other) const { return !(*this == other); }
+ // This checks if the two code origins correspond to the same stack trace snippets,
+ // but ignore whether the InlineCallFrame's are identical.
+ bool isApproximatelyEqualTo(const CodeOrigin& other) const;
+
+ unsigned approximateHash() const;
+
// Get the inline stack. This is slow, and is intended for debugging only.
Vector<CodeOrigin> inlineStack() const;
void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+
+private:
+ static InlineCallFrame* deletedMarker()
+ {
+ return bitwise_cast<InlineCallFrame*>(static_cast<uintptr_t>(1));
+ }
};
struct InlineCallFrame {
- Vector<ValueRecovery> arguments;
- WriteBarrier<ExecutableBase> executable;
- WriteBarrier<JSFunction> callee; // This may be null, indicating that this is a closure call and that the JSFunction and JSScope are already on the stack.
+ Vector<ValueRecovery> arguments; // Includes 'this'.
+ WriteBarrier<ScriptExecutable> executable;
+ ValueRecovery calleeRecovery;
CodeOrigin caller;
BitVector capturedVars; // Indexed by the machine call frame's variable numbering.
- unsigned stackOffset : 31;
+ signed stackOffset : 30;
bool isCall : 1;
+ bool isClosureCall : 1; // If false then we know that callee/scope are constants and the DFG won't treat them as variables, i.e. they have to be recovered manually.
+ VirtualRegister argumentsRegister; // This is only set if the code uses arguments. The unmodified arguments register follows the unmodifiedArgumentsRegister() convention (see CodeBlock.h).
+
+ // There is really no good notion of a "default" set of values for
+ // InlineCallFrame's fields. This constructor is here just to reduce confusion if
+ // we forgot to initialize explicitly.
+ InlineCallFrame()
+ : stackOffset(0)
+ , isCall(false)
+ , isClosureCall(false)
+ {
+ }
CodeSpecializationKind specializationKind() const { return specializationFromIsCall(isCall); }
-
- bool isClosureCall() const { return !callee; }
+
+ JSFunction* calleeConstant() const
+ {
+ if (calleeRecovery.isConstant())
+ return jsCast<JSFunction*>(calleeRecovery.constant());
+ return 0;
+ }
+
+ void visitAggregate(SlotVisitor& visitor)
+ {
+ visitor.append(&executable);
+ }
// Get the callee given a machine call frame to which this InlineCallFrame belongs.
JSFunction* calleeForCallFrame(ExecState*) const;
- String inferredName() const;
+ CString inferredName() const;
CodeBlockHash hash() const;
+ CString hashAsStringIfPossible() const;
CodeBlock* baselineCodeBlock() const;
+ ptrdiff_t callerFrameOffset() const { return stackOffset * sizeof(Register) + CallFrame::callerFrameOffset(); }
+ ptrdiff_t returnPCOffset() const { return stackOffset * sizeof(Register) + CallFrame::returnPCOffset(); }
+
void dumpBriefFunctionInformation(PrintStream&) const;
void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
MAKE_PRINT_METHOD(InlineCallFrame, dumpBriefFunctionInformation, briefFunctionInformation);
};
-struct CodeOriginAtCallReturnOffset {
- CodeOrigin codeOrigin;
- unsigned callReturnOffset;
-};
-
-inline unsigned CodeOrigin::stackOffset() const
+inline int CodeOrigin::stackOffset() const
{
if (!inlineCallFrame)
return 0;
return inlineCallFrame->stackOffset;
}
+inline unsigned CodeOrigin::hash() const
+{
+ return WTF::IntHash<unsigned>::hash(bytecodeIndex) +
+ WTF::PtrHash<InlineCallFrame*>::hash(inlineCallFrame);
+}
+
inline bool CodeOrigin::operator==(const CodeOrigin& other) const
{
return bytecodeIndex == other.bytecodeIndex
&& inlineCallFrame == other.inlineCallFrame;
}
-inline unsigned getCallReturnOffsetForCodeOrigin(CodeOriginAtCallReturnOffset* data)
-{
- return data->callReturnOffset;
-}
-
-inline ExecutableBase* CodeOrigin::codeOriginOwner() const
+inline ScriptExecutable* CodeOrigin::codeOriginOwner() const
{
if (!inlineCallFrame)
return 0;
return inlineCallFrame->executable.get();
}
+struct CodeOriginHash {
+ static unsigned hash(const CodeOrigin& key) { return key.hash(); }
+ static bool equal(const CodeOrigin& a, const CodeOrigin& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct CodeOriginApproximateHash {
+ static unsigned hash(const CodeOrigin& key) { return key.approximateHash(); }
+ static bool equal(const CodeOrigin& a, const CodeOrigin& b) { return a.isApproximatelyEqualTo(b); }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
} // namespace JSC
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::CodeOrigin> {
+ typedef JSC::CodeOriginHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::CodeOrigin> : SimpleClassHashTraits<JSC::CodeOrigin> {
+ static const bool emptyValueIsZero = false;
+};
+
+} // namespace WTF
+
#endif // CodeOrigin_h