From 4fdec8b2a13a6129931df88579599f446a4f36fe Mon Sep 17 00:00:00 2001 From: Edwin Smith Date: Tue, 7 May 2013 06:17:22 -0700 Subject: [PATCH] Move pretty-printing code into its own file Pretty-printing the IR is a kind of pass, and definitely a separate concern, just like rendering IR as machine code. Put it in its own file. --- hphp/runtime/vm/translator/hopt/codegen.cpp | 3 +- hphp/runtime/vm/translator/hopt/ir.cpp | 401 +---------------- hphp/runtime/vm/translator/hopt/ir.h | 37 +- .../vm/translator/hopt/irtranslator.cpp | 1 + .../runtime/vm/translator/hopt/linearscan.cpp | 10 +- hphp/runtime/vm/translator/hopt/linearscan.h | 12 +- hphp/runtime/vm/translator/hopt/opt.cpp | 1 + hphp/runtime/vm/translator/hopt/print.cpp | 412 ++++++++++++++++++ hphp/runtime/vm/translator/hopt/print.h | 63 +++ 9 files changed, 513 insertions(+), 427 deletions(-) create mode 100644 hphp/runtime/vm/translator/hopt/print.cpp create mode 100644 hphp/runtime/vm/translator/hopt/print.h diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 6cc4e4adb..741f48390 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -44,6 +44,7 @@ #include "runtime/vm/translator/hopt/ir.h" #include "runtime/vm/translator/hopt/linearscan.h" #include "runtime/vm/translator/hopt/nativecalls.h" +#include "runtime/vm/translator/hopt/print.h" using HPHP::VM::Transl::TCA; using namespace HPHP::VM::Transl::TargetCache; @@ -5142,7 +5143,7 @@ void cgTrace(Trace* trace, Asm& amain, Asm& astubs, Transl::TranslatorX64* tx64, } void CodeGenerator::print() const { - m_curTrace->print(std::cout, m_state.asmInfo); + JIT::print(std::cout, m_curTrace, m_state.asmInfo); } // select instructions for the trace and its exits diff --git a/hphp/runtime/vm/translator/hopt/ir.cpp b/hphp/runtime/vm/translator/hopt/ir.cpp index e90faa8d3..43a4a0ce8 100644 --- a/hphp/runtime/vm/translator/hopt/ir.cpp +++ b/hphp/runtime/vm/translator/hopt/ir.cpp @@ -26,9 +26,7 @@ #include "folly/Format.h" #include "folly/Traits.h" -#include "util/disasm.h" #include "util/trace.h" -#include "util/text_color.h" #include "runtime/base/string_data.h" #include "runtime/vm/runtime.h" #include "runtime/base/stats.h" @@ -36,6 +34,8 @@ #include "runtime/vm/translator/hopt/linearscan.h" #include "runtime/vm/translator/hopt/cse.h" #include "runtime/vm/translator/hopt/simplifier.h" +#include "runtime/vm/translator/hopt/print.h" +#include "runtime/vm/translator/hopt/codegen.h" // Include last to localize effects to this file #include "util/assert_throw.h" @@ -262,7 +262,7 @@ typename std::enable_if< has_show::value, std::string >::type showExtraImpl(T* t) { return t->show(); } -std::string showExtraImpl(IRExtraData*) { return "..."; } +std::string showExtraImpl(const IRExtraData*) { return "..."; } MAKE_DISPATCHER(HashDispatcher, size_t, cseHashExtraImpl); size_t cseHashExtra(Opcode opc, IRExtraData* data) { @@ -280,22 +280,16 @@ IRExtraData* cloneExtra(Opcode opc, IRExtraData* data, Arena& a) { } MAKE_DISPATCHER(ShowDispatcher, std::string, showExtraImpl); -std::string showExtra(Opcode opc, IRExtraData* data) { - return dispatchExtra(opc, data); + +} // namespace + +std::string showExtra(Opcode opc, const IRExtraData* data) { + return dispatchExtra(opc, + const_cast(data)); } ////////////////////////////////////////////////////////////////////// -// Helper for pretty-printing punctuation. -std::string punc(const char* str) { - return folly::format("{}{}{}", - color(ANSI_COLOR_DARK_GRAY), str, color(ANSI_COLOR_END)).str(); -} - -////////////////////////////////////////////////////////////////////// - -} - IRInstruction::IRInstruction(Arena& arena, const IRInstruction* inst, IId iid) : m_op(inst->m_op) , m_typeParam(inst->m_typeParam) @@ -732,198 +726,12 @@ size_t IRInstruction::cseHash() const { return CSEHash::hashCombine(srcHash, m_op, m_typeParam); } -void IRInstruction::printOpcode(std::ostream& os) const { - os << color(ANSI_COLOR_CYAN) - << opcodeName(m_op) - << color(ANSI_COLOR_END) - ; - - if (m_typeParam == Type::None && !hasExtra()) { - return; - } - os << color(ANSI_COLOR_LIGHT_BLUE) << '<' << color(ANSI_COLOR_END); - if (m_typeParam != Type::None) { - os << color(ANSI_COLOR_GREEN) - << m_typeParam.toString() - << color(ANSI_COLOR_END) - ; - if (hasExtra()) { - os << punc(","); - } - } - if (hasExtra()) { - os << color(ANSI_COLOR_GREEN) - << showExtra(op(), m_extra) - << color(ANSI_COLOR_END); - } - os << color(ANSI_COLOR_LIGHT_BLUE) - << '>' - << color(ANSI_COLOR_END); -} - -void IRInstruction::printDst(std::ostream& os) const { - if (getNumDsts() == 0) return; - - const char* sep = ""; - for (const SSATmp& dst : getDsts()) { - os << punc(sep); - dst.print(os, true); - sep = ", "; - } - os << punc(" = "); -} - -void IRInstruction::printSrc(std::ostream& ostream, uint32_t i) const { - SSATmp* src = getSrc(i); - if (src != nullptr) { - if (m_id != 0 && !src->isConst() && src->getLastUseId() == m_id) { - ostream << "~"; - } - src->print(ostream); - } else { - ostream << color(ANSI_COLOR_RED) - << "!!!NULL @ " << i - << color(ANSI_COLOR_END) - ; - } -} - -void IRInstruction::printSrcs(std::ostream& os) const { - bool first = true; - if (op() == IncStat) { - os << " " << Stats::g_counterNames[getSrc(0)->getValInt()] - << ", " << getSrc(1)->getValInt(); - return; - } - for (uint32_t i = 0; i < m_numSrcs; i++) { - if (!first) { - os << punc(", "); - } else { - os << " "; - first = false; - } - printSrc(os, i); - } -} - -void IRInstruction::print(std::ostream& ostream) const { - if (op() == Marker) { - auto* marker = getExtra(); - ostream << color(ANSI_COLOR_BLUE) - << folly::format("--- bc {}, spOff {} ({})", - marker->bcOff, - marker->stackOff, - marker->func->fullName()->data()) - << color(ANSI_COLOR_END); - return; - } - - if (!isTransient()) { - ostream << color(ANSI_COLOR_YELLOW); - if (!m_id) ostream << folly::format("({:02d}) ", getIId()); - else ostream << folly::format("({:02d}@{:02d}) ", getIId(), m_id); - ostream << color(ANSI_COLOR_END); - } - printDst(ostream); - printOpcode(ostream); - printSrcs(ostream); - - if (m_taken) { - ostream << punc(" -> "); - m_taken->printLabel(ostream); - } - - if (m_tca) { - ostream << punc(", "); - if (m_tca == kIRDirectJccJmpActive) { - ostream << "JccJmp_Exit "; - } - else - if (m_tca == kIRDirectJccActive) { - ostream << "Jcc_Exit "; - } - else - if (m_tca == kIRDirectGuardActive) { - ostream << "Guard_Exit "; - } - else { - ostream << (void*)m_tca; - } - } -} - -void IRInstruction::print() const { - print(std::cerr); - std::cerr << std::endl; -} - std::string IRInstruction::toString() const { std::ostringstream str; - print(str); + print(str, this); return str.str(); } -static void printConst(std::ostream& os, IRInstruction* inst) { - os << color(ANSI_COLOR_LIGHT_BLUE); - SCOPE_EXIT { os << color(ANSI_COLOR_END); }; - - auto t = inst->getTypeParam(); - auto c = inst->getExtra(); - if (t == Type::Int) { - os << c->as(); - } else if (t == Type::Dbl) { - os << c->as(); - } else if (t == Type::Bool) { - os << (c->as() ? "true" : "false"); - } else if (t.isString()) { - auto str = c->as(); - os << "\"" - << Util::escapeStringForCPP(str->data(), str->size()) - << "\""; - } else if (t.isArray()) { - auto arr = inst->getExtra()->as(); - if (arr->empty()) { - os << "array()"; - } else { - os << "Array(" << arr << ")"; - } - } else if (t.isNull()) { - os << t.toString(); - } else if (t.subtypeOf(Type::Func)) { - auto func = c->as(); - os << "Func(" << (func ? func->fullName()->data() : "0") << ")"; - } else if (t.subtypeOf(Type::Cls)) { - auto cls = c->as(); - os << "Cls(" << (cls ? cls->name()->data() : "0") << ")"; - } else if (t.subtypeOf(Type::NamedEntity)) { - auto ne = c->as(); - os << "NamedEntity(" << ne << ")"; - } else if (t.subtypeOf(Type::TCA)) { - TCA tca = c->as(); - auto name = Util::getNativeFunctionName(tca); - SCOPE_EXIT { free(name); }; - os << folly::format("TCA: {}({})", tca, - boost::trim_copy(std::string(name))); - } else if (t.subtypeOf(Type::None)) { - os << "None:" << c->as(); - } else if (t.isPtr()) { - os << folly::format("{}({:#x})", t.toString(), c->as()); - } else if (t.subtypeOf(Type::CacheHandle)) { - os << folly::format("CacheHandle({:#x})", c->as()); - } else { - not_reached(); - } -} - -void Block::printLabel(std::ostream& os) const { - os << color(ANSI_COLOR_MAGENTA); - os << "L" << m_id; - if (getHint() == Unlikely) { - os << ""; - } - os << color(ANSI_COLOR_END); -} - int SSATmp::numNeededRegs() const { auto t = type(); if (t.subtypeOfAny(Type::None, Type::Null, Type::ActRec, Type::RetAddr)) { @@ -1074,176 +882,16 @@ std::string ExitData::show() const { std::string SSATmp::toString() const { std::ostringstream out; - print(out); + print(out, this); return out.str(); } -void SSATmp::print(std::ostream& os, bool printLastUse) const { - if (m_inst->op() == DefConst) { - printConst(os, m_inst); - return; - } - os << color(ANSI_COLOR_WHITE); - os << "t" << m_id; - os << color(ANSI_COLOR_END); - if (printLastUse && m_lastUseId != 0) { - os << color(ANSI_COLOR_GRAY) - << "@" << m_lastUseId << "#" << m_useCount - << color(ANSI_COLOR_END); - } - if (m_isSpilled || numAllocatedRegs() > 0) { - os << color(ANSI_COLOR_BROWN) << '('; - if (!m_isSpilled) { - for (int i = 0, sz = numAllocatedRegs(); i < sz; ++i) { - if (i != 0) os << ","; - os << reg::regname(Reg64(int(m_regs[i]))); - } - } else { - for (int i = 0, sz = numNeededRegs(); i < sz; ++i) { - if (i != 0) os << ","; - os << m_spillInfo[i]; - } - } - os << ')' << color(ANSI_COLOR_END); - } - os << punc(":") - << color(ANSI_COLOR_GREEN) - << type().toString() - << color(ANSI_COLOR_END) - ; -} - -void SSATmp::print() const { - print(std::cerr); - std::cerr << std::endl; -} - std::string Trace::toString() const { std::ostringstream out; - print(out, nullptr); + print(out, this, nullptr); return out.str(); } -void Trace::print() const { - print(std::cout, nullptr); -} - -void Trace::print(std::ostream& os, const AsmInfo* asmInfo) const { - static const int kIndent = 4; - Disasm disasm(Disasm::Options().indent(kIndent + 4) - .printEncoding(dumpIREnabled(6)) - .color(color(ANSI_COLOR_BROWN))); - - // Print unlikely blocks at the end - BlockList blocks, unlikely; - for (Block* block : m_blocks) { - if (block->getHint() == Block::Unlikely) { - unlikely.push_back(block); - } else { - blocks.push_back(block); - } - } - blocks.splice(blocks.end(), unlikely); - - for (Block* block : blocks) { - TcaRange blockRange = asmInfo ? asmInfo->asmRanges[block] : - TcaRange(nullptr, nullptr); - for (auto it = block->begin(); it != block->end();) { - auto& inst = *it; ++it; - - if (inst.op() == Marker) { - os << std::string(kIndent, ' '); - inst.print(os); - os << '\n'; - - // Don't print bytecode in a non-main trace. - if (!isMain()) continue; - - auto* marker = inst.getExtra(); - uint32_t bcOffset = marker->bcOff; - if (const auto* func = marker->func) { - std::ostringstream uStr; - func->unit()->prettyPrint( - uStr, Unit::PrintOpts() - .range(bcOffset, bcOffset+1) - .noLineNumbers() - .indent(0)); - std::vector vec; - folly::split('\n', uStr.str(), vec); - for (auto& s : vec) { - os << color(ANSI_COLOR_BLUE) << s << color(ANSI_COLOR_END) << '\n'; - } - continue; - } - } - - if (inst.op() == DefLabel) { - os << std::string(kIndent - 2, ' '); - inst.getBlock()->printLabel(os); - os << punc(":") << "\n"; - // print phi pseudo-instructions - for (unsigned i = 0, n = inst.getNumDsts(); i < n; ++i) { - os << std::string(kIndent + - folly::format("({}) ", inst.getIId()).str().size(), - ' '); - inst.getDst(i)->print(os, false); - os << punc(" = ") << color(ANSI_COLOR_CYAN) << "phi " - << color(ANSI_COLOR_END); - bool first = true; - inst.getBlock()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) { - if (!first) os << punc(", "); - first = false; - jmp->printSrc(os, i); - os << punc("@"); - jmp->getBlock()->printLabel(os); - }); - os << '\n'; - } - } - - os << std::string(kIndent, ' '); - inst.print(os); - os << '\n'; - - if (asmInfo) { - TcaRange instRange = asmInfo->instRanges[inst]; - if (!instRange.empty()) { - disasm.disasm(os, instRange.begin(), instRange.end()); - os << '\n'; - assert(instRange.end() >= blockRange.start() && - instRange.end() <= blockRange.end()); - blockRange = TcaRange(instRange.end(), blockRange.end()); - } - } - } - - if (asmInfo) { - // print code associated with this block that isn't tied to any - // instruction. This includes code after the last isntruction (e.g. - // jmp to next block), and AStubs code. - if (!blockRange.empty()) { - os << std::string(kIndent, ' ') << punc("A:") << "\n"; - disasm.disasm(os, blockRange.start(), blockRange.end()); - } - auto astubRange = asmInfo->astubRanges[block]; - if (!astubRange.empty()) { - os << std::string(kIndent, ' ') << punc("AStubs:") << "\n"; - disasm.disasm(os, astubRange.start(), astubRange.end()); - } - if (!blockRange.empty() || !astubRange.empty()) { - os << '\n'; - } - } - } - - for (auto* exitTrace : m_exitTraces) { - os << "\n" << color(ANSI_COLOR_GREEN) - << " ------- Exit Trace -------" - << color(ANSI_COLOR_END) << '\n'; - exitTrace->print(os, asmInfo); - } -} - int32_t spillValueCells(IRInstruction* spillStack) { assert(spillStack->op() == SpillStack); int32_t numSrcs = spillStack->getNumSrcs(); @@ -1417,30 +1065,5 @@ bool hasInternalFlow(Trace* trace) { return false; } -void dumpTraceImpl(const Trace* trace, std::ostream& out, - const AsmInfo* asmInfo) { - trace->print(out, asmInfo); -} - -// Suggested captions: "before jiffy removal", "after goat saturation", -// etc. -void dumpTrace(int level, const Trace* trace, const char* caption, - AsmInfo* ai) { - if (dumpIREnabled(level)) { - std::ostringstream str; - auto bannerFmt = "{:-^40}\n"; - str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN) - << folly::format(bannerFmt, caption) - << color(ANSI_COLOR_END) - ; - dumpTraceImpl(trace, str, ai); - str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN) - << folly::format(bannerFmt, "") - << color(ANSI_COLOR_END) - ; - HPHP::Trace::traceRelease("%s", str.str().c_str()); - } -} - }}} diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 2f502693f..e4b6ca128 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -886,6 +886,8 @@ template void assert_opcode_extra(Opcode opc) { #undef O } +std::string showExtra(Opcode opc, const IRExtraData* data); + ////////////////////////////////////////////////////////////////////// inline bool isCmpOp(Opcode opc) { @@ -1730,6 +1732,11 @@ struct IRInstruction { */ void setExtra(IRExtraData* data) { assert(!m_extra); m_extra = data; } + /* + * Return the raw extradata pointer, for pretty-printing. + */ + const IRExtraData* rawExtra() const { return m_extra; } + /* * Clear the extra data pointer in a IRInstruction. Used during * IRFactory::gen to avoid having dangling IRExtraData*'s into stack @@ -1867,8 +1874,6 @@ struct IRInstruction { bool cseEquals(IRInstruction* inst) const; size_t cseHash() const; - void print(std::ostream& ostream) const; - void print() const; std::string toString() const; /* @@ -1903,11 +1908,6 @@ struct IRInstruction { // ModifiesStack set. bool hasMainDst() const; - void printDst(std::ostream& ostream) const; - void printSrc(std::ostream& ostream, uint32_t srcIndex) const; - void printOpcode(std::ostream& ostream) const; - void printSrcs(std::ostream& ostream) const; - private: bool mayReenterHelper() const; @@ -1986,13 +1986,11 @@ public: void setUseCount(uint32_t count) { m_useCount = count; } void incUseCount() { m_useCount++; } uint32_t decUseCount() { return --m_useCount; } + bool isSpilled() const { return m_isSpilled; } bool isBoxed() const { return type().isBoxed(); } bool isString() const { return isA(Type::Str); } bool isArray() const { return isA(Type::Arr); } std::string toString() const; - void print(std::ostream& ostream, - bool printLastUse = false) const; - void print() const; // XXX: false for Null, etc. Would rather it returns whether we // have a compile-time constant value. @@ -2277,8 +2275,6 @@ struct Block : boost::noncopyable { unsigned postId() const { return m_postid; } void setPostId(unsigned id) { m_postid = id; } - void printLabel(std::ostream& ostream) const; - // insert inst after this block's label, return an iterator to the // newly inserted instruction. iterator prepend(IRInstruction* inst) { @@ -2385,6 +2381,7 @@ public: } std::list& getBlocks() { return m_blocks; } + const std::list& getBlocks() const { return m_blocks; } Block* front() { return *m_blocks.begin(); } Block* back() { auto it = m_blocks.end(); return *(--it); } const Block* front() const { return *m_blocks.begin(); } @@ -2423,9 +2420,8 @@ public: typedef std::list::iterator ExitIterator; ExitList& getExitTraces() { return m_exitTraces; } + const ExitList& getExitTraces() const { return m_exitTraces; } std::string toString() const; - void print(std::ostream& ostream, const AsmInfo* asmInfo = nullptr) const; - void print() const; private: // offset of the first bytecode in this trace; 0 if this trace doesn't @@ -2607,19 +2603,6 @@ void forEachTraceInst(Trace* main, Body body) { }); } -/* - * Some utilities related to dumping. Rather than file-by-file control, we control - * most IR logging via the hhir trace module. - */ -static inline bool dumpIREnabled(int level = 1) { - return HPHP::Trace::moduleEnabledRelease(HPHP::Trace::hhir, level); -} - -void dumpTraceImpl(const Trace* trace, std::ostream& out, - const AsmInfo* asmInfo = nullptr); -void dumpTrace(int level, const Trace* trace, const char* caption, - AsmInfo* ai = nullptr); - }}} namespace std { diff --git a/hphp/runtime/vm/translator/hopt/irtranslator.cpp b/hphp/runtime/vm/translator/hopt/irtranslator.cpp index 3c75bfcd6..d9ac0d252 100644 --- a/hphp/runtime/vm/translator/hopt/irtranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/irtranslator.cpp @@ -37,6 +37,7 @@ #include "runtime/vm/translator/hopt/linearscan.h" #include "runtime/vm/translator/hopt/codegen.h" #include "runtime/vm/translator/hopt/hhbctranslator.h" +#include "runtime/vm/translator/hopt/print.h" // Include last to localize effects to this file #include "util/assert_throw.h" diff --git a/hphp/runtime/vm/translator/hopt/linearscan.cpp b/hphp/runtime/vm/translator/hopt/linearscan.cpp index 3447e4781..b7aab156f 100644 --- a/hphp/runtime/vm/translator/hopt/linearscan.cpp +++ b/hphp/runtime/vm/translator/hopt/linearscan.cpp @@ -18,6 +18,14 @@ #include "runtime/vm/translator/hopt/irfactory.h" #include "runtime/vm/translator/hopt/nativecalls.h" +#include "runtime/vm/translator/hopt/print.h" +#include "runtime/vm/translator/hopt/ir.h" +#include "runtime/vm/translator/hopt/tracebuilder.h" +#include "runtime/vm/translator/hopt/codegen.h" +#include "runtime/vm/translator/hopt/state_vector.h" +#include "runtime/vm/translator/physreg.h" +#include "runtime/vm/translator/abi-x64.h" +#include namespace HPHP { namespace VM { @@ -278,7 +286,7 @@ template static inline void dumpIR(const Inner* in, const char* msg) { if (dumpIREnabled(DumpVal)) { std::ostringstream str; - in->print(str); + print(str, in); HPHP::Trace::traceRelease("--- %s: %s\n", msg, str.str().c_str()); } } diff --git a/hphp/runtime/vm/translator/hopt/linearscan.h b/hphp/runtime/vm/translator/hopt/linearscan.h index aa75321da..375f65be5 100644 --- a/hphp/runtime/vm/translator/hopt/linearscan.h +++ b/hphp/runtime/vm/translator/hopt/linearscan.h @@ -17,17 +17,11 @@ #ifndef incl_HPHP_VM_LINEAR_SCAN_H_ #define incl_HPHP_VM_LINEAR_SCAN_H_ -#include - -#include "runtime/vm/translator/physreg.h" -#include "runtime/vm/translator/abi-x64.h" -#include "runtime/vm/translator/hopt/ir.h" -#include "runtime/vm/translator/hopt/tracebuilder.h" -#include "runtime/vm/translator/hopt/codegen.h" -#include "runtime/vm/translator/hopt/state_vector.h" - namespace HPHP { namespace VM { namespace JIT { +class Trace; +class IRFactory; + /* * The main entry point for register allocation. Called prior to code * generation. diff --git a/hphp/runtime/vm/translator/hopt/opt.cpp b/hphp/runtime/vm/translator/hopt/opt.cpp index 207cc5756..27b8a2b64 100644 --- a/hphp/runtime/vm/translator/hopt/opt.cpp +++ b/hphp/runtime/vm/translator/hopt/opt.cpp @@ -17,6 +17,7 @@ #include "runtime/vm/translator/hopt/tracebuilder.h" #include "util/trace.h" #include "runtime/vm/translator/hopt/irfactory.h" +#include "runtime/vm/translator/hopt/print.h" namespace HPHP { namespace VM { diff --git a/hphp/runtime/vm/translator/hopt/print.cpp b/hphp/runtime/vm/translator/hopt/print.cpp new file mode 100644 index 000000000..554efd610 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/print.cpp @@ -0,0 +1,412 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#include "runtime/vm/translator/hopt/print.h" + +#include "runtime/vm/translator/hopt/ir.h" +#include "runtime/vm/translator/hopt/linearscan.h" +#include "runtime/vm/translator/hopt/codegen.h" +#include "runtime/base/stats.h" +#include "util/disasm.h" +#include "util/text_color.h" + +namespace HPHP { namespace VM { namespace JIT { + +////////////////////////////////////////////////////////////////////// + +// Helper for pretty-printing punctuation. +static std::string punc(const char* str) { + return folly::format("{}{}{}", + color(ANSI_COLOR_DARK_GRAY), str, color(ANSI_COLOR_END)).str(); +} + +////////////////////////////////////////////////////////////////////// + +void printOpcode(std::ostream& os, const IRInstruction* inst) { + os << color(ANSI_COLOR_CYAN) + << opcodeName(inst->op()) + << color(ANSI_COLOR_END) + ; + + auto type_param = inst->getTypeParam(); + if (type_param == Type::None && !inst->hasExtra()) { + return; + } + os << color(ANSI_COLOR_LIGHT_BLUE) << '<' << color(ANSI_COLOR_END); + if (type_param != Type::None) { + os << color(ANSI_COLOR_GREEN) + << type_param.toString() + << color(ANSI_COLOR_END) + ; + if (inst->hasExtra()) { + os << punc(","); + } + } + if (inst->hasExtra()) { + os << color(ANSI_COLOR_GREEN) + << showExtra(inst->op(), inst->rawExtra()) + << color(ANSI_COLOR_END); + } + os << color(ANSI_COLOR_LIGHT_BLUE) + << '>' + << color(ANSI_COLOR_END); +} + +void printDst(std::ostream& os, const IRInstruction* inst) { + if (inst->getNumDsts() == 0) return; + + const char* sep = ""; + for (const SSATmp& dst : inst->getDsts()) { + os << punc(sep); + print(os, &dst, true); + sep = ", "; + } + os << punc(" = "); +} + +void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i) { + SSATmp* src = inst->getSrc(i); + if (src != nullptr) { + if (inst->getId() != 0 && !src->isConst() && + src->getLastUseId() == inst->getId()) { + ostream << "~"; + } + print(ostream, src); + } else { + ostream << color(ANSI_COLOR_RED) + << "!!!NULL @ " << i + << color(ANSI_COLOR_END) + ; + } +} + +void printSrcs(std::ostream& os, const IRInstruction* inst) { + bool first = true; + if (inst->op() == IncStat) { + os << " " << Stats::g_counterNames[inst->getSrc(0)->getValInt()] + << ", " << inst->getSrc(1)->getValInt(); + return; + } + for (uint32_t i = 0, n = inst->getNumSrcs(); i < n; i++) { + if (!first) { + os << punc(", "); + } else { + os << " "; + first = false; + } + printSrc(os, inst, i); + } +} + +void printLabel(std::ostream& os, const Block* block) { + os << color(ANSI_COLOR_MAGENTA); + os << "L" << block->getId(); + if (block->getHint() == Block::Unlikely) os << ""; + os << color(ANSI_COLOR_END); +} + +void print(std::ostream& ostream, const IRInstruction* inst) { + if (inst->op() == Marker) { + auto* marker = inst->getExtra(); + ostream << color(ANSI_COLOR_BLUE) + << folly::format("--- bc {}, spOff {} ({})", + marker->bcOff, + marker->stackOff, + marker->func->fullName()->data()) + << color(ANSI_COLOR_END); + return; + } + + if (!inst->isTransient()) { + ostream << color(ANSI_COLOR_YELLOW); + if (!inst->getId()) { + ostream << folly::format("({:02d}) ", inst->getIId()); + } else { + ostream << folly::format("({:02d}@{:02d}) ", inst->getIId(), + inst->getId()); + } + ostream << color(ANSI_COLOR_END); + } + printDst(ostream, inst); + printOpcode(ostream, inst); + printSrcs(ostream, inst); + + if (Block* taken = inst->getTaken()) { + ostream << punc(" -> "); + printLabel(ostream, taken); + } + + if (TCA tca = inst->getTCA()) { + ostream << punc(", "); + if (tca == kIRDirectJccJmpActive) { + ostream << "JccJmp_Exit "; + } + else if (tca == kIRDirectJccActive) { + ostream << "Jcc_Exit "; + } + else if (tca == kIRDirectGuardActive) { + ostream << "Guard_Exit "; + } + else { + ostream << (void*)tca; + } + } +} + +void print(const IRInstruction* inst) { + print(std::cerr, inst); + std::cerr << std::endl; +} + +static void printConst(std::ostream& os, IRInstruction* inst) { + os << color(ANSI_COLOR_LIGHT_BLUE); + SCOPE_EXIT { os << color(ANSI_COLOR_END); }; + + auto t = inst->getTypeParam(); + auto c = inst->getExtra(); + if (t == Type::Int) { + os << c->as(); + } else if (t == Type::Dbl) { + os << c->as(); + } else if (t == Type::Bool) { + os << (c->as() ? "true" : "false"); + } else if (t.isString()) { + auto str = c->as(); + os << "\"" + << Util::escapeStringForCPP(str->data(), str->size()) + << "\""; + } else if (t.isArray()) { + auto arr = inst->getExtra()->as(); + if (arr->empty()) { + os << "array()"; + } else { + os << "Array(" << arr << ")"; + } + } else if (t.isNull()) { + os << t.toString(); + } else if (t.subtypeOf(Type::Func)) { + auto func = c->as(); + os << "Func(" << (func ? func->fullName()->data() : "0") << ")"; + } else if (t.subtypeOf(Type::Cls)) { + auto cls = c->as(); + os << "Cls(" << (cls ? cls->name()->data() : "0") << ")"; + } else if (t.subtypeOf(Type::NamedEntity)) { + auto ne = c->as(); + os << "NamedEntity(" << ne << ")"; + } else if (t.subtypeOf(Type::TCA)) { + TCA tca = c->as(); + auto name = Util::getNativeFunctionName(tca); + SCOPE_EXIT { free(name); }; + os << folly::format("TCA: {}({})", tca, + boost::trim_copy(std::string(name))); + } else if (t.subtypeOf(Type::None)) { + os << "None:" << c->as(); + } else if (t.isPtr()) { + os << folly::format("{}({:#x})", t.toString(), c->as()); + } else if (t.subtypeOf(Type::CacheHandle)) { + os << folly::format("CacheHandle({:#x})", c->as()); + } else { + not_reached(); + } +} + +void print(std::ostream& os, const SSATmp* tmp, bool printLastUse) { + if (tmp->inst()->op() == DefConst) { + printConst(os, tmp->inst()); + return; + } + os << color(ANSI_COLOR_WHITE); + os << "t" << tmp->getId(); + os << color(ANSI_COLOR_END); + if (printLastUse && tmp->getLastUseId() != 0) { + os << color(ANSI_COLOR_GRAY) + << "@" << tmp->getLastUseId() << "#" << tmp->getUseCount() + << color(ANSI_COLOR_END); + } + if (tmp->isSpilled() || tmp->numAllocatedRegs() > 0) { + os << color(ANSI_COLOR_BROWN) << '('; + if (!tmp->isSpilled()) { + for (int i = 0, sz = tmp->numAllocatedRegs(); i < sz; ++i) { + if (i != 0) os << ","; + os << reg::regname(Reg64(tmp->getReg(i))); + } + } else { + for (int i = 0, sz = tmp->numNeededRegs(); i < sz; ++i) { + if (i != 0) os << ","; + os << tmp->getSpillInfo(i); + } + } + os << ')' << color(ANSI_COLOR_END); + } + os << punc(":") + << color(ANSI_COLOR_GREEN) + << tmp->type().toString() + << color(ANSI_COLOR_END) + ; +} + +void print(const SSATmp* tmp) { + print(std::cerr, tmp); + std::cerr << std::endl; +} + +void print(const Trace* trace) { + print(std::cout, trace, nullptr); +} + +void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { + static const int kIndent = 4; + Disasm disasm(Disasm::Options().indent(kIndent + 4) + .printEncoding(dumpIREnabled(6)) + .color(color(ANSI_COLOR_BROWN))); + + // Print unlikely blocks at the end + BlockList blocks, unlikely; + for (Block* block : trace->getBlocks()) { + if (block->getHint() == Block::Unlikely) { + unlikely.push_back(block); + } else { + blocks.push_back(block); + } + } + blocks.splice(blocks.end(), unlikely); + + for (Block* block : blocks) { + TcaRange blockRange = asmInfo ? asmInfo->asmRanges[block] : + TcaRange(nullptr, nullptr); + for (auto it = block->begin(); it != block->end();) { + auto& inst = *it; ++it; + + if (inst.op() == Marker) { + os << std::string(kIndent, ' '); + JIT::print(os, &inst); + os << '\n'; + + // Don't print bytecode in a non-main trace. + if (!trace->isMain()) continue; + + auto* marker = inst.getExtra(); + uint32_t bcOffset = marker->bcOff; + if (const auto* func = marker->func) { + std::ostringstream uStr; + func->unit()->prettyPrint( + uStr, Unit::PrintOpts() + .range(bcOffset, bcOffset+1) + .noLineNumbers() + .indent(0)); + std::vector vec; + folly::split('\n', uStr.str(), vec); + for (auto& s : vec) { + os << color(ANSI_COLOR_BLUE) << s << color(ANSI_COLOR_END) << '\n'; + } + continue; + } + } + + if (inst.op() == DefLabel) { + os << std::string(kIndent - 2, ' '); + printLabel(os, inst.getBlock()); + os << punc(":") << "\n"; + // print phi pseudo-instructions + for (unsigned i = 0, n = inst.getNumDsts(); i < n; ++i) { + os << std::string(kIndent + + folly::format("({}) ", inst.getIId()).str().size(), + ' '); + JIT::print(os, inst.getDst(i), false); + os << punc(" = ") << color(ANSI_COLOR_CYAN) << "phi " + << color(ANSI_COLOR_END); + bool first = true; + inst.getBlock()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) { + if (!first) os << punc(", "); + first = false; + printSrc(os, jmp, i); + os << punc("@"); + printLabel(os, jmp->getBlock()); + }); + os << '\n'; + } + } + + os << std::string(kIndent, ' '); + JIT::print(os, &inst); + os << '\n'; + + if (asmInfo) { + TcaRange instRange = asmInfo->instRanges[inst]; + if (!instRange.empty()) { + disasm.disasm(os, instRange.begin(), instRange.end()); + os << '\n'; + assert(instRange.end() >= blockRange.start() && + instRange.end() <= blockRange.end()); + blockRange = TcaRange(instRange.end(), blockRange.end()); + } + } + } + + if (asmInfo) { + // print code associated with this block that isn't tied to any + // instruction. This includes code after the last isntruction (e.g. + // jmp to next block), and AStubs code. + if (!blockRange.empty()) { + os << std::string(kIndent, ' ') << punc("A:") << "\n"; + disasm.disasm(os, blockRange.start(), blockRange.end()); + } + auto astubRange = asmInfo->astubRanges[block]; + if (!astubRange.empty()) { + os << std::string(kIndent, ' ') << punc("AStubs:") << "\n"; + disasm.disasm(os, astubRange.start(), astubRange.end()); + } + if (!blockRange.empty() || !astubRange.empty()) { + os << '\n'; + } + } + } + + for (auto* exitTrace : trace->getExitTraces()) { + os << "\n" << color(ANSI_COLOR_GREEN) + << " ------- Exit Trace -------" + << color(ANSI_COLOR_END) << '\n'; + print(os, exitTrace, asmInfo); + } +} + +void dumpTraceImpl(const Trace* trace, std::ostream& out, + const AsmInfo* asmInfo) { + print(out, trace, asmInfo); +} + +// Suggested captions: "before jiffy removal", "after goat saturation", +// etc. +void dumpTrace(int level, const Trace* trace, const char* caption, + AsmInfo* ai) { + if (dumpIREnabled(level)) { + std::ostringstream str; + auto bannerFmt = "{:-^40}\n"; + str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN) + << folly::format(bannerFmt, caption) + << color(ANSI_COLOR_END) + ; + dumpTraceImpl(trace, str, ai); + str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN) + << folly::format(bannerFmt, "") + << color(ANSI_COLOR_END) + ; + HPHP::Trace::traceRelease("%s", str.str().c_str()); + } +} + +}}} + diff --git a/hphp/runtime/vm/translator/hopt/print.h b/hphp/runtime/vm/translator/hopt/print.h new file mode 100644 index 000000000..bb8f44789 --- /dev/null +++ b/hphp/runtime/vm/translator/hopt/print.h @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef incl_HPHP_VM_PRINT_H_ +#define incl_HPHP_VM_PRINT_H_ + +#include +#include "util/trace.h" + +namespace HPHP { +namespace VM { +namespace JIT { + +struct IRInstruction; +class SSATmp; +struct Block; +struct AsmInfo; +class Trace; + +// IRInstruction +void print(std::ostream& ostream, const IRInstruction*); +void print(const IRInstruction*); +void printSrc(std::ostream& ostream, const IRInstruction*, uint32_t srcIndex); + +// SSATmp +void print(std::ostream& ostream, const SSATmp*, + bool printLastUse = false); +void print(const SSATmp*); + +// Trace +void print(std::ostream& ostream, const Trace*, + const AsmInfo* asmInfo = nullptr); +void print(const Trace*); + +/* + * Some utilities related to dumping. Rather than file-by-file control, we + * control most IR logging via the hhir trace module. + */ +static inline bool dumpIREnabled(int level = 1) { + return HPHP::Trace::moduleEnabledRelease(HPHP::Trace::hhir, level); +} + +void dumpTraceImpl(const Trace* trace, std::ostream& out, + const AsmInfo* asmInfo = nullptr); +void dumpTrace(int level, const Trace* trace, const char* caption, + AsmInfo* ai = nullptr); + +}}} + +#endif