Refactor MASM probe to allow printing of custom types.
https://bugs.webkit.org/show_bug.cgi?id=171101

Reviewed by JF Bastien.

For example, this allows us to add MASM printing of CodeBlock* and Air::Args.

In general, MASM print can be used like dataLog, except that it generates JITted
code for doing the dataLogging later when the JITted code runs.  MASM print can
print any value type that a specialized Printer template or a setPrinter()
function implemented for that type.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssembler.h:

* assembler/MacroAssemblerPrinter.cpp:
(JSC::Printer::printAllRegisters):
(JSC::Printer::printPCRegister):
(JSC::Printer::printRegisterID):
(JSC::Printer::printFPRegisterID):
(JSC::Printer::printAddress):
(JSC::Printer::printMemory):
(JSC::Printer::printCallback):
(JSC::printIndent): Deleted.
(JSC::printCPU): Deleted.
(JSC::printCPURegisters): Deleted.
(JSC::printPC): Deleted.
(JSC::printRegister): Deleted.
(JSC::printMemory): Deleted.
(JSC::MacroAssemblerPrinter::printCallback): Deleted.
* assembler/MacroAssemblerPrinter.h:
(JSC::AllRegisters::AllRegisters):
(JSC::Printer::Printer<AllRegisters>::Printer):
(JSC::Printer::Printer<PCRegister>::Printer):
(JSC::Printer::Printer<MacroAssembler::RegisterID>::Printer):
(JSC::Printer::Printer<MacroAssembler::FPRegisterID>::Printer):
(JSC::Printer::Printer<MacroAssembler::Address>::Printer):
(JSC::Printer::Printer<Memory>::Printer):
(JSC::Printer::Printer<MemWord<IntType>>::Printer):
(JSC::MacroAssembler::print):
(JSC::MacroAssemblerPrinter::print): Deleted.
(JSC::MacroAssemblerPrinter::PrintArg::PrintArg): Deleted.
(JSC::MacroAssemblerPrinter::appendPrintArg): Deleted.
- Refactored to move the underlying PrintRecord (and associated data structures)
  out to Printer.cpp/h.
- MacroAssemblerPrinter.cpp/h now only add custom Printers for MASM types like
  RegisterID and Memory.  It also defines the implementation of
  MacroAssembler::print().

  As before, JIT code that wishes to use MacroAssembler::print() needs to
  #include "MacroAssemblerPrinter.h".

- Also added the ability to specify an optional indentation (in number of chars)
  when MASM printing AllRegisters.  This is useful because AllRegisters prints
  a block of data unlike other printers which print inline.

* assembler/Printer.cpp: Added.
(JSC::Printer::printConstCharString):
(JSC::Printer::printIntptr):
(JSC::Printer::printUintptr):
(JSC::Printer::printPointer):
(JSC::Printer::setPrinter):
* assembler/Printer.h: Added.
(JSC::Printer::Context::Context):
(JSC::Printer::PrintRecord::PrintRecord):
(JSC::Printer::appendPrinter):
(JSC::Printer::makePrintRecordList):
(JSC::Printer::Printer<RawPointer>::Printer):
(JSC::Printer::setPrinter):
(JSC::Printer::Printer::Printer):
- Data structures for creating a list of PrintRecords.  Classes which wish to
  add custom support for MASM printing can #include "Printer.h" and implement
  either:
  1. a specialized Printer template, or
  2. a setPrinter() function.

  See Printer<Reg> and Printer<B3::Air::Tmp> in AirPrintSpecial.h for examples of
  (1).  See CodeBlock's setPrinter() for an example of (2).

* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::print):
* b3/air/AirPrintSpecial.cpp: Added.
(JSC::B3::Air::PrintSpecial::PrintSpecial):
(JSC::B3::Air::PrintSpecial::~PrintSpecial):
(JSC::B3::Air::PrintSpecial::forEachArg):
(JSC::B3::Air::PrintSpecial::isValid):
(JSC::B3::Air::PrintSpecial::admitsStack):
(JSC::B3::Air::PrintSpecial::reportUsedRegisters):
(JSC::B3::Air::PrintSpecial::generate):
(JSC::B3::Air::PrintSpecial::extraEarlyClobberedRegs):
(JSC::B3::Air::PrintSpecial::extraClobberedRegs):
(JSC::B3::Air::PrintSpecial::dumpImpl):
(JSC::B3::Air::PrintSpecial::deepDumpImpl):
(JSC::Printer::printAirArg):
* b3/air/AirPrintSpecial.h: Added.
(JSC::Printer::appendAirArg):
(JSC::Printer::appendAirArgs):
(JSC::Printer::Printer<B3::Air::Tmp>::Printer):
(JSC::Printer::Printer<Reg>::Printer):
- Add the print() operation for use in LowerToAir.  print() will emit a
  PrintSpecial that will ultimately emit a MASM print to print what we want.
- LowerToAir's print() adds the ability to print Air::Args.
- Unlike in the baseline JIT and the DFG, LowerToAir's print() can perturb the
  usage of registers.  This is because PrintSpecial is a patch point, and it
  prevents certain optimizations.  If not used carefully, an attempt to print()
  an Arg by taking a Tmp, can force the B3 Value into a Tmp earlier than it would
  otherwise do so.  So, use LowerToAir's print() with care.

* bytecode/CodeBlock.cpp:
(JSC::setPrinter):
- Now we can MASM print CodeBlock*.
(WTF::printInternal):
- Now we can dataLog CodeBlock* (including null CodeBlock pointers).

* bytecode/CodeBlock.h:

* runtime/VM.cpp:
(JSC::VM::throwException):
- Use the new ability to dataLog CodeBlock*.  No need to do an explicit null
  check before printing anymore.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@215642 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp b/Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp
index 718f10e..c1cfc25 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp
@@ -31,28 +31,25 @@
 #include "MacroAssembler.h"
 
 namespace JSC {
+namespace Printer {
 
 using CPUState = MacroAssembler::CPUState;
 using RegisterID = MacroAssembler::RegisterID;
 using FPRegisterID = MacroAssembler::FPRegisterID;
 
-static void printIndent(int indentation)
+void printAllRegisters(PrintStream& out, Context& context)
 {
-    for (; indentation > 0; indentation--)
-        dataLog("    ");
-}
+    auto& cpu = context.probeContext.cpu;
+    unsigned charsToIndent = context.data.as<unsigned>();
 
-#define INDENT printIndent(indentation)
-    
-void printCPU(CPUState& cpu, int indentation)
-{
-    INDENT, dataLog("cpu: {\n");
-    printCPURegisters(cpu, indentation + 1);
-    INDENT, dataLog("}\n");
-}
+    auto indent = [&] () {
+        for (unsigned i = 0; i < charsToIndent; ++i)
+            out.print(" ");
+    };
+#define INDENT indent()
 
-void printCPURegisters(CPUState& cpu, int indentation)
-{
+    INDENT, out.print("cpu: {\n");
+
 #if USE(JSVALUE32_64)
     #define INTPTR_HEX_VALUE_FORMAT "0x%08lx"
 #else
@@ -61,7 +58,7 @@
 
     #define PRINT_GPREGISTER(_type, _regName) { \
         intptr_t value = reinterpret_cast<intptr_t>(cpu._regName); \
-        INDENT, dataLogF("%6s: " INTPTR_HEX_VALUE_FORMAT "  %ld\n", #_regName, value, value) ; \
+        INDENT, out.printf("    %6s: " INTPTR_HEX_VALUE_FORMAT "  %ld\n", #_regName, value, value) ; \
     }
     FOR_EACH_CPU_GPREGISTER(PRINT_GPREGISTER)
     FOR_EACH_CPU_SPECIAL_REGISTER(PRINT_GPREGISTER)
@@ -71,56 +68,63 @@
     #define PRINT_FPREGISTER(_type, _regName) { \
         uint64_t* u = reinterpret_cast<uint64_t*>(&cpu._regName); \
         double* d = reinterpret_cast<double*>(&cpu._regName); \
-        INDENT, dataLogF("%6s: 0x%016llx  %.13g\n", #_regName, *u, *d); \
+        INDENT, out.printf("    %6s: 0x%016llx  %.13g\n", #_regName, *u, *d); \
     }
     FOR_EACH_CPU_FPREGISTER(PRINT_FPREGISTER)
     #undef PRINT_FPREGISTER
+
+    INDENT, out.print("}\n");
+#undef INDENT
+
 }
 
-static void printPC(CPUState& cpu)
+void printPCRegister(PrintStream& out, Context& context)
 {
-    union {
-        void* voidPtr;
-        intptr_t intptrValue;
-    } u;
+    auto cpu = context.probeContext.cpu;
+    void* value;
 #if CPU(X86) || CPU(X86_64)
-    u.voidPtr = cpu.eip;
+    value = cpu.eip;
 #elif CPU(ARM_TRADITIONAL) || CPU(ARM_THUMB2) || CPU(ARM64)
-    u.voidPtr = cpu.pc;
+    value = cpu.pc;
 #else
 #error "Unsupported CPU"
 #endif
-    dataLogF("pc:<%p %ld>", u.voidPtr, u.intptrValue);
+    out.printf("pc:<%p %ld>", value, bitwise_cast<intptr_t>(value));
 }
 
-void printRegister(CPUState& cpu, RegisterID regID)
+void printRegisterID(PrintStream& out, Context& context)
 {
+    RegisterID regID = context.data.as<RegisterID>();
     const char* name = CPUState::gprName(regID);
-    union {
-        void* voidPtr;
-        intptr_t intptrValue;
-    } u;
-    u.voidPtr = cpu.gpr(regID);
-    dataLogF("%s:<%p %ld>", name, u.voidPtr, u.intptrValue);
+    void* value = context.probeContext.gpr(regID);
+    out.printf("%s:<%p %ld>", name, value, bitwise_cast<intptr_t>(value));
 }
 
-void printRegister(CPUState& cpu, FPRegisterID regID)
+void printFPRegisterID(PrintStream& out, Context& context)
 {
+    FPRegisterID regID = context.data.as<FPRegisterID>();
     const char* name = CPUState::fprName(regID);
-    union {
-        double doubleValue;
-        uint64_t uint64Value;
-    } u;
-    u.doubleValue = cpu.fpr(regID);
-    dataLogF("%s:<0x%016llx %.13g>", name, u.uint64Value, u.doubleValue);
+    double value = context.probeContext.fpr(regID);
+    out.printf("%s:<0x%016llx %.13g>", name, bitwise_cast<uint64_t>(value), value);
 }
 
-void printMemory(CPUState& cpu, const Memory& memory)
+void printAddress(PrintStream& out, Context& context)
 {
+    MacroAssembler::Address address = context.data.as<MacroAssembler::Address>();
+    RegisterID regID = address.base;
+    const char* name = CPUState::gprName(regID);
+    void* value = context.probeContext.gpr(regID);
+    out.printf("Address{base:%s:<%p %ld>, offset:<0x%x %d>", name, value, bitwise_cast<intptr_t>(value), address.offset, address.offset);
+}
+
+void printMemory(PrintStream& out, Context& context)
+{
+    const Memory& memory = context.data.as<Memory>();
+
     uint8_t* ptr = nullptr;
     switch (memory.addressType) {
     case Memory::AddressType::Address: {
-        ptr = reinterpret_cast<uint8_t*>(cpu.gpr(memory.u.address.base));
+        ptr = reinterpret_cast<uint8_t*>(context.probeContext.gpr(memory.u.address.base));
         ptr += memory.u.address.offset;
         break;
     }
@@ -133,22 +137,22 @@
     if (memory.dumpStyle == Memory::SingleWordDump) {
         if (memory.numBytes == sizeof(int8_t)) {
             auto p = reinterpret_cast<int8_t*>(ptr);
-            dataLogF("%p:<0x%02x %d>", p, *p, *p);
+            out.printf("%p:<0x%02x %d>", p, *p, *p);
             return;
         }
         if (memory.numBytes == sizeof(int16_t)) {
             auto p = reinterpret_cast<int16_t*>(ptr);
-            dataLogF("%p:<0x%04x %d>", p, *p, *p);
+            out.printf("%p:<0x%04x %d>", p, *p, *p);
             return;
         }
         if (memory.numBytes == sizeof(int32_t)) {
             auto p = reinterpret_cast<int32_t*>(ptr);
-            dataLogF("%p:<0x%08x %d>", p, *p, *p);
+            out.printf("%p:<0x%08x %d>", p, *p, *p);
             return;
         }
         if (memory.numBytes == sizeof(int64_t)) {
             auto p = reinterpret_cast<int64_t*>(ptr);
-            dataLogF("%p:<0x%016llx %lld>", p, *p, *p);
+            out.printf("%p:<0x%016llx %lld>", p, *p, *p);
             return;
         }
         // Else, unknown word size. Fall thru and dump in the generic way.
@@ -158,58 +162,31 @@
     size_t numBytes = memory.numBytes;
     for (size_t i = 0; i < numBytes; i++) {
         if (!(i % 16))
-            dataLogF("%p: ", &ptr[i]);
+            out.printf("%p: ", &ptr[i]);
         else if (!(i % 4))
-            dataLog(" ");
+            out.printf(" ");
 
-        dataLogF("%02x", ptr[i]);
+        out.printf("%02x", ptr[i]);
 
         if (i % 16 == 15)
-            dataLog("\n");
+            out.print("\n");
     }
     if (numBytes % 16 < 15)
-        dataLog("\n");
+        out.print("\n");
 }
 
-void MacroAssemblerPrinter::printCallback(ProbeContext* context)
+void printCallback(ProbeContext* probeContext)
 {
-    typedef PrintArg Arg;
-    PrintArgsList& argsList =
-    *reinterpret_cast<PrintArgsList*>(context->arg);
-    for (size_t i = 0; i < argsList.size(); i++) {
-        auto& arg = argsList[i];
-        switch (arg.type) {
-        case Arg::Type::AllRegisters:
-            printCPU(context->cpu, 1);
-            break;
-        case Arg::Type::PCRegister:
-            printPC(context->cpu);
-            break;
-        case Arg::Type::RegisterID:
-            printRegister(context->cpu, arg.u.gpRegisterID);
-            break;
-        case Arg::Type::FPRegisterID:
-            printRegister(context->cpu, arg.u.fpRegisterID);
-            break;
-        case Arg::Type::Memory:
-            printMemory(context->cpu, arg.u.memory);
-            break;
-        case Arg::Type::ConstCharPtr:
-            dataLog(arg.u.constCharPtr);
-            break;
-        case Arg::Type::ConstVoidPtr:
-            dataLogF("%p", arg.u.constVoidPtr);
-            break;
-        case Arg::Type::IntptrValue:
-            dataLog(arg.u.intptrValue);
-            break;
-        case Arg::Type::UintptrValue:
-            dataLog(arg.u.uintptrValue);
-            break;
-        }
+    auto& out = WTF::dataFile();
+    PrintRecordList& list = *reinterpret_cast<PrintRecordList*>(probeContext->arg);
+    for (size_t i = 0; i < list.size(); i++) {
+        auto& record = list[i];
+        Context context(*probeContext, record.data);
+        record.printer(out, context);
     }
 }
 
+} // namespace Printer
 } // namespace JSC
 
 #endif // ENABLE(MASM_PROBE)