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