1/*
2 * Copyright (C) 2017 Yusuke Suzuki <[email protected]>
3 * Copyright (C) 2017-2019 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 "B3Type.h"
32#include "BytecodeGenerator.h"
33#include "BytecodeStructs.h"
34#include "CallLinkStatus.h"
35#include "CodeBlock.h"
36#include "Error.h"
37#include "HeapInlines.h"
38#include "InterpreterInlines.h"
39#include "PolymorphicAccess.h"
40#include "PutByIdFlags.h"
41#include "StructureInlines.h"
42#include "ToThisStatus.h"
43#include "UnlinkedCodeBlock.h"
44#include "UnlinkedMetadataTableInlines.h"
45#include "WasmFunctionCodeBlock.h"
46#include "WasmGeneratorTraits.h"
47#include "WasmModuleInformation.h"
48#include "WasmOps.h"
49#include "WasmSignatureInlines.h"
50
51namespace JSC {
52
53static ALWAYS_INLINE bool isConstantRegisterIndex(int index)
54{
55 return index >= FirstConstantRegisterIndex;
56}
57
58template<class Block>
59CString BytecodeDumper<Block>::registerName(int r) const
60{
61 if (isConstantRegisterIndex(r))
62 return constantName(r);
63
64 return toCString(VirtualRegister(r));
65}
66
67template<class Block>
68CString BytecodeDumper<Block>::constantName(int index) const
69{
70 auto value = block()->getConstant(index);
71 return toCString(value, "(", VirtualRegister(index), ")");
72}
73
74template<class Block>
75void BytecodeDumper<Block>::printLocationAndOp(InstructionStream::Offset location, const char* op)
76{
77 m_currentLocation = location;
78 m_out.printf("[%4u] %-18s ", location, op);
79}
80
81template<class Block>
82void BytecodeDumper<Block>::dumpValue(VirtualRegister reg)
83{
84 m_out.printf("%s", registerName(reg.offset()).data());
85}
86
87template<class Block>
88void BytecodeDumper<Block>::dumpBytecode(const InstructionStream::Ref& it, const ICStatusMap&)
89{
90 ::JSC::dumpBytecode(this, it.offset(), it.ptr());
91 this->m_out.print("\n");
92}
93
94template<class Block>
95void BytecodeDumper<Block>::dumpBytecode(Block* block, PrintStream& out, const InstructionStream::Ref& it, const ICStatusMap& statusMap)
96{
97 BytecodeDumper dumper(block, out);
98 dumper.dumpBytecode(it, statusMap);
99}
100
101template<class Block>
102VM& CodeBlockBytecodeDumper<Block>::vm() const
103{
104 return this->block()->vm();
105}
106
107template<class Block>
108const Identifier& CodeBlockBytecodeDumper<Block>::identifier(int index) const
109{
110 return this->block()->identifier(index);
111}
112
113template<class Block>
114void CodeBlockBytecodeDumper<Block>::dumpIdentifiers()
115{
116 if (size_t count = this->block()->numberOfIdentifiers()) {
117 this->m_out.printf("\nIdentifiers:\n");
118 size_t i = 0;
119 do {
120 this->m_out.print(" id", static_cast<unsigned>(i), " = ", identifier(i), "\n");
121 ++i;
122 } while (i != count);
123 }
124}
125
126template<class Block>
127void CodeBlockBytecodeDumper<Block>::dumpConstants()
128{
129 if (!this->block()->constantRegisters().isEmpty()) {
130 this->m_out.printf("\nConstants:\n");
131 size_t i = 0;
132 for (const auto& constant : this->block()->constantRegisters()) {
133 const char* sourceCodeRepresentationDescription = nullptr;
134 switch (this->block()->constantsSourceCodeRepresentation()[i]) {
135 case SourceCodeRepresentation::Double:
136 sourceCodeRepresentationDescription = ": in source as double";
137 break;
138 case SourceCodeRepresentation::Integer:
139 sourceCodeRepresentationDescription = ": in source as integer";
140 break;
141 case SourceCodeRepresentation::Other:
142 sourceCodeRepresentationDescription = "";
143 break;
144 case SourceCodeRepresentation::LinkTimeConstant:
145 sourceCodeRepresentationDescription = ": in source as linke-time-constant";
146 break;
147 }
148 this->m_out.printf(" k%u = %s%s\n", static_cast<unsigned>(i), toCString(constant.get()).data(), sourceCodeRepresentationDescription);
149 ++i;
150 }
151 }
152}
153
154template<class Block>
155void CodeBlockBytecodeDumper<Block>::dumpExceptionHandlers()
156{
157 if (unsigned count = this->block()->numberOfExceptionHandlers()) {
158 this->m_out.printf("\nException Handlers:\n");
159 unsigned i = 0;
160 do {
161 const auto& handler = this->block()->exceptionHandler(i);
162 this->m_out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] } %s\n", i + 1, handler.start, handler.end, handler.target, handler.typeName());
163 ++i;
164 } while (i < count);
165 }
166}
167
168template<class Block>
169void CodeBlockBytecodeDumper<Block>::dumpSwitchJumpTables()
170{
171 if (unsigned count = this->block()->numberOfSwitchJumpTables()) {
172 this->m_out.printf("Switch Jump Tables:\n");
173 unsigned i = 0;
174 do {
175 this->m_out.printf(" %1d = {\n", i);
176 const auto& switchJumpTable = this->block()->switchJumpTable(i);
177 int entry = 0;
178 auto end = switchJumpTable.branchOffsets.end();
179 for (auto iter = switchJumpTable.branchOffsets.begin(); iter != end; ++iter, ++entry) {
180 if (!*iter)
181 continue;
182 this->m_out.printf("\t\t%4d => %04d\n", entry + switchJumpTable.min, *iter);
183 }
184 this->m_out.printf(" }\n");
185 ++i;
186 } while (i < count);
187 }
188}
189
190template<class Block>
191void CodeBlockBytecodeDumper<Block>::dumpStringSwitchJumpTables()
192{
193 if (unsigned count = this->block()->numberOfStringSwitchJumpTables()) {
194 this->m_out.printf("\nString Switch Jump Tables:\n");
195 unsigned i = 0;
196 do {
197 this->m_out.printf(" %1d = {\n", i);
198 const auto& stringSwitchJumpTable = this->block()->stringSwitchJumpTable(i);
199 auto end = stringSwitchJumpTable.offsetTable.end();
200 for (auto iter = stringSwitchJumpTable.offsetTable.begin(); iter != end; ++iter)
201 this->m_out.printf("\t\t\"%s\" => %04d\n", iter->key->utf8().data(), iter->value.branchOffset);
202 this->m_out.printf(" }\n");
203 ++i;
204 } while (i < count);
205 }
206}
207
208template<class Block>
209void CodeBlockBytecodeDumper<Block>::dumpBlock(Block* block, const InstructionStream& instructions, PrintStream& out, const ICStatusMap& statusMap)
210{
211 size_t instructionCount = 0;
212 size_t wide16InstructionCount = 0;
213 size_t wide32InstructionCount = 0;
214 size_t instructionWithMetadataCount = 0;
215
216 for (const auto& instruction : instructions) {
217 if (instruction->isWide16())
218 ++wide16InstructionCount;
219 else if (instruction->isWide32())
220 ++wide32InstructionCount;
221 if (instruction->hasMetadata())
222 ++instructionWithMetadataCount;
223 ++instructionCount;
224 }
225
226 out.print(*block);
227 out.printf(
228 ": %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)",
229 static_cast<unsigned long>(instructionCount),
230 static_cast<unsigned long>(wide16InstructionCount),
231 static_cast<unsigned long>(wide32InstructionCount),
232 static_cast<unsigned long>(instructionWithMetadataCount),
233 static_cast<unsigned long>(instructions.sizeInBytes() + block->metadataSizeInBytes()),
234 static_cast<unsigned long>(block->metadataSizeInBytes()),
235 block->numParameters(), block->numCalleeLocals(), block->numVars());
236 out.print("; scope at ", block->scopeRegister());
237 out.printf("\n");
238
239 CodeBlockBytecodeDumper<Block> dumper(block, out);
240 for (const auto& it : instructions)
241 dumper.dumpBytecode(it, statusMap);
242
243 dumper.dumpIdentifiers();
244 dumper.dumpConstants();
245 dumper.dumpExceptionHandlers();
246 dumper.dumpSwitchJumpTables();
247 dumper.dumpStringSwitchJumpTables();
248
249 out.printf("\n");
250}
251
252template class BytecodeDumper<CodeBlock>;
253template class CodeBlockBytecodeDumper<UnlinkedCodeBlock>;
254template class CodeBlockBytecodeDumper<CodeBlock>;
255
256#if ENABLE(WEBASSEMBLY)
257
258namespace Wasm {
259
260void BytecodeDumper::dumpBlock(FunctionCodeBlock* block, const ModuleInformation& moduleInformation, PrintStream& out)
261{
262 size_t instructionCount = 0;
263 size_t wide16InstructionCount = 0;
264 size_t wide32InstructionCount = 0;
265
266 for (auto it = block->instructions().begin(); it != block->instructions().end(); it += it->size<WasmOpcodeTraits>()) {
267 if (it->isWide16())
268 ++wide16InstructionCount;
269 else if (it->isWide32())
270 ++wide32InstructionCount;
271 ++instructionCount;
272 }
273
274 size_t functionIndexSpace = moduleInformation.importFunctionCount() + block->functionIndex();
275 out.print(makeString(IndexOrName(functionIndexSpace, moduleInformation.nameSection->get(functionIndexSpace))));
276
277 const auto& function = moduleInformation.functions[block->functionIndex()];
278 SignatureIndex signatureIndex = moduleInformation.internalFunctionSignatureIndices[block->functionIndex()];
279 const Signature& signature = SignatureInformation::get(signatureIndex);
280 out.print(" : ", signature, "\n");
281 out.print("wasm size: ", function.data.size(), " bytes\n");
282
283 out.printf(
284 "bytecode: %lu instructions (%lu 16-bit instructions, %lu 32-bit instructions); %lu bytes; %d parameter(s); %d local(s); %d callee register(s)\n",
285 static_cast<unsigned long>(instructionCount),
286 static_cast<unsigned long>(wide16InstructionCount),
287 static_cast<unsigned long>(wide32InstructionCount),
288 static_cast<unsigned long>(block->instructions().sizeInBytes()),
289 block->numArguments(),
290 block->numVars(),
291 block->numCalleeLocals());
292
293 BytecodeDumper dumper(block, out);
294 for (auto it = block->instructions().begin(); it != block->instructions().end(); it += it->size<WasmOpcodeTraits>()) {
295 dumpWasm(&dumper, it.offset(), it.ptr());
296 out.print("\n");
297 }
298
299 dumper.dumpConstants();
300
301 out.printf("\n");
302}
303
304void BytecodeDumper::dumpConstants()
305{
306 FunctionCodeBlock* block = this->block();
307 if (!block->constants().isEmpty()) {
308 this->m_out.printf("\nConstants:\n");
309 unsigned i = 0;
310 for (const auto& constant : block->constants()) {
311 Type type = block->constantTypes()[i];
312 this->m_out.print(" const", i, " : ", type, " = ", formatConstant(type, constant), "\n");
313 ++i;
314 }
315 }
316}
317
318CString BytecodeDumper::constantName(int index) const
319{
320 FunctionCodeBlock* block = this->block();
321 auto value = formatConstant(block->getConstantType(index), block->getConstant(index));
322 return toCString(value, "(", VirtualRegister(index), ")");
323}
324
325CString BytecodeDumper::formatConstant(Type type, uint64_t constant) const
326{
327 switch (type) {
328 case Type::I32:
329 return toCString(static_cast<int32_t>(constant));
330 case Type::I64:
331 return toCString(constant);
332 case Type::F32:
333 return toCString(bitwise_cast<float>(static_cast<int32_t>(constant)));
334 break;
335 case Type::F64:
336 return toCString(bitwise_cast<double>(constant));
337 break;
338 case Type::Anyref:
339 case Type::Funcref:
340 if (JSValue::decode(constant) == jsNull())
341 return "null";
342 return toCString(RawPointer(bitwise_cast<void*>(constant)));
343 default:
344 RELEASE_ASSERT_NOT_REACHED();
345 return "";
346 }
347}
348
349} // namespace Wasm
350
351#endif // ENABLE(WEBASSEMBLY)
352}
353