1/*
2 * Copyright (C) 2017 Yusuke Suzuki <[email protected]>
3 * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "BytecodeDumper.h"
29
30#include "ArithProfile.h"
31#include "BytecodeStructs.h"
32#include "CallLinkStatus.h"
33#include "CodeBlock.h"
34#include "Error.h"
35#include "HeapInlines.h"
36#include "InterpreterInlines.h"
37#include "PolymorphicAccess.h"
38#include "PutByIdFlags.h"
39#include "StructureInlines.h"
40#include "ToThisStatus.h"
41#include "UnlinkedCodeBlock.h"
42#include "UnlinkedMetadataTableInlines.h"
43
44namespace JSC {
45
46template<class Block>
47VM* BytecodeDumper<Block>::vm() const
48{
49 return block()->vm();
50}
51
52template<class Block>
53const Identifier& BytecodeDumper<Block>::identifier(int index) const
54{
55 return block()->identifier(index);
56}
57
58static ALWAYS_INLINE bool isConstantRegisterIndex(int index)
59{
60 return index >= FirstConstantRegisterIndex;
61}
62
63template<class Block>
64CString BytecodeDumper<Block>::registerName(int r) const
65{
66 if (isConstantRegisterIndex(r))
67 return constantName(r);
68
69 return toCString(VirtualRegister(r));
70}
71
72template<class Block>
73CString BytecodeDumper<Block>::constantName(int index) const
74{
75 JSValue value = block()->getConstant(index);
76 return toCString(value, "(", VirtualRegister(index), ")");
77}
78
79template<class Block>
80void BytecodeDumper<Block>::printLocationAndOp(InstructionStream::Offset location, const char* op)
81{
82 m_currentLocation = location;
83 m_out.printf("[%4u] %-18s ", location, op);
84}
85
86template<class Block>
87void BytecodeDumper<Block>::dumpBytecode(const InstructionStream::Ref& it, const ICStatusMap&)
88{
89 ::JSC::dumpBytecode(this, it.offset(), it.ptr());
90 m_out.print("\n");
91}
92
93template<class Block>
94void BytecodeDumper<Block>::dumpBytecode(Block* block, PrintStream& out, const InstructionStream::Ref& it, const ICStatusMap& statusMap)
95{
96 BytecodeDumper dumper(block, out);
97 dumper.dumpBytecode(it, statusMap);
98}
99
100template<class Block>
101void BytecodeDumper<Block>::dumpIdentifiers()
102{
103 if (size_t count = block()->numberOfIdentifiers()) {
104 m_out.printf("\nIdentifiers:\n");
105 size_t i = 0;
106 do {
107 m_out.print(" id", static_cast<unsigned>(i), " = ", identifier(i), "\n");
108 ++i;
109 } while (i != count);
110 }
111}
112
113template<class Block>
114void BytecodeDumper<Block>::dumpConstants()
115{
116 if (!block()->constantRegisters().isEmpty()) {
117 m_out.printf("\nConstants:\n");
118 size_t i = 0;
119 for (const auto& constant : block()->constantRegisters()) {
120 const char* sourceCodeRepresentationDescription = nullptr;
121 switch (block()->constantsSourceCodeRepresentation()[i]) {
122 case SourceCodeRepresentation::Double:
123 sourceCodeRepresentationDescription = ": in source as double";
124 break;
125 case SourceCodeRepresentation::Integer:
126 sourceCodeRepresentationDescription = ": in source as integer";
127 break;
128 case SourceCodeRepresentation::Other:
129 sourceCodeRepresentationDescription = "";
130 break;
131 }
132 m_out.printf(" k%u = %s%s\n", static_cast<unsigned>(i), toCString(constant.get()).data(), sourceCodeRepresentationDescription);
133 ++i;
134 }
135 }
136}
137
138template<class Block>
139void BytecodeDumper<Block>::dumpExceptionHandlers()
140{
141 if (unsigned count = block()->numberOfExceptionHandlers()) {
142 m_out.printf("\nException Handlers:\n");
143 unsigned i = 0;
144 do {
145 const auto& handler = block()->exceptionHandler(i);
146 m_out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] } %s\n", i + 1, handler.start, handler.end, handler.target, handler.typeName());
147 ++i;
148 } while (i < count);
149 }
150}
151
152template<class Block>
153void BytecodeDumper<Block>::dumpSwitchJumpTables()
154{
155 if (unsigned count = block()->numberOfSwitchJumpTables()) {
156 m_out.printf("Switch Jump Tables:\n");
157 unsigned i = 0;
158 do {
159 m_out.printf(" %1d = {\n", i);
160 const auto& switchJumpTable = block()->switchJumpTable(i);
161 int entry = 0;
162 auto end = switchJumpTable.branchOffsets.end();
163 for (auto iter = switchJumpTable.branchOffsets.begin(); iter != end; ++iter, ++entry) {
164 if (!*iter)
165 continue;
166 m_out.printf("\t\t%4d => %04d\n", entry + switchJumpTable.min, *iter);
167 }
168 m_out.printf(" }\n");
169 ++i;
170 } while (i < count);
171 }
172}
173
174template<class Block>
175void BytecodeDumper<Block>::dumpStringSwitchJumpTables()
176{
177 if (unsigned count = block()->numberOfStringSwitchJumpTables()) {
178 m_out.printf("\nString Switch Jump Tables:\n");
179 unsigned i = 0;
180 do {
181 m_out.printf(" %1d = {\n", i);
182 const auto& stringSwitchJumpTable = block()->stringSwitchJumpTable(i);
183 auto end = stringSwitchJumpTable.offsetTable.end();
184 for (auto iter = stringSwitchJumpTable.offsetTable.begin(); iter != end; ++iter)
185 m_out.printf("\t\t\"%s\" => %04d\n", iter->key->utf8().data(), iter->value.branchOffset);
186 m_out.printf(" }\n");
187 ++i;
188 } while (i < count);
189 }
190}
191
192template<class Block>
193void BytecodeDumper<Block>::dumpBlock(Block* block, const InstructionStream& instructions, PrintStream& out, const ICStatusMap& statusMap)
194{
195 size_t instructionCount = 0;
196 size_t wide16InstructionCount = 0;
197 size_t wide32InstructionCount = 0;
198 size_t instructionWithMetadataCount = 0;
199
200 for (const auto& instruction : instructions) {
201 if (instruction->isWide16())
202 ++wide16InstructionCount;
203 else if (instruction->isWide32())
204 ++wide32InstructionCount;
205 if (instruction->hasMetadata())
206 ++instructionWithMetadataCount;
207 ++instructionCount;
208 }
209
210 out.print(*block);
211 out.printf(
212 ": %lu instructions (%lu 16-bit instructions, %lu 32-bit instructions, %lu instructions with metadata); %lu bytes (%lu metadata bytes); %d parameter(s); %d callee register(s); %d variable(s)",
213 static_cast<unsigned long>(instructionCount),
214 static_cast<unsigned long>(wide16InstructionCount),
215 static_cast<unsigned long>(wide32InstructionCount),
216 static_cast<unsigned long>(instructionWithMetadataCount),
217 static_cast<unsigned long>(instructions.sizeInBytes() + block->metadataSizeInBytes()),
218 static_cast<unsigned long>(block->metadataSizeInBytes()),
219 block->numParameters(), block->numCalleeLocals(), block->numVars());
220 out.print("; scope at ", block->scopeRegister());
221 out.printf("\n");
222
223 BytecodeDumper<Block> dumper(block, out);
224 for (const auto& it : instructions)
225 dumper.dumpBytecode(it, statusMap);
226
227 dumper.dumpIdentifiers();
228 dumper.dumpConstants();
229 dumper.dumpExceptionHandlers();
230 dumper.dumpSwitchJumpTables();
231 dumper.dumpStringSwitchJumpTables();
232
233 out.printf("\n");
234}
235
236template class BytecodeDumper<UnlinkedCodeBlock>;
237template class BytecodeDumper<CodeBlock>;
238
239}
240