blob: abe9a8a282edf4e78a463830f350b76f98d0f50f [file] [log] [blame]
/*
* Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "BytecodeDumper.h"
#include "ArithProfile.h"
#include "CallLinkStatus.h"
#include "CodeBlock.h"
#include "Error.h"
#include "HeapInlines.h"
#include "InterpreterInlines.h"
#include "PolymorphicAccess.h"
#include "PutByIdFlags.h"
#include "StructureInlines.h"
#include "ToThisStatus.h"
#include "UnlinkedCodeBlock.h"
namespace JSC {
static StructureID getStructureID(const Instruction& instruction)
{
return instruction.u.structureID;
}
static StructureID getStructureID(const UnlinkedInstruction&)
{
return 0;
}
static Special::Pointer getSpecialPointer(const Instruction& instruction)
{
return instruction.u.specialPointer;
}
static Special::Pointer getSpecialPointer(const UnlinkedInstruction& instruction)
{
return static_cast<Special::Pointer>(instruction.u.operand);
}
static PutByIdFlags getPutByIdFlags(const Instruction& instruction)
{
return instruction.u.putByIdFlags;
}
static PutByIdFlags getPutByIdFlags(const UnlinkedInstruction& instruction)
{
return static_cast<PutByIdFlags>(instruction.u.operand);
}
static ToThisStatus getToThisStatus(const Instruction& instruction)
{
return instruction.u.toThisStatus;
}
static ToThisStatus getToThisStatus(const UnlinkedInstruction& instruction)
{
return static_cast<ToThisStatus>(instruction.u.operand);
}
static void* getPointer(const Instruction& instruction)
{
return instruction.u.pointer;
}
static void* getPointer(const UnlinkedInstruction&)
{
return nullptr;
}
static StructureChain* getStructureChain(const Instruction& instruction)
{
return instruction.u.structureChain.get();
}
static StructureChain* getStructureChain(const UnlinkedInstruction&)
{
return nullptr;
}
static Structure* getStructure(const Instruction& instruction)
{
return instruction.u.structure.get();
}
static Structure* getStructure(const UnlinkedInstruction&)
{
return nullptr;
}
static LLIntCallLinkInfo* getCallLinkInfo(const Instruction& instruction)
{
return instruction.u.callLinkInfo;
}
static LLIntCallLinkInfo* getCallLinkInfo(const UnlinkedInstruction&)
{
return nullptr;
}
static BasicBlockLocation* getBasicBlockLocation(const Instruction& instruction)
{
return instruction.u.basicBlockLocation;
}
static BasicBlockLocation* getBasicBlockLocation(const UnlinkedInstruction&)
{
return nullptr;
}
template<class Block>
void* BytecodeDumper<Block>::actualPointerFor(Special::Pointer) const
{
return nullptr;
}
template<>
void* BytecodeDumper<CodeBlock>::actualPointerFor(Special::Pointer pointer) const
{
return block()->globalObject()->actualPointerFor(pointer);
}
static void beginDumpProfiling(PrintStream& out, bool& hasPrintedProfiling)
{
if (hasPrintedProfiling) {
out.print("; ");
return;
}
out.print(" ");
hasPrintedProfiling = true;
}
template<class Block>
void BytecodeDumper<Block>::dumpValueProfiling(PrintStream&, const typename Block::Instruction*& it, bool&)
{
++it;
}
template<>
void BytecodeDumper<CodeBlock>::dumpValueProfiling(PrintStream& out, const typename CodeBlock::Instruction*& it, bool& hasPrintedProfiling)
{
ConcurrentJSLocker locker(block()->m_lock);
++it;
CString description = it->u.profile->briefDescription(locker);
if (!description.length())
return;
beginDumpProfiling(out, hasPrintedProfiling);
out.print(description);
}
template<class Block>
void BytecodeDumper<Block>::dumpArrayProfiling(PrintStream&, const typename Block::Instruction*& it, bool&)
{
++it;
}
template<>
void BytecodeDumper<CodeBlock>::dumpArrayProfiling(PrintStream& out, const typename CodeBlock::Instruction*& it, bool& hasPrintedProfiling)
{
ConcurrentJSLocker locker(block()->m_lock);
++it;
if (!it->u.arrayProfile)
return;
CString description = it->u.arrayProfile->briefDescription(locker, block());
if (!description.length())
return;
beginDumpProfiling(out, hasPrintedProfiling);
out.print(description);
}
template<class Block>
void BytecodeDumper<Block>::dumpProfilesForBytecodeOffset(PrintStream&, unsigned, bool&)
{
}
static void dumpRareCaseProfile(PrintStream& out, const char* name, RareCaseProfile* profile, bool& hasPrintedProfiling)
{
if (!profile || !profile->m_counter)
return;
beginDumpProfiling(out, hasPrintedProfiling);
out.print(name, profile->m_counter);
}
static void dumpArithProfile(PrintStream& out, ArithProfile* profile, bool& hasPrintedProfiling)
{
if (!profile)
return;
beginDumpProfiling(out, hasPrintedProfiling);
out.print("results: ", *profile);
}
template<>
void BytecodeDumper<CodeBlock>::dumpProfilesForBytecodeOffset(PrintStream& out, unsigned location, bool& hasPrintedProfiling)
{
dumpRareCaseProfile(out, "rare case: ", block()->rareCaseProfileForBytecodeOffset(location), hasPrintedProfiling);
{
dumpArithProfile(out, block()->arithProfileForBytecodeOffset(location), hasPrintedProfiling);
}
#if ENABLE(DFG_JIT)
Vector<DFG::FrequentExitSite> exitSites = block()->unlinkedCodeBlock()->exitProfile().exitSitesFor(location);
if (!exitSites.isEmpty()) {
out.print(" !! frequent exits: ");
CommaPrinter comma;
for (auto& exitSite : exitSites)
out.print(comma, exitSite.kind(), " ", exitSite.jitType());
}
#else // ENABLE(DFG_JIT)
UNUSED_PARAM(location);
#endif // ENABLE(DFG_JIT)
}
template<class Block>
VM* BytecodeDumper<Block>::vm() const
{
return block()->vm();
}
template<class Block>
const Identifier& BytecodeDumper<Block>::identifier(int index) const
{
return block()->identifier(index);
}
static CString regexpToSourceString(RegExp* regExp)
{
char postfix[7] = { '/', 0, 0, 0, 0, 0, 0 };
int index = 1;
if (regExp->global())
postfix[index++] = 'g';
if (regExp->ignoreCase())
postfix[index++] = 'i';
if (regExp->multiline())
postfix[index] = 'm';
if (regExp->dotAll())
postfix[index++] = 's';
if (regExp->unicode())
postfix[index++] = 'u';
if (regExp->sticky())
postfix[index++] = 'y';
return toCString("/", regExp->pattern().impl(), postfix);
}
static CString regexpName(int re, RegExp* regexp)
{
return toCString(regexpToSourceString(regexp), "(@re", re, ")");
}
template<class Instruction>
static void printLocationAndOp(PrintStream& out, int location, const Instruction*&, const char* op)
{
out.printf("[%4d] %-17s ", location, op);
}
static ALWAYS_INLINE bool isConstantRegisterIndex(int index)
{
return index >= FirstConstantRegisterIndex;
}
NEVER_INLINE static const char* debugHookName(int debugHookType)
{
switch (static_cast<DebugHookType>(debugHookType)) {
case DidEnterCallFrame:
return "didEnterCallFrame";
case WillLeaveCallFrame:
return "willLeaveCallFrame";
case WillExecuteStatement:
return "willExecuteStatement";
case WillExecuteExpression:
return "willExecuteExpression";
case WillExecuteProgram:
return "willExecuteProgram";
case DidExecuteProgram:
return "didExecuteProgram";
case DidReachBreakpoint:
return "didReachBreakpoint";
}
RELEASE_ASSERT_NOT_REACHED();
return "";
}
template<class Block>
CString BytecodeDumper<Block>::registerName(int r) const
{
if (isConstantRegisterIndex(r))
return constantName(r);
return toCString(VirtualRegister(r));
}
static CString idName(int id0, const Identifier& ident)
{
return toCString(ident.impl(), "(@id", id0, ")");
}
template<class Block>
CString BytecodeDumper<Block>::constantName(int index) const
{
JSValue value = block()->getConstant(index);
return toCString(value, "(", VirtualRegister(index), ")");
}
template<class Block>
void BytecodeDumper<Block>::printUnaryOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op)
{
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
}
template<class Block>
void BytecodeDumper<Block>::printBinaryOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op)
{
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
}
template<class Block>
void BytecodeDumper<Block>::printConditionalJump(PrintStream& out, const typename Block::Instruction*, const typename Block::Instruction*& it, int location, const char* op)
{
int r0 = (++it)->u.operand;
int offset = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %d(->%d)", registerName(r0).data(), offset, location + offset);
}
template<class Block>
void BytecodeDumper<Block>::printCompareJump(PrintStream& out, const typename Block::Instruction*, const typename Block::Instruction*& it, int location, const char* op)
{
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int offset = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
}
template<class Block>
void BytecodeDumper<Block>::printGetByIdOp(PrintStream& out, int location, const typename Block::Instruction*& it)
{
const char* op;
switch (Interpreter::getOpcodeID(*it)) {
case op_get_by_id:
op = "get_by_id";
break;
case op_get_by_id_proto_load:
op = "get_by_id_proto_load";
break;
case op_get_by_id_unset:
op = "get_by_id_unset";
break;
case op_get_array_length:
op = "array_length";
break;
default:
RELEASE_ASSERT_NOT_REACHED();
#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
op = 0;
#endif
}
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
it += 4; // Increment up to the value profiler.
}
static void dumpStructure(PrintStream& out, const char* name, Structure* structure, const Identifier& ident)
{
if (!structure)
return;
out.printf("%s = %p", name, structure);
PropertyOffset offset = structure->getConcurrently(ident.impl());
if (offset != invalidOffset)
out.printf(" (offset = %d)", offset);
}
static void dumpChain(PrintStream& out, StructureChain* chain, const Identifier& ident)
{
out.printf("chain = %p: [", chain);
bool first = true;
for (WriteBarrier<Structure>* currentStructure = chain->head(); *currentStructure; ++currentStructure) {
if (first)
first = false;
else
out.printf(", ");
dumpStructure(out, "struct", currentStructure->get(), ident);
}
out.printf("]");
}
template<class Block>
void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int location, const StubInfoMap& map)
{
const auto* instruction = instructionsBegin() + location;
const Identifier& ident = identifier(instruction[3].u.operand);
UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations.
if (Interpreter::getOpcodeID(instruction[0]) == op_get_array_length)
out.printf(" llint(array_length)");
else if (StructureID structureID = getStructureID(instruction[4])) {
Structure* structure = vm()->heap.structureIDTable().get(structureID);
out.printf(" llint(");
dumpStructure(out, "struct", structure, ident);
out.printf(")");
if (Interpreter::getOpcodeID(instruction[0]) == op_get_by_id_proto_load)
out.printf(" proto(%p)", getPointer(instruction[6]));
}
#if ENABLE(JIT)
if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) {
StructureStubInfo& stubInfo = *stubPtr;
if (stubInfo.resetByGC)
out.print(" (Reset By GC)");
out.printf(" jit(");
Structure* baseStructure = nullptr;
PolymorphicAccess* stub = nullptr;
switch (stubInfo.cacheType) {
case CacheType::GetByIdSelf:
out.printf("self");
baseStructure = stubInfo.u.byIdSelf.baseObjectStructure.get();
break;
case CacheType::Stub:
out.printf("stub");
stub = stubInfo.u.stub;
break;
case CacheType::Unset:
out.printf("unset");
break;
case CacheType::ArrayLength:
out.printf("ArrayLength");
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
if (baseStructure) {
out.printf(", ");
dumpStructure(out, "struct", baseStructure, ident);
}
if (stub)
out.print(", ", *stub);
out.printf(")");
}
#else
UNUSED_PARAM(map);
#endif
}
template<class Block>
void BytecodeDumper<Block>::printPutByIdCacheStatus(PrintStream& out, int location, const StubInfoMap& map)
{
const auto* instruction = instructionsBegin() + location;
const Identifier& ident = identifier(instruction[2].u.operand);
UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations.
out.print(", ", getPutByIdFlags(instruction[8]));
if (StructureID structureID = getStructureID(instruction[4])) {
Structure* structure = vm()->heap.structureIDTable().get(structureID);
out.print(" llint(");
if (StructureID newStructureID = getStructureID(instruction[6])) {
Structure* newStructure = vm()->heap.structureIDTable().get(newStructureID);
dumpStructure(out, "prev", structure, ident);
out.print(", ");
dumpStructure(out, "next", newStructure, ident);
if (StructureChain* chain = getStructureChain(instruction[7])) {
out.print(", ");
dumpChain(out, chain, ident);
}
} else
dumpStructure(out, "struct", structure, ident);
out.print(")");
}
#if ENABLE(JIT)
if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) {
StructureStubInfo& stubInfo = *stubPtr;
if (stubInfo.resetByGC)
out.print(" (Reset By GC)");
out.printf(" jit(");
switch (stubInfo.cacheType) {
case CacheType::PutByIdReplace:
out.print("replace, ");
dumpStructure(out, "struct", stubInfo.u.byIdSelf.baseObjectStructure.get(), ident);
break;
case CacheType::Stub: {
out.print("stub, ", *stubInfo.u.stub);
break;
}
case CacheType::Unset:
out.printf("unset");
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
out.printf(")");
}
#else
UNUSED_PARAM(map);
#endif
}
#if ENABLE(JIT)
template<typename Block>
void BytecodeDumper<Block>::dumpCallLinkStatus(PrintStream&, unsigned, const CallLinkInfoMap&)
{
}
template<>
void BytecodeDumper<CodeBlock>::dumpCallLinkStatus(PrintStream& out, unsigned location, const CallLinkInfoMap& map)
{
if (block()->jitType() != JITCode::FTLJIT)
out.print(" status(", CallLinkStatus::computeFor(block(), location, map), ")");
}
#endif
template<class Block>
void BytecodeDumper<Block>::printCallOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap& map)
{
int dst = (++it)->u.operand;
int func = (++it)->u.operand;
int argCount = (++it)->u.operand;
int registerOffset = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.print(registerName(dst), ", ", registerName(func), ", ", argCount, ", ", registerOffset);
out.print(" (this at ", virtualRegisterForArgument(0, -registerOffset), ")");
if (cacheDumpMode == DumpCaches) {
LLIntCallLinkInfo* callLinkInfo = getCallLinkInfo(it[1]);
if (callLinkInfo->lastSeenCallee) {
JSObject* object = callLinkInfo->lastSeenCallee.get();
if (auto* function = jsDynamicCast<JSFunction*>(*vm(), object))
out.printf(" llint(%p, exec %p)", function, function->executable());
else
out.printf(" llint(%p)", object);
}
#if ENABLE(JIT)
if (CallLinkInfo* info = map.get(CodeOrigin(location))) {
if (info->haveLastSeenCallee()) {
JSObject* object = info->lastSeenCallee();
if (auto* function = jsDynamicCast<JSFunction*>(*vm(), object))
out.printf(" jit(%p, exec %p)", function, function->executable());
else
out.printf(" jit(%p)", object);
}
}
dumpCallLinkStatus(out, location, map);
#else
UNUSED_PARAM(map);
#endif
}
++it;
++it;
dumpArrayProfiling(out, it, hasPrintedProfiling);
dumpValueProfiling(out, it, hasPrintedProfiling);
}
template<class Block>
void BytecodeDumper<Block>::printPutByIdOp(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op)
{
int r0 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, op);
out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data());
it += 5;
}
template<class Block>
void BytecodeDumper<Block>::printLocationOpAndRegisterOperand(PrintStream& out, int location, const typename Block::Instruction*& it, const char* op, int operand)
{
printLocationAndOp(out, location, it, op);
out.printf("%s", registerName(operand).data());
}
template<class Block>
void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
{
int location = it - begin;
bool hasPrintedProfiling = false;
OpcodeID opcode = Interpreter::getOpcodeID(*it);
switch (opcode) {
case op_enter: {
printLocationAndOp(out, location, it, "enter");
break;
}
case op_get_scope: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "get_scope", r0);
break;
}
case op_create_direct_arguments: {
int r0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "create_direct_arguments");
out.printf("%s", registerName(r0).data());
break;
}
case op_create_scoped_arguments: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "create_scoped_arguments");
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break;
}
case op_create_cloned_arguments: {
int r0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "create_cloned_arguments");
out.printf("%s", registerName(r0).data());
break;
}
case op_argument_count: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "argument_count", r0);
break;
}
case op_get_argument: {
int r0 = (++it)->u.operand;
int index = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "argument", r0);
out.printf(", %d", index);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_create_rest: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
unsigned argumentOffset = (++it)->u.unsignedValue;
printLocationAndOp(out, location, it, "create_rest");
out.printf("%s, %s, ", registerName(r0).data(), registerName(r1).data());
out.printf("ArgumentsOffset: %u", argumentOffset);
break;
}
case op_get_rest_length: {
int r0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_rest_length");
out.printf("%s, ", registerName(r0).data());
unsigned argumentOffset = (++it)->u.unsignedValue;
out.printf("ArgumentsOffset: %u", argumentOffset);
break;
}
case op_create_this: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
unsigned inferredInlineCapacity = (++it)->u.operand;
unsigned cachedFunction = (++it)->u.operand;
printLocationAndOp(out, location, it, "create_this");
out.printf("%s, %s, %u, %u", registerName(r0).data(), registerName(r1).data(), inferredInlineCapacity, cachedFunction);
break;
}
case op_to_this: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "to_this", r0);
Structure* structure = getStructure(*(++it));
if (structure)
out.print(", cache(struct = ", RawPointer(structure), ")");
out.print(", ", getToThisStatus(*(++it)));
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_check_tdz: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "op_check_tdz", r0);
break;
}
case op_new_object: {
int r0 = (++it)->u.operand;
unsigned inferredInlineCapacity = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_object");
out.printf("%s, %u", registerName(r0).data(), inferredInlineCapacity);
++it; // Skip object allocation profile.
break;
}
case op_new_array: {
int dst = (++it)->u.operand;
int argv = (++it)->u.operand;
int argc = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_array");
out.printf("%s, %s, %d", registerName(dst).data(), registerName(argv).data(), argc);
++it; // Skip array allocation profile.
break;
}
case op_new_array_with_spread: {
int dst = (++it)->u.operand;
int argv = (++it)->u.operand;
int argc = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_array_with_spread");
out.printf("%s, %s, %d, ", registerName(dst).data(), registerName(argv).data(), argc);
unsigned bitVectorIndex = (++it)->u.unsignedValue;
const BitVector& bitVector = block()->bitVector(bitVectorIndex);
out.print("BitVector:", bitVectorIndex, ":");
for (unsigned i = 0; i < static_cast<unsigned>(argc); i++) {
if (bitVector.get(i))
out.print("1");
else
out.print("0");
}
break;
}
case op_spread: {
int dst = (++it)->u.operand;
int arg = (++it)->u.operand;
printLocationAndOp(out, location, it, "spread");
out.printf("%s, %s", registerName(dst).data(), registerName(arg).data());
break;
}
case op_new_array_with_size: {
int dst = (++it)->u.operand;
int length = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_array_with_size");
out.printf("%s, %s", registerName(dst).data(), registerName(length).data());
++it; // Skip array allocation profile.
break;
}
case op_new_array_buffer: {
int dst = (++it)->u.operand;
int array = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_array_buffer");
out.printf("%s, %s", registerName(dst).data(), registerName(array).data());
++it; // Skip array allocation profile.
break;
}
case op_new_regexp: {
int r0 = (++it)->u.operand;
int re0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_regexp");
out.printf("%s, ", registerName(r0).data());
if (r0 >=0 && r0 < (int)block()->numberOfRegExps())
out.printf("%s", regexpName(re0, block()->regexp(re0)).data());
else
out.printf("bad_regexp(%d)", re0);
break;
}
case op_mov: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "mov");
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break;
}
case op_profile_type: {
int r0 = (++it)->u.operand;
++it;
++it;
++it;
++it;
printLocationAndOp(out, location, it, "op_profile_type");
out.printf("%s", registerName(r0).data());
break;
}
case op_profile_control_flow: {
BasicBlockLocation* basicBlockLocation = getBasicBlockLocation(*(++it));
printLocationAndOp(out, location, it, "profile_control_flow");
if (basicBlockLocation)
out.printf("[%d, %d]", basicBlockLocation->startOffset(), basicBlockLocation->endOffset());
break;
}
case op_not: {
printUnaryOp(out, location, it, "not");
break;
}
case op_eq: {
printBinaryOp(out, location, it, "eq");
break;
}
case op_eq_null: {
printUnaryOp(out, location, it, "eq_null");
break;
}
case op_neq: {
printBinaryOp(out, location, it, "neq");
break;
}
case op_neq_null: {
printUnaryOp(out, location, it, "neq_null");
break;
}
case op_stricteq: {
printBinaryOp(out, location, it, "stricteq");
break;
}
case op_nstricteq: {
printBinaryOp(out, location, it, "nstricteq");
break;
}
case op_less: {
printBinaryOp(out, location, it, "less");
break;
}
case op_lesseq: {
printBinaryOp(out, location, it, "lesseq");
break;
}
case op_greater: {
printBinaryOp(out, location, it, "greater");
break;
}
case op_greatereq: {
printBinaryOp(out, location, it, "greatereq");
break;
}
case op_below: {
printBinaryOp(out, location, it, "below");
break;
}
case op_beloweq: {
printBinaryOp(out, location, it, "beloweq");
break;
}
case op_inc: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "inc", r0);
break;
}
case op_dec: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "dec", r0);
break;
}
case op_to_number: {
printUnaryOp(out, location, it, "to_number");
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_to_string: {
printUnaryOp(out, location, it, "to_string");
break;
}
case op_to_object: {
printUnaryOp(out, location, it, "to_object");
int id0 = (++it)->u.operand;
out.printf(" %s", idName(id0, identifier(id0)).data());
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_negate: {
printUnaryOp(out, location, it, "negate");
++it; // op_negate has an extra operand for the ArithProfile.
break;
}
case op_add: {
printBinaryOp(out, location, it, "add");
++it;
break;
}
case op_mul: {
printBinaryOp(out, location, it, "mul");
++it;
break;
}
case op_div: {
printBinaryOp(out, location, it, "div");
++it;
break;
}
case op_mod: {
printBinaryOp(out, location, it, "mod");
break;
}
case op_pow: {
printBinaryOp(out, location, it, "pow");
break;
}
case op_sub: {
printBinaryOp(out, location, it, "sub");
++it;
break;
}
case op_lshift: {
printBinaryOp(out, location, it, "lshift");
break;
}
case op_rshift: {
printBinaryOp(out, location, it, "rshift");
break;
}
case op_urshift: {
printBinaryOp(out, location, it, "urshift");
break;
}
case op_bitand: {
printBinaryOp(out, location, it, "bitand");
++it;
break;
}
case op_bitxor: {
printBinaryOp(out, location, it, "bitxor");
++it;
break;
}
case op_bitor: {
printBinaryOp(out, location, it, "bitor");
++it;
break;
}
case op_overrides_has_instance: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "overrides_has_instance");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
break;
}
case op_instanceof: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "instanceof");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
break;
}
case op_instanceof_custom: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int r3 = (++it)->u.operand;
printLocationAndOp(out, location, it, "instanceof_custom");
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data());
break;
}
case op_unsigned: {
printUnaryOp(out, location, it, "unsigned");
break;
}
case op_typeof: {
printUnaryOp(out, location, it, "typeof");
break;
}
case op_is_empty: {
printUnaryOp(out, location, it, "is_empty");
break;
}
case op_is_undefined: {
printUnaryOp(out, location, it, "is_undefined");
break;
}
case op_is_boolean: {
printUnaryOp(out, location, it, "is_boolean");
break;
}
case op_is_number: {
printUnaryOp(out, location, it, "is_number");
break;
}
case op_is_cell_with_type: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int type = (++it)->u.operand;
printLocationAndOp(out, location, it, "is_cell_with_type");
out.printf("%s, %s, %d", registerName(r0).data(), registerName(r1).data(), type);
break;
}
case op_is_object: {
printUnaryOp(out, location, it, "is_object");
break;
}
case op_is_object_or_null: {
printUnaryOp(out, location, it, "is_object_or_null");
break;
}
case op_is_function: {
printUnaryOp(out, location, it, "is_function");
break;
}
case op_in: {
printBinaryOp(out, location, it, "in");
dumpArrayProfiling(out, it, hasPrintedProfiling);
break;
}
case op_try_get_by_id: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "try_get_by_id");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_get_by_id_direct: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_by_id_direct");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
it += 2; // Increment up to the value profiler.
printGetByIdCacheStatus(out, location, stubInfos);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_get_by_id:
case op_get_by_id_proto_load:
case op_get_by_id_unset:
case op_get_array_length: {
printGetByIdOp(out, location, it);
printGetByIdCacheStatus(out, location, stubInfos);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_get_by_id_with_this: {
printLocationAndOp(out, location, it, "get_by_id_with_this");
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int id0 = (++it)->u.operand;
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), idName(id0, identifier(id0)).data());
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_get_by_val_with_this: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int r3 = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_by_val_with_this");
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data());
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_put_by_id: {
printPutByIdOp(out, location, it, "put_by_id");
printPutByIdCacheStatus(out, location, stubInfos);
break;
}
case op_put_by_id_with_this: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_by_id_with_this");
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data(), registerName(r2).data());
break;
}
case op_put_by_val_with_this: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int r3 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_by_val_with_this");
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data());
break;
}
case op_put_getter_by_id: {
int r0 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int n0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_getter_by_id");
out.printf("%s, %s, %d, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), n0, registerName(r1).data());
break;
}
case op_put_setter_by_id: {
int r0 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int n0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_setter_by_id");
out.printf("%s, %s, %d, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), n0, registerName(r1).data());
break;
}
case op_put_getter_setter_by_id: {
int r0 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int n0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_getter_setter_by_id");
out.printf("%s, %s, %d, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), n0, registerName(r1).data(), registerName(r2).data());
break;
}
case op_put_getter_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int n0 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_getter_by_val");
out.printf("%s, %s, %d, %s", registerName(r0).data(), registerName(r1).data(), n0, registerName(r2).data());
break;
}
case op_put_setter_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int n0 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_setter_by_val");
out.printf("%s, %s, %d, %s", registerName(r0).data(), registerName(r1).data(), n0, registerName(r2).data());
break;
}
case op_define_data_property: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int r3 = (++it)->u.operand;
printLocationAndOp(out, location, it, "define_data_property");
out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data());
break;
}
case op_define_accessor_property: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int r3 = (++it)->u.operand;
int r4 = (++it)->u.operand;
printLocationAndOp(out, location, it, "define_accessor_property");
out.printf("%s, %s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data(), registerName(r4).data());
break;
}
case op_del_by_id: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "del_by_id");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
break;
}
case op_get_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_by_val");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
dumpArrayProfiling(out, it, hasPrintedProfiling);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_put_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_by_val");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
dumpArrayProfiling(out, it, hasPrintedProfiling);
break;
}
case op_put_by_val_direct: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_by_val_direct");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
dumpArrayProfiling(out, it, hasPrintedProfiling);
break;
}
case op_del_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
printLocationAndOp(out, location, it, "del_by_val");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
break;
}
case op_jmp: {
int offset = (++it)->u.operand;
printLocationAndOp(out, location, it, "jmp");
out.printf("%d(->%d)", offset, location + offset);
break;
}
case op_jtrue: {
printConditionalJump(out, begin, it, location, "jtrue");
break;
}
case op_jfalse: {
printConditionalJump(out, begin, it, location, "jfalse");
break;
}
case op_jeq_null: {
printConditionalJump(out, begin, it, location, "jeq_null");
break;
}
case op_jneq_null: {
printConditionalJump(out, begin, it, location, "jneq_null");
break;
}
case op_jneq_ptr: {
int r0 = (++it)->u.operand;
Special::Pointer pointer = getSpecialPointer(*(++it));
int offset = (++it)->u.operand;
printLocationAndOp(out, location, it, "jneq_ptr");
out.printf("%s, %d (%p), %d(->%d)", registerName(r0).data(), pointer, actualPointerFor(pointer), offset, location + offset);
++it;
break;
}
case op_jless: {
printCompareJump(out, begin, it, location, "jless");
break;
}
case op_jlesseq: {
printCompareJump(out, begin, it, location, "jlesseq");
break;
}
case op_jgreater: {
printCompareJump(out, begin, it, location, "jgreater");
break;
}
case op_jgreatereq: {
printCompareJump(out, begin, it, location, "jgreatereq");
break;
}
case op_jnless: {
printCompareJump(out, begin, it, location, "jnless");
break;
}
case op_jnlesseq: {
printCompareJump(out, begin, it, location, "jnlesseq");
break;
}
case op_jngreater: {
printCompareJump(out, begin, it, location, "jngreater");
break;
}
case op_jngreatereq: {
printCompareJump(out, begin, it, location, "jngreatereq");
break;
}
case op_jeq: {
printCompareJump(out, begin, it, location, "jeq");
break;
}
case op_jneq: {
printCompareJump(out, begin, it, location, "jneq");
break;
}
case op_jstricteq: {
printCompareJump(out, begin, it, location, "jstricteq");
break;
}
case op_jnstricteq: {
printCompareJump(out, begin, it, location, "jnstricteq");
break;
}
case op_jbelow: {
printCompareJump(out, begin, it, location, "jbelow");
break;
}
case op_jbeloweq: {
printCompareJump(out, begin, it, location, "jbeloweq");
break;
}
case op_loop_hint: {
printLocationAndOp(out, location, it, "loop_hint");
break;
}
case op_check_traps: {
printLocationAndOp(out, location, it, "check_traps");
break;
}
case op_nop: {
printLocationAndOp(out, location, it, "nop");
break;
}
case op_super_sampler_begin: {
printLocationAndOp(out, location, it, "super_sampler_begin");
break;
}
case op_super_sampler_end: {
printLocationAndOp(out, location, it, "super_sampler_end");
break;
}
case op_log_shadow_chicken_prologue: {
int r0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "log_shadow_chicken_prologue");
out.printf("%s", registerName(r0).data());
break;
}
case op_log_shadow_chicken_tail: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "log_shadow_chicken_tail");
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break;
}
case op_switch_imm: {
int tableIndex = (++it)->u.operand;
int defaultTarget = (++it)->u.operand;
int scrutineeRegister = (++it)->u.operand;
printLocationAndOp(out, location, it, "switch_imm");
out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data());
break;
}
case op_switch_char: {
int tableIndex = (++it)->u.operand;
int defaultTarget = (++it)->u.operand;
int scrutineeRegister = (++it)->u.operand;
printLocationAndOp(out, location, it, "switch_char");
out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data());
break;
}
case op_switch_string: {
int tableIndex = (++it)->u.operand;
int defaultTarget = (++it)->u.operand;
int scrutineeRegister = (++it)->u.operand;
printLocationAndOp(out, location, it, "switch_string");
out.printf("%d, %d(->%d), %s", tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).data());
break;
}
case op_new_func: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_func");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_generator_func: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_generator_func");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_async_func: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_async_func");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_async_generator_func: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_async_generator_func");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_func_exp: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_func_exp");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_generator_func_exp: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_generator_func_exp");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_async_func_exp: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "new_async_func_exp");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_new_async_generator_func_exp: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int f0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "op_new_async_generator_func_exp");
out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0);
break;
}
case op_set_function_name: {
int funcReg = (++it)->u.operand;
int nameReg = (++it)->u.operand;
printLocationAndOp(out, location, it, "set_function_name");
out.printf("%s, %s", registerName(funcReg).data(), registerName(nameReg).data());
break;
}
case op_call: {
printCallOp(out, location, it, "call", DumpCaches, hasPrintedProfiling, callLinkInfos);
break;
}
case op_tail_call: {
printCallOp(out, location, it, "tail_call", DumpCaches, hasPrintedProfiling, callLinkInfos);
break;
}
case op_call_eval: {
printCallOp(out, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling, callLinkInfos);
break;
}
case op_construct_varargs:
case op_call_varargs:
case op_tail_call_varargs:
case op_tail_call_forward_arguments: {
int result = (++it)->u.operand;
int callee = (++it)->u.operand;
int thisValue = (++it)->u.operand;
int arguments = (++it)->u.operand;
int firstFreeRegister = (++it)->u.operand;
int varArgOffset = (++it)->u.operand;
++it;
const char* opName;
if (opcode == op_call_varargs)
opName = "call_varargs";
else if (opcode == op_construct_varargs)
opName = "construct_varargs";
else if (opcode == op_tail_call_varargs)
opName = "tail_call_varargs";
else if (opcode == op_tail_call_forward_arguments)
opName = "tail_call_forward_arguments";
else
RELEASE_ASSERT_NOT_REACHED();
printLocationAndOp(out, location, it, opName);
out.printf("%s, %s, %s, %s, %d, %d", registerName(result).data(), registerName(callee).data(), registerName(thisValue).data(), registerName(arguments).data(), firstFreeRegister, varArgOffset);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_ret: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "ret", r0);
break;
}
case op_construct: {
printCallOp(out, location, it, "construct", DumpCaches, hasPrintedProfiling, callLinkInfos);
break;
}
case op_strcat: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int count = (++it)->u.operand;
printLocationAndOp(out, location, it, "strcat");
out.printf("%s, %s, %d", registerName(r0).data(), registerName(r1).data(), count);
break;
}
case op_to_primitive: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "to_primitive");
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break;
}
case op_get_enumerable_length: {
int dst = it[1].u.operand;
int base = it[2].u.operand;
printLocationAndOp(out, location, it, "op_get_enumerable_length");
out.printf("%s, %s", registerName(dst).data(), registerName(base).data());
it += OPCODE_LENGTH(op_get_enumerable_length) - 1;
break;
}
case op_has_indexed_property: {
int dst = (++it)->u.operand;
int base = (++it)->u.operand;
int propertyName = (++it)->u.operand;
printLocationAndOp(out, location, it, "op_has_indexed_property");
out.printf("%s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data());
dumpArrayProfiling(out, it, hasPrintedProfiling);
break;
}
case op_has_structure_property: {
int dst = it[1].u.operand;
int base = it[2].u.operand;
int propertyName = it[3].u.operand;
int enumerator = it[4].u.operand;
printLocationAndOp(out, location, it, "op_has_structure_property");
out.printf("%s, %s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data(), registerName(enumerator).data());
it += OPCODE_LENGTH(op_has_structure_property) - 1;
break;
}
case op_has_generic_property: {
int dst = it[1].u.operand;
int base = it[2].u.operand;
int propertyName = it[3].u.operand;
printLocationAndOp(out, location, it, "op_has_generic_property");
out.printf("%s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data());
it += OPCODE_LENGTH(op_has_generic_property) - 1;
break;
}
case op_get_direct_pname: {
int dst = (++it)->u.operand;
int base = (++it)->u.operand;
int propertyName = (++it)->u.operand;
int index = (++it)->u.operand;
int enumerator = (++it)->u.operand;
printLocationAndOp(out, location, it, "op_get_direct_pname");
out.printf("%s, %s, %s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data(), registerName(index).data(), registerName(enumerator).data());
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_get_property_enumerator: {
int dst = it[1].u.operand;
int base = it[2].u.operand;
printLocationAndOp(out, location, it, "op_get_property_enumerator");
out.printf("%s, %s", registerName(dst).data(), registerName(base).data());
it += OPCODE_LENGTH(op_get_property_enumerator) - 1;
break;
}
case op_enumerator_structure_pname: {
int dst = it[1].u.operand;
int enumerator = it[2].u.operand;
int index = it[3].u.operand;
printLocationAndOp(out, location, it, "op_enumerator_structure_pname");
out.printf("%s, %s, %s", registerName(dst).data(), registerName(enumerator).data(), registerName(index).data());
it += OPCODE_LENGTH(op_enumerator_structure_pname) - 1;
break;
}
case op_enumerator_generic_pname: {
int dst = it[1].u.operand;
int enumerator = it[2].u.operand;
int index = it[3].u.operand;
printLocationAndOp(out, location, it, "op_enumerator_generic_pname");
out.printf("%s, %s, %s", registerName(dst).data(), registerName(enumerator).data(), registerName(index).data());
it += OPCODE_LENGTH(op_enumerator_generic_pname) - 1;
break;
}
case op_to_index_string: {
int dst = it[1].u.operand;
int index = it[2].u.operand;
printLocationAndOp(out, location, it, "op_to_index_string");
out.printf("%s, %s", registerName(dst).data(), registerName(index).data());
it += OPCODE_LENGTH(op_to_index_string) - 1;
break;
}
case op_push_with_scope: {
int dst = (++it)->u.operand;
int newScope = (++it)->u.operand;
int currentScope = (++it)->u.operand;
printLocationAndOp(out, location, it, "push_with_scope");
out.printf("%s, %s, %s", registerName(dst).data(), registerName(newScope).data(), registerName(currentScope).data());
break;
}
case op_get_parent_scope: {
int dst = (++it)->u.operand;
int parentScope = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_parent_scope");
out.printf("%s, %s", registerName(dst).data(), registerName(parentScope).data());
break;
}
case op_create_lexical_environment: {
int dst = (++it)->u.operand;
int scope = (++it)->u.operand;
int symbolTable = (++it)->u.operand;
int initialValue = (++it)->u.operand;
printLocationAndOp(out, location, it, "create_lexical_environment");
out.printf("%s, %s, %s, %s",
registerName(dst).data(), registerName(scope).data(), registerName(symbolTable).data(), registerName(initialValue).data());
break;
}
case op_catch: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
void* pointer = getPointer(*(++it));
printLocationAndOp(out, location, it, "catch");
out.printf("%s, %s, %p", registerName(r0).data(), registerName(r1).data(), pointer);
break;
}
case op_throw: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "throw", r0);
break;
}
case op_throw_static_error: {
int r0 = (++it)->u.operand;
ErrorType k1 = static_cast<ErrorType>((++it)->u.unsignedValue);
printLocationAndOp(out, location, it, "throw_static_error");
out.printf("%s, ", registerName(r0).data());
out.print(k1);
break;
}
case op_debug: {
int debugHookType = (++it)->u.operand;
int hasBreakpointFlag = (++it)->u.operand;
printLocationAndOp(out, location, it, "debug");
out.printf("%s, %d", debugHookName(debugHookType), hasBreakpointFlag);
break;
}
case op_identity_with_profile: {
int r0 = (++it)->u.operand;
++it; // Profile top half
++it; // Profile bottom half
printLocationAndOp(out, location, it, "identity_with_profile");
out.printf("%s", registerName(r0).data());
break;
}
case op_unreachable: {
printLocationAndOp(out, location, it, "unreachable");
break;
}
case op_end: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, location, it, "end", r0);
break;
}
case op_resolve_scope_for_hoisting_func_decl_in_eval: {
int r0 = (++it)->u.operand;
int scope = (++it)->u.operand;
int id0 = (++it)->u.operand;
printLocationAndOp(out, location, it, "resolve_scope_for_hoisting_func_decl_in_eval");
out.printf("%s, %s, %s", registerName(r0).data(), registerName(scope).data(), idName(id0, identifier(id0)).data());
break;
}
case op_resolve_scope: {
int r0 = (++it)->u.operand;
int scope = (++it)->u.operand;
int id0 = (++it)->u.operand;
ResolveType resolveType = static_cast<ResolveType>((++it)->u.operand);
int depth = (++it)->u.operand;
void* pointer = getPointer(*(++it));
printLocationAndOp(out, location, it, "resolve_scope");
out.printf("%s, %s, %s, <%s>, %d, %p", registerName(r0).data(), registerName(scope).data(), idName(id0, identifier(id0)).data(), resolveTypeName(resolveType), depth, pointer);
break;
}
case op_get_from_scope: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int id0 = (++it)->u.operand;
GetPutInfo getPutInfo = GetPutInfo((++it)->u.operand);
++it; // Structure
int operand = (++it)->u.operand; // Operand
printLocationAndOp(out, location, it, "get_from_scope");
out.print(registerName(r0), ", ", registerName(r1));
if (static_cast<unsigned>(id0) == UINT_MAX)
out.print(", anonymous");
else
out.print(", ", idName(id0, identifier(id0)));
out.print(", ", getPutInfo.operand(), "<", resolveModeName(getPutInfo.resolveMode()), "|", resolveTypeName(getPutInfo.resolveType()), "|", initializationModeName(getPutInfo.initializationMode()), ">, ", operand);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_put_to_scope: {
int r0 = (++it)->u.operand;
int id0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
GetPutInfo getPutInfo = GetPutInfo((++it)->u.operand);
++it; // Structure
int operand = (++it)->u.operand; // Operand
printLocationAndOp(out, location, it, "put_to_scope");
out.print(registerName(r0));
if (static_cast<unsigned>(id0) == UINT_MAX)
out.print(", anonymous");
else
out.print(", ", idName(id0, identifier(id0)));
out.print(", ", registerName(r1), ", ", getPutInfo.operand(), "<", resolveModeName(getPutInfo.resolveMode()), "|", resolveTypeName(getPutInfo.resolveType()), "|", initializationModeName(getPutInfo.initializationMode()), ">, <structure>, ", operand);
break;
}
case op_get_from_arguments: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int offset = (++it)->u.operand;
printLocationAndOp(out, location, it, "get_from_arguments");
out.printf("%s, %s, %d", registerName(r0).data(), registerName(r1).data(), offset);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
}
case op_put_to_arguments: {
int r0 = (++it)->u.operand;
int offset = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "put_to_arguments");
out.printf("%s, %d, %s", registerName(r0).data(), offset, registerName(r1).data());
break;
}
case op_yield: {
int r0 = (++it)->u.operand;
unsigned yieldPoint = (++it)->u.unsignedValue;
int r1 = (++it)->u.operand;
printLocationAndOp(out, location, it, "yield");
out.printf("%s, %u, %s", registerName(r0).data(), yieldPoint, registerName(r1).data());
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
}
dumpProfilesForBytecodeOffset(out, location, hasPrintedProfiling);
out.print("\n");
}
template<class Block>
void BytecodeDumper<Block>::dumpBytecode(Block* block, PrintStream& out, const typename Block::Instruction* begin, const typename Block::Instruction*& it, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
{
BytecodeDumper dumper(block, begin);
dumper.dumpBytecode(out, begin, it, stubInfos, callLinkInfos);
}
template<class Block>
void BytecodeDumper<Block>::dumpIdentifiers(PrintStream& out)
{
if (size_t count = block()->numberOfIdentifiers()) {
out.printf("\nIdentifiers:\n");
size_t i = 0;
do {
out.printf(" id%u = %s\n", static_cast<unsigned>(i), identifier(i).string().utf8().data());
++i;
} while (i != count);
}
}
template<class Block>
void BytecodeDumper<Block>::dumpConstants(PrintStream& out)
{
if (!block()->constantRegisters().isEmpty()) {
out.printf("\nConstants:\n");
size_t i = 0;
for (const auto& constant : block()->constantRegisters()) {
const char* sourceCodeRepresentationDescription = nullptr;
switch (block()->constantsSourceCodeRepresentation()[i]) {
case SourceCodeRepresentation::Double:
sourceCodeRepresentationDescription = ": in source as double";
break;
case SourceCodeRepresentation::Integer:
sourceCodeRepresentationDescription = ": in source as integer";
break;
case SourceCodeRepresentation::Other:
sourceCodeRepresentationDescription = "";
break;
}
out.printf(" k%u = %s%s\n", static_cast<unsigned>(i), toCString(constant.get()).data(), sourceCodeRepresentationDescription);
++i;
}
}
}
template<class Block>
void BytecodeDumper<Block>::dumpRegExps(PrintStream& out)
{
if (size_t count = block()->numberOfRegExps()) {
out.printf("\nm_regexps:\n");
size_t i = 0;
do {
out.printf(" re%u = %s\n", static_cast<unsigned>(i), regexpToSourceString(block()->regexp(i)).data());
++i;
} while (i < count);
}
}
template<class Block>
void BytecodeDumper<Block>::dumpExceptionHandlers(PrintStream& out)
{
if (unsigned count = block()->numberOfExceptionHandlers()) {
out.printf("\nException Handlers:\n");
unsigned i = 0;
do {
const auto& handler = block()->exceptionHandler(i);
out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] } %s\n", i + 1, handler.start, handler.end, handler.target, handler.typeName());
++i;
} while (i < count);
}
}
template<class Block>
void BytecodeDumper<Block>::dumpSwitchJumpTables(PrintStream& out)
{
if (unsigned count = block()->numberOfSwitchJumpTables()) {
out.printf("Switch Jump Tables:\n");
unsigned i = 0;
do {
out.printf(" %1d = {\n", i);
const auto& switchJumpTable = block()->switchJumpTable(i);
int entry = 0;
auto end = switchJumpTable.branchOffsets.end();
for (auto iter = switchJumpTable.branchOffsets.begin(); iter != end; ++iter, ++entry) {
if (!*iter)
continue;
out.printf("\t\t%4d => %04d\n", entry + switchJumpTable.min, *iter);
}
out.printf(" }\n");
++i;
} while (i < count);
}
}
template<class Block>
void BytecodeDumper<Block>::dumpStringSwitchJumpTables(PrintStream& out)
{
if (unsigned count = block()->numberOfStringSwitchJumpTables()) {
out.printf("\nString Switch Jump Tables:\n");
unsigned i = 0;
do {
out.printf(" %1d = {\n", i);
const auto& stringSwitchJumpTable = block()->stringSwitchJumpTable(i);
auto end = stringSwitchJumpTable.offsetTable.end();
for (auto iter = stringSwitchJumpTable.offsetTable.begin(); iter != end; ++iter)
out.printf("\t\t\"%s\" => %04d\n", iter->key->utf8().data(), iter->value.branchOffset);
out.printf(" }\n");
++i;
} while (i < count);
}
}
template<class Block>
void BytecodeDumper<Block>::dumpBlock(Block* block, const typename Block::UnpackedInstructions& instructions, PrintStream& out, const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos)
{
size_t instructionCount = 0;
for (size_t i = 0; i < instructions.size(); i += opcodeLengths[Interpreter::getOpcodeID(instructions[i])])
++instructionCount;
out.print(*block);
out.printf(
": %lu m_instructions; %lu bytes; %d parameter(s); %d callee register(s); %d variable(s)",
static_cast<unsigned long>(instructions.size()),
static_cast<unsigned long>(instructions.size() * sizeof(Instruction)),
block->numParameters(), block->numCalleeLocals(), block->m_numVars);
out.print("; scope at ", block->scopeRegister());
out.printf("\n");
const auto* begin = instructions.begin();
const auto* end = instructions.end();
BytecodeDumper<Block> dumper(block, begin);
for (const auto* it = begin; it != end; ++it)
dumper.dumpBytecode(out, begin, it, stubInfos, callLinkInfos);
dumper.dumpIdentifiers(out);
dumper.dumpConstants(out);
dumper.dumpRegExps(out);
dumper.dumpExceptionHandlers(out);
dumper.dumpSwitchJumpTables(out);
dumper.dumpStringSwitchJumpTables(out);
out.printf("\n");
}
template class BytecodeDumper<UnlinkedCodeBlock>;
template class BytecodeDumper<CodeBlock>;
}