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.
Esse commit está contido em:
@@ -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
|
||||
|
||||
@@ -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<T,std::string () const>::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<std::string,ShowDispatcher>(opc, data);
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string showExtra(Opcode opc, const IRExtraData* data) {
|
||||
return dispatchExtra<std::string,ShowDispatcher>(opc,
|
||||
const_cast<IRExtraData*>(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<Marker>();
|
||||
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<DefConst>();
|
||||
if (t == Type::Int) {
|
||||
os << c->as<int64_t>();
|
||||
} else if (t == Type::Dbl) {
|
||||
os << c->as<double>();
|
||||
} else if (t == Type::Bool) {
|
||||
os << (c->as<bool>() ? "true" : "false");
|
||||
} else if (t.isString()) {
|
||||
auto str = c->as<const StringData*>();
|
||||
os << "\""
|
||||
<< Util::escapeStringForCPP(str->data(), str->size())
|
||||
<< "\"";
|
||||
} else if (t.isArray()) {
|
||||
auto arr = inst->getExtra<DefConst>()->as<const ArrayData*>();
|
||||
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<const Func*>();
|
||||
os << "Func(" << (func ? func->fullName()->data() : "0") << ")";
|
||||
} else if (t.subtypeOf(Type::Cls)) {
|
||||
auto cls = c->as<const Class*>();
|
||||
os << "Cls(" << (cls ? cls->name()->data() : "0") << ")";
|
||||
} else if (t.subtypeOf(Type::NamedEntity)) {
|
||||
auto ne = c->as<const NamedEntity*>();
|
||||
os << "NamedEntity(" << ne << ")";
|
||||
} else if (t.subtypeOf(Type::TCA)) {
|
||||
TCA tca = c->as<TCA>();
|
||||
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<int64_t>();
|
||||
} else if (t.isPtr()) {
|
||||
os << folly::format("{}({:#x})", t.toString(), c->as<uint64_t>());
|
||||
} else if (t.subtypeOf(Type::CacheHandle)) {
|
||||
os << folly::format("CacheHandle({:#x})", c->as<int64_t>());
|
||||
} else {
|
||||
not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
void Block::printLabel(std::ostream& os) const {
|
||||
os << color(ANSI_COLOR_MAGENTA);
|
||||
os << "L" << m_id;
|
||||
if (getHint() == Unlikely) {
|
||||
os << "<Unlikely>";
|
||||
}
|
||||
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<Marker>();
|
||||
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<std::string> 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());
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -886,6 +886,8 @@ template<class T> 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<Block*>& getBlocks() { return m_blocks; }
|
||||
const std::list<Block*>& 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<Trace*>::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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 <boost/noncopyable.hpp>
|
||||
|
||||
namespace HPHP {
|
||||
namespace VM {
|
||||
@@ -278,7 +286,7 @@ template<typename Inner, int DumpVal=4>
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,11 @@
|
||||
#ifndef incl_HPHP_VM_LINEAR_SCAN_H_
|
||||
#define incl_HPHP_VM_LINEAR_SCAN_H_
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 << "<Unlikely>";
|
||||
os << color(ANSI_COLOR_END);
|
||||
}
|
||||
|
||||
void print(std::ostream& ostream, const IRInstruction* inst) {
|
||||
if (inst->op() == Marker) {
|
||||
auto* marker = inst->getExtra<Marker>();
|
||||
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<DefConst>();
|
||||
if (t == Type::Int) {
|
||||
os << c->as<int64_t>();
|
||||
} else if (t == Type::Dbl) {
|
||||
os << c->as<double>();
|
||||
} else if (t == Type::Bool) {
|
||||
os << (c->as<bool>() ? "true" : "false");
|
||||
} else if (t.isString()) {
|
||||
auto str = c->as<const StringData*>();
|
||||
os << "\""
|
||||
<< Util::escapeStringForCPP(str->data(), str->size())
|
||||
<< "\"";
|
||||
} else if (t.isArray()) {
|
||||
auto arr = inst->getExtra<DefConst>()->as<const ArrayData*>();
|
||||
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<const Func*>();
|
||||
os << "Func(" << (func ? func->fullName()->data() : "0") << ")";
|
||||
} else if (t.subtypeOf(Type::Cls)) {
|
||||
auto cls = c->as<const Class*>();
|
||||
os << "Cls(" << (cls ? cls->name()->data() : "0") << ")";
|
||||
} else if (t.subtypeOf(Type::NamedEntity)) {
|
||||
auto ne = c->as<const NamedEntity*>();
|
||||
os << "NamedEntity(" << ne << ")";
|
||||
} else if (t.subtypeOf(Type::TCA)) {
|
||||
TCA tca = c->as<TCA>();
|
||||
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<int64_t>();
|
||||
} else if (t.isPtr()) {
|
||||
os << folly::format("{}({:#x})", t.toString(), c->as<uint64_t>());
|
||||
} else if (t.subtypeOf(Type::CacheHandle)) {
|
||||
os << folly::format("CacheHandle({:#x})", c->as<int64_t>());
|
||||
} 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<Marker>();
|
||||
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<std::string> 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());
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
@@ -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 <iosfwd>
|
||||
#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
|
||||
Referência em uma Nova Issue
Bloquear um usuário