/ bytecode / BytecodeDumper.cpp
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  }