Arquivos
hhvm/hphp/runtime/vm/jit/print.cpp
T
Jordan DeLong 0a18064197 Add some unique_ptr-related support to smart:: containers
- smart::make_unique, returns a unique pointer to smart allocated
    data.

  - Support forwarding in our allocator's construct, so
    smart::vector<foo::unique_ptr<>> works.

  - Move smart containers to their own header.
2013-06-04 17:53:38 -07:00

447 linhas
14 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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 "hphp/runtime/vm/jit/print.h"
#include "hphp/runtime/base/memory/smart_containers.h"
#include "hphp/runtime/vm/jit/ir.h"
#include "hphp/runtime/vm/jit/linearscan.h"
#include "hphp/runtime/vm/jit/codegen.h"
#include "hphp/runtime/base/stats.h"
#include "hphp/util/disasm.h"
#include "hphp/util/text_color.h"
namespace HPHP { 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->typeParam();
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,
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
if (inst->numDsts() == 0) return;
const char* sep = "";
for (const SSATmp& dst : inst->dsts()) {
os << punc(sep);
print(os, &dst, regs, lifetime, true);
sep = ", ";
}
os << punc(" = ");
}
void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i,
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
SSATmp* src = inst->src(i);
if (src != nullptr) {
if (lifetime && lifetime->linear[inst] != 0 && !src->isConst() &&
lifetime->uses[src].lastUse == lifetime->linear[inst]) {
ostream << "~";
}
print(ostream, src, regs, lifetime);
} else {
ostream << color(ANSI_COLOR_RED)
<< "!!!NULL @ " << i
<< color(ANSI_COLOR_END)
;
}
}
void printSrcs(std::ostream& os, const IRInstruction* inst,
const RegAllocInfo* regs,
const LifetimeInfo* lifetime) {
bool first = true;
if (inst->op() == IncStat) {
os << " " << Stats::g_counterNames[inst->src(0)->getValInt()]
<< ", " << inst->src(1)->getValInt();
return;
}
for (uint32_t i = 0, n = inst->numSrcs(); i < n; i++) {
if (!first) {
os << punc(", ");
} else {
os << " ";
first = false;
}
printSrc(os, inst, i, regs, lifetime);
}
}
void printLabel(std::ostream& os, const Block* block) {
os << color(ANSI_COLOR_MAGENTA);
os << "L" << block->id();
switch (block->hint()) {
case Block::Unlikely: os << "<Unlikely>"; break;
case Block::Likely: os << "<Likely>"; break;
default:
break;
}
os << color(ANSI_COLOR_END);
}
void print(std::ostream& ostream, const IRInstruction* inst,
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
if (inst->op() == Marker) {
auto* marker = inst->extra<Marker>();
ostream << color(ANSI_COLOR_BLUE)
<< marker->show()
<< color(ANSI_COLOR_END);
return;
}
if (!inst->isTransient()) {
ostream << color(ANSI_COLOR_YELLOW);
if (!lifetime || !lifetime->linear[inst]) {
ostream << folly::format("({:02d}) ", inst->id());
} else {
ostream << folly::format("({:02d}@{:02d}) ", inst->id(),
lifetime->linear[inst]);
}
ostream << color(ANSI_COLOR_END);
}
printDst(ostream, inst, regs, lifetime);
printOpcode(ostream, inst);
printSrcs(ostream, inst, regs, lifetime);
if (Block* taken = inst->taken()) {
ostream << punc(" -> ");
printLabel(ostream, taken);
}
}
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->typeParam();
auto c = inst->extra<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->extra<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, const RegAllocInfo* regs,
const LifetimeInfo* lifetime, bool printLastUse) {
if (tmp->inst()->op() == DefConst) {
printConst(os, tmp->inst());
return;
}
os << color(ANSI_COLOR_WHITE);
os << "t" << tmp->id();
os << color(ANSI_COLOR_END);
if (printLastUse && lifetime && lifetime->uses[tmp].lastUse != 0) {
os << color(ANSI_COLOR_GRAY)
<< "@" << lifetime->uses[tmp].lastUse << "#" << lifetime->uses[tmp].count
<< color(ANSI_COLOR_END);
}
if (regs) {
const RegisterInfo& info = (*regs)[tmp];
if (info.spilled() || info.numAllocatedRegs() > 0) {
os << color(ANSI_COLOR_BROWN) << '(';
if (!info.spilled()) {
for (int i = 0, sz = info.numAllocatedRegs(); i < sz; ++i) {
if (i != 0) os << ",";
PhysReg reg = info.reg(i);
if (reg.type() == PhysReg::GP) {
os << reg::regname(Reg64(reg));
} else {
os << reg::regname(RegXMM(reg));
}
}
} else {
for (int i = 0, sz = tmp->numNeededRegs(); i < sz; ++i) {
if (i != 0) os << ",";
os << info.spillInfo(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);
}
// Print unlikely blocks at the end in normal generation. If we have
// asmInfo, order the blocks based on how they were layed out.
static smart::vector<Block*> blocks(const Trace* trace,
const AsmInfo* asmInfo) {
smart::vector<Block*> blocks;
if (!asmInfo) {
smart::vector<Block*> unlikely;
for (Block* block : trace->blocks()) {
if (block->hint() == Block::Unlikely) {
unlikely.push_back(block);
} else {
blocks.push_back(block);
}
}
for (Trace* e : trace->exitTraces()) {
unlikely.insert(unlikely.end(),
e->blocks().begin(),
e->blocks().end());
}
blocks.insert(blocks.end(), unlikely.begin(), unlikely.end());
return blocks;
}
blocks.assign(trace->blocks().begin(), trace->blocks().end());
for (Trace* e : trace->exitTraces()) {
blocks.insert(blocks.end(), e->blocks().begin(), e->blocks().end());
}
std::sort(
blocks.begin(),
blocks.end(),
[&] (Block* a, Block* b) {
return asmInfo->asmRanges[a].begin() < asmInfo->asmRanges[b].begin();
}
);
return blocks;
}
void print(std::ostream& os, const Trace* trace, const RegAllocInfo* regs,
const LifetimeInfo* lifetime, const AsmInfo* asmInfo) {
static const int kIndent = 4;
Disasm disasm(Disasm::Options().indent(kIndent + 4)
.printEncoding(dumpIREnabled(kExtraLevel))
.color(color(ANSI_COLOR_BROWN)));
for (Block* block : blocks(trace, asmInfo)) {
if (!block->isMain()) {
os << "\n" << color(ANSI_COLOR_GREEN)
<< " ------- Exit Trace -------"
<< color(ANSI_COLOR_END) << '\n';
}
TcaRange blockRange = asmInfo ? asmInfo->asmRanges[block] :
TcaRange(nullptr, nullptr);
os << std::string(kIndent - 2, ' ');
printLabel(os, block);
os << punc(":") << "\n";
for (auto it = block->begin(); it != block->end();) {
auto& inst = *it; ++it;
if (inst.op() == Marker) {
os << std::string(kIndent, ' ');
JIT::print(os, &inst, regs, lifetime);
os << '\n';
// Don't print bytecode in a non-main trace.
if (!trace->isMain()) continue;
auto* marker = inst.extra<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) {
// print phi pseudo-instructions
for (unsigned i = 0, n = inst.numDsts(); i < n; ++i) {
os << std::string(kIndent +
folly::format("({}) ", inst.id()).str().size(),
' ');
JIT::print(os, inst.dst(i), regs, lifetime, false);
os << punc(" = ") << color(ANSI_COLOR_CYAN) << "phi "
<< color(ANSI_COLOR_END);
bool first = true;
inst.block()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) {
if (!first) os << punc(", ");
first = false;
printSrc(os, jmp, i, regs, lifetime);
os << punc("@");
printLabel(os, jmp->block());
});
os << '\n';
}
}
os << std::string(kIndent, ' ');
JIT::print(os, &inst, regs, lifetime);
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';
}
}
}
}
void dumpTraceImpl(const Trace* trace,
std::ostream& out,
const RegAllocInfo* regs,
const LifetimeInfo* lifetime,
const AsmInfo* asmInfo) {
print(out, trace, regs, lifetime, asmInfo);
}
// Suggested captions: "before jiffy removal", "after goat saturation",
// etc.
void dumpTrace(int level, const Trace* trace, const char* caption,
const RegAllocInfo* regs, const LifetimeInfo* lifetime,
AsmInfo* ai) {
if (dumpIREnabled(level)) {
std::ostringstream str;
auto bannerFmt = "{:-^80}\n";
str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN)
<< folly::format(bannerFmt, caption)
<< color(ANSI_COLOR_END)
;
dumpTraceImpl(trace, str, regs, lifetime, ai);
str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN)
<< folly::format(bannerFmt, "")
<< color(ANSI_COLOR_END)
;
HPHP::Trace::traceRelease("%s\n", str.str().c_str());
}
}
}}