Remove dead includes in hphp/runtime/vm
[hiphop-php.git] / hphp / runtime / vm / jit / print.cpp
blob005c8053c21ddb0196f904e8e0c0f6a85183005a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/print.h"
19 #include <folly/dynamic.h>
20 #include <folly/json.h>
22 #include <iostream>
23 #include <sstream>
24 #include <vector>
25 #include <algorithm>
27 #include "hphp/util/arch.h"
28 #include "hphp/util/disasm.h"
29 #include "hphp/util/struct-log.h"
30 #include "hphp/util/text-color.h"
31 #include "hphp/util/text-util.h"
33 #include "hphp/runtime/base/rds.h"
34 #include "hphp/runtime/base/stats.h"
36 #include "hphp/runtime/vm/jit/array-access-profile.h"
37 #include "hphp/runtime/vm/jit/array-kind-profile.h"
38 #include "hphp/runtime/vm/jit/array-iter-profile.h"
39 #include "hphp/runtime/vm/jit/asm-info.h"
40 #include "hphp/runtime/vm/jit/block.h"
41 #include "hphp/runtime/vm/jit/call-target-profile.h"
42 #include "hphp/runtime/vm/jit/cfg.h"
43 #include "hphp/runtime/vm/jit/cls-cns-profile.h"
44 #include "hphp/runtime/vm/jit/decref-profile.h"
45 #include "hphp/runtime/vm/jit/incref-profile.h"
46 #include "hphp/runtime/vm/jit/containers.h"
47 #include "hphp/runtime/vm/jit/guard-constraints.h"
48 #include "hphp/runtime/vm/jit/ir-opcode.h"
49 #include "hphp/runtime/vm/jit/mcgen.h"
50 #include "hphp/runtime/vm/jit/meth-profile.h"
51 #include "hphp/runtime/vm/jit/release-vv-profile.h"
52 #include "hphp/runtime/vm/jit/switch-profile.h"
53 #include "hphp/runtime/vm/jit/type-profile.h"
55 #include "hphp/ppc64-asm/asm-ppc64.h"
56 #include "hphp/ppc64-asm/dasm-ppc64.h"
58 #include "hphp/vixl/a64/disasm-a64.h"
60 namespace HPHP { namespace jit {
62 ///////////////////////////////////////////////////////////////////////////////
64 namespace {
66 ///////////////////////////////////////////////////////////////////////////////
68 // Helper for pretty-printing punctuation.
69 std::string punc(const char* str) {
70 return folly::format("{}{}{}",
71 color(ANSI_COLOR_DARK_GRAY), str, color(ANSI_COLOR_END)).str();
74 std::string constToString(Type t) {
75 std::ostringstream os;
76 os << color(ANSI_COLOR_LIGHT_BLUE)
77 << t.constValString()
78 << color(ANSI_COLOR_END);
79 return os.str();
82 void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i) {
83 SSATmp* src = inst->src(i);
84 if (src != nullptr) {
85 print(ostream, src);
86 } else {
87 ostream << color(ANSI_COLOR_RED)
88 << "!!!NULL @ " << i
89 << color(ANSI_COLOR_END)
94 void printLabel(std::ostream& os, const Block* block) {
95 os << color(ANSI_COLOR_MAGENTA);
96 os << "B" << block->id();
97 if (block->isCatch()) {
98 os << "<Catch>";
99 } else {
100 switch (block->hint()) {
101 case Block::Hint::Unused: os << "<Unused>"; break;
102 case Block::Hint::Unlikely: os << "<Unlikely>"; break;
103 case Block::Hint::Likely: os << "<Likely>"; break;
104 default:
105 break;
108 os << color(ANSI_COLOR_END);
111 // Simple tuple-like class used to order instructions for printing.
112 struct InstAreaRange {
113 // order by instruction index, area then instruction range.
114 bool operator<(const InstAreaRange& other) const {
115 return (m_instIdx < other.m_instIdx ||
116 (m_instIdx == other.m_instIdx &&
117 (m_area < other.m_area ||
118 (m_area == other.m_area &&
119 (m_instRange.begin() < other.m_instRange.begin() ||
120 (m_instRange.begin() == other.m_instRange.begin() &&
121 (m_instRange.end() < other.m_instRange.end())))))));
124 bool isContiguous(const InstAreaRange& other) const {
125 // TODO(T52857006) - check assertions as described in D16623372
126 return (m_instRange.end() == other.m_instRange.begin() &&
127 m_area == other.m_area &&
128 m_instIdx == other.m_instIdx);
131 InstAreaRange(size_t instIdx,
132 AreaIndex area,
133 TcaRange instRange)
134 : m_instIdx(instIdx),
135 m_area(area),
136 m_instRange(instRange)
139 size_t m_instIdx{0};
140 AreaIndex m_area{AreaIndex::Main};
141 TcaRange m_instRange;
144 bool dumpPrettyIR(int level) {
145 return HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir, level) ||
146 (RuntimeOption::EvalDumpIR >= level);
149 bool dumpJsonIR(int level) {
150 return HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir_json, level) ||
151 (RuntimeOption::EvalDumpIRJson >= level);
154 bool dumpRuntimeIR(int level) {
155 return RuntimeOption::EvalDumpIR >= level ||
156 RuntimeOption::EvalDumpIRJson >= level;
159 ///////////////////////////////////////////////////////////////////////////////
163 static constexpr auto kIndent = 4;
165 namespace get_json {
166 using folly::dynamic;
168 dynamic getSSATmp(const SSATmp* tmp) {
169 return dynamic::object("id", tmp->id())
170 ("type", tmp->type().toString());
173 dynamic getLabel(const Block* block) {
174 dynamic id = block->id();
176 return dynamic::object("id", id)
177 ("isCatch", block->isCatch())
178 ("hint", blockHintName(block->hint()));
182 dynamic getOpcode(const IRInstruction* inst,
183 const GuardConstraints* constraints) {
184 const dynamic typeParam = inst->hasTypeParam() ?
185 inst->typeParam().toString() :
186 dynamic(nullptr);
187 const dynamic extra = inst->hasExtra() ?
188 showExtra(inst->op(), inst->rawExtra()) :
189 dynamic(nullptr);
191 const bool isGuard = constraints &&
192 !inst->isTransient() &&
193 isGuardOp(inst->op());
194 dynamic guard;
195 if (isGuard) {
196 auto const it = constraints->guards.find(inst);
197 guard = (it == constraints->guards.end() ?
198 "unused" :
199 it->second.toString());
200 } else {
201 guard = dynamic(nullptr);
204 return dynamic::object("opcodeName", opcodeName(inst->op()))
205 ("typeParam", typeParam)
206 ("extra", extra)
207 ("guard", guard);
210 dynamic getSrcs(const IRInstruction* inst) {
211 // TODO(T52857257)
212 if (inst->op() == IncStat) {
213 return dynamic::object("counterName",
214 Stats::g_counterNames[inst->src(0)->intVal()]);
216 dynamic srcs = dynamic::array;
217 for (uint32_t i = 0, n = inst->numSrcs(); i < n; i++) {
218 srcs.push_back(getSSATmp(inst->src(i)));
220 return srcs;
223 dynamic getDsts(const IRInstruction* inst) {
224 dynamic dsts = dynamic::array;
225 for (uint32_t i = 0, n = inst->numDsts(); i < n; i++) {
226 dsts.push_back(getSSATmp(inst->dst(i)));
228 return dsts;
231 dynamic getIRInstruction(const IRInstruction& inst,
232 const GuardConstraints* guards) {
233 dynamic result = dynamic::object;
234 dynamic markerObj = dynamic::object;
235 std::ostringstream mStr;
236 std::ostringstream funcStr;
237 auto const& newMarker = inst.marker();
238 if (!newMarker.hasFunc()) {
239 markerObj = dynamic(nullptr);
240 } else {
241 auto func = newMarker.func();
242 func->prettyPrint(mStr, Func::PrintOpts());
243 mStr << std::string(kIndent, ' ')
244 << newMarker.show()
245 << '\n';
247 auto const bcOffset = newMarker.bcOff();
248 // TODO(T46690139)
249 func->unit()->prettyPrint(
250 mStr, Unit::PrintOpts()
251 .range(bcOffset, bcOffset+1)
252 .noLineNumbers()
253 .noFuncs()
254 .indent(0));
255 std::vector<std::string> vec;
256 folly::split('\n', mStr.str(), vec);
257 for (auto const& s : vec) {
258 if (s.empty()) continue;
259 funcStr << s << '\n';
261 markerObj["raw"] = funcStr.str();
263 result["marker"] = markerObj;
265 dynamic phiPseudoInstrs = dynamic::array;
266 if (inst.op() == DefLabel) {
267 // print phi pseudo-instructions
268 for (unsigned i = 0, n = inst.numDsts(); i < n; ++i) {
269 dynamic phiPseudoInstr = dynamic::object;
270 phiPseudoInstr["dst"] = getSSATmp(inst.dst(i));
272 dynamic srcs = dynamic::array;
273 inst.block()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) {
274 srcs.push_back(dynamic::object("src", getSSATmp(jmp->src(i)))
275 ("label", getLabel(jmp->block())));
277 phiPseudoInstr["srcs"] = srcs;
279 phiPseudoInstrs.push_back(phiPseudoInstr);
282 result["phiPseudoInstrs"] = phiPseudoInstrs;
284 const dynamic id = inst.isTransient() ? dynamic(nullptr) : inst.id();
286 const Block* taken = inst.taken();
287 const dynamic takenObj = taken ? getLabel(taken) : dynamic(nullptr);
289 auto const offset = inst.marker().bcOff() + inst.iroff();
290 result.update(dynamic::merge(getOpcode(&inst, guards),
291 dynamic::object("id", id)
292 ("taken", takenObj)
293 ("srcs", getSrcs(&inst))
294 ("dsts", getDsts(&inst))
295 ("offset", offset)));
296 return result;
299 dynamic getTCRange(const AreaIndex area,
300 const TransKind kind,
301 const TcaRange& range) {
302 std::ostringstream disasmStr;
303 disasmRange(disasmStr, kind, range);
304 auto const startStr = folly::sformat("{}", static_cast<void*>(range.begin()));
305 auto const endStr = folly::sformat("{}", static_cast<void*>(range.end()));
306 return dynamic::object("area", areaAsString(area))
307 ("start", startStr)
308 ("end", endStr)
309 ("disasm", disasmStr.str());
312 namespace {
313 // TODO(T52856776) - move into AnnotationData class
314 struct TargetProfileVisitor {
315 explicit TargetProfileVisitor(const TransContext& ctx) : ctx(ctx) {}
317 template<typename T>
318 dynamic getProfileDynamic(const rds::Profile<T>& prof,
319 const dynamic& linkObj) const {
320 return dynamic::object("offset", prof.bcOff)
321 ("name", folly::sformat("{}", prof.name))
322 ("data", linkObj);
325 dynamic operator() (const rds::Profile<jit::SwitchProfile>& prof) const {
326 auto const link = rds::bind<jit::SwitchProfile, rds::Mode::Local>(prof);
327 auto const func = ctx.initSrcKey.func();
328 assert(func->contains(prof.bcOff));
329 auto const funcBCOffset = func->unit()->at(func->base() + prof.bcOff);
330 auto const bcSize = getImmVector(funcBCOffset).size();
331 return getProfileDynamic(prof, link.get()->toDynamic(bcSize));
334 template<typename T>
335 dynamic operator() (const rds::Profile<T>& prof) const {
336 auto const link = rds::bind<T, rds::Mode::Local>(prof);
337 return getProfileDynamic(prof, link.get()->toDynamic());;
340 template<typename T>
341 dynamic operator() (T&&) const {
342 assertx(false);
343 return dynamic();
346 const TransContext& ctx;
350 dynamic getBlock(const Block* block,
351 const TransKind kind,
352 const AsmInfo* asmInfo,
353 const GuardConstraints* guards,
354 const IRUnit& unit) {
355 dynamic result = dynamic::object;
357 result["label"] = getLabel(block);
358 result["profCount"] = block->profCount();
360 dynamic predIds = dynamic::array;
362 auto const& preds = block->preds();
363 if (!preds.empty()) {
364 for (auto const& edge : preds) {
365 predIds.push_back(edge.from()->id());
368 result["preds"] = predIds;
369 result["next"] = block->next() ? getLabel(block->next()) : dynamic(nullptr);
370 result["instrs"] = dynamic::array;
372 if (block->empty()) return result;
374 if (asmInfo) {
375 std::vector<const IRInstruction*> instrs;
376 std::vector<InstAreaRange> instRanges;
377 std::array<TcaRange, kNumAreas> lastRange;
378 size_t instIdx = 0;
380 // Collect all the instruction ranges for the current block and sort
381 // them. Entries will be sorted by instruction index, area then machine
382 // code pc. This reflects the order in which code will be printed.
383 // IR instructions with no associated assembly will still get entries
384 // in the instRanges vector so they will be printed too (they just get
385 // an empty machine code range).
386 for (auto it = block->begin(); it != block->end(); ++it, ++instIdx) {
387 const auto& inst = *it;
388 const size_t lastInstRangesSize = instRanges.size();
389 instrs.push_back(&inst); // Map back to IRInstruction from index.
391 for (auto i = 0; i < kNumAreas; ++i) {
392 const auto instArea = static_cast<AreaIndex>(i);
393 auto const& areaRanges = asmInfo->instRangesForArea(instArea);
394 auto const& rngs = areaRanges[inst];
395 for (auto itr = rngs.first; itr != rngs.second; ++itr) {
396 auto const range = TcaRange(itr->second.start() + areaRanges.offset,
397 itr->second.end() + areaRanges.offset);
398 instRanges.push_back(InstAreaRange(instIdx, instArea, range));
399 lastRange[(int)instArea] = range;
403 // Add an entry for IRInstructions that have no associated machine
404 // code. Use the end address of the last instruction range to assign
405 // an empty range to this element.
406 if (instRanges.size() == lastInstRangesSize) {
407 instRanges.push_back(
408 InstAreaRange(instIdx,
409 AreaIndex::Main,
410 TcaRange(lastRange[(int)AreaIndex::Main].end(),
411 lastRange[(int)AreaIndex::Main].end())));
415 std::sort(instRanges.begin(), instRanges.end());
417 std::vector<InstAreaRange> collatedInstRanges;
418 for (auto const& inst : instRanges) {
419 if (collatedInstRanges.empty()) {
420 collatedInstRanges.push_back(inst);
421 continue;
424 auto& prevRange = collatedInstRanges.back();
425 if (prevRange.isContiguous(inst)) {
426 prevRange.m_instRange = TcaRange(prevRange.m_instRange.begin(),
427 inst.m_instRange.end());
428 } else {
429 collatedInstRanges.push_back(inst);
432 instRanges = collatedInstRanges;
434 const IRInstruction* lastInst = nullptr;
435 AreaIndex lastArea = AreaIndex::Main;
436 bool printArea = false;
438 dynamic currInstrObj = (dynamic) nullptr;
440 for (auto itr = instRanges.begin(); itr != instRanges.end(); ++itr) {
441 auto const currInstIdx = itr->m_instIdx;
442 auto const currInst = instrs[currInstIdx];
443 auto const currArea = itr->m_area;
444 auto const instRange = itr->m_instRange;
445 if (lastInst != currInst) {
446 if (!currInstrObj.isNull()) {
447 result["instrs"].push_back(currInstrObj);
449 currInstrObj = dynamic::merge(getIRInstruction(*currInst, guards),
450 dynamic::object("tc_ranges",
451 dynamic::array));
452 printArea = true;
453 lastInst = currInst;
454 lastRange[(int)currArea] = TcaRange(nullptr,nullptr);
456 if (printArea || currArea != lastArea) {
457 lastArea = currArea;
458 printArea = false;
459 lastRange[(int)currArea] = TcaRange(nullptr,nullptr);
462 const auto lastEnd = lastRange[(int)currArea].end();
464 if (lastEnd && lastEnd != instRange.begin()) {
465 // There may be gaps between instruction ranges that have been
466 // added by the relocator, e.g. adding nops. This check will
467 // determine if the gap belongs to another instruction or not.
468 // If it doesn't belong to any other instruction then print it.
469 auto const offset = asmInfo->instRangesForArea(currArea).offset;
470 auto const gapRange = TcaRange(lastEnd - offset,
471 instRange.begin() - offset);
472 if (!asmInfo->instRangeExists(currArea, gapRange)) {
473 currInstrObj["tc_ranges"].push_back(getTCRange(currArea,
474 kind,
475 gapRange));
478 currInstrObj["tc_ranges"].push_back(getTCRange(currArea,
479 kind,
480 instRange));
481 lastRange[(int)currArea] = instRange;
483 if (!currInstrObj.isNull()) {
484 result["instrs"].push_back(currInstrObj);
486 } else {
487 for (auto it = block->begin(); it != block->end(); ++it) {
488 result["instrs"].push_back(dynamic::merge(getIRInstruction(*it, guards),
489 dynamic::object("tc_ranges",
490 dynamic())));
494 auto const& ctx = unit.context();
496 for (auto& inst : result["instrs"]) {
497 auto const& offset = inst["offset"].asInt();
498 dynamic profileObjs = dynamic::array;
499 for (auto const& key : unit.annotationData->profileKeys) {
500 auto const profile = boost::apply_visitor(TargetProfileVisitor(ctx), key);
501 if (profile["offset"].asInt() == offset) profileObjs.push_back(profile);
503 inst["profileData"] = profileObjs;
506 return result;
509 dynamic getSrcKey(const SrcKey& sk) {
510 return dynamic::object("func", sk.func()->name()->slice())
511 ("unit", sk.unit()->filepath()->slice())
512 ("prologue", sk.prologue())
513 ("offset", sk.offset())
514 ("resumeMode", resumeModeShortName(sk.resumeMode()))
515 ("hasThis", sk.hasThis());
518 dynamic getTransContext(const TransContext& ctx) {
519 auto const func = ctx.initSrcKey.func();
520 return dynamic::object("kind", show(ctx.kind))
521 ("id", ctx.transID)
522 ("optIndex", ctx.optIndex)
523 ("srcKey", getSrcKey(ctx.initSrcKey))
524 ("funcName", func->fullName()->data())
525 ("sourceFile", func->filename()->data())
526 ("startLine", func->line1())
527 ("endLine", func->line2());
530 dynamic getUnit(const IRUnit& unit,
531 const AsmInfo* asmInfo,
532 const GuardConstraints* guards) {
533 dynamic result = dynamic::object;
535 auto const& ctx = unit.context();
536 auto const kind = ctx.kind;
537 result["translation"] = getTransContext(ctx);
539 result["inliningDecisions"] = unit.annotationData ?
540 unit.annotationData->getInliningDynamic() :
541 dynamic::array();
543 auto blocks = rpoSortCfg(unit);
544 // Partition into main, cold and frozen, without changing relative order.
545 auto const cold = std::stable_partition(blocks.begin(), blocks.end(),
546 [&] (Block* b) {
547 return b->hint() == Block::Hint::Neither ||
548 b->hint() == Block::Hint::Likely;
551 auto const frozen = std::stable_partition(cold, blocks.end(),
552 [&] (Block* b) { return b->hint() == Block::Hint::Unlikely; }
555 dynamic blockObjs = dynamic::array;
557 AreaIndex currArea = AreaIndex::Main;
558 for (auto it = blocks.begin(); it != blocks.end(); ++it) {
559 if (it == cold) {
560 currArea = AreaIndex::Cold;
562 if (it == frozen) {
563 currArea = AreaIndex::Frozen;
566 blockObjs.push_back(dynamic::merge(
567 getBlock(*it, kind, asmInfo, guards, unit),
568 dynamic::object("area", areaAsString(currArea))));
570 result["blocks"] = blockObjs;
571 return result;
576 ///////////////////////////////////////////////////////////////////////////////
577 // IRInstruction.
579 void printOpcode(std::ostream& os, const IRInstruction* inst,
580 const GuardConstraints* constraints) {
581 os << color(ANSI_COLOR_CYAN)
582 << opcodeName(inst->op())
583 << color(ANSI_COLOR_END)
586 auto const hasTypeParam = inst->hasTypeParam();
587 auto const hasExtra = inst->hasExtra();
588 auto const isGuard =
589 constraints && !inst->isTransient() && isGuardOp(inst->op());
591 if (!hasTypeParam && !hasExtra && !isGuard) return;
592 os << color(ANSI_COLOR_LIGHT_BLUE) << '<' << color(ANSI_COLOR_END);
594 if (hasTypeParam) {
595 os << color(ANSI_COLOR_GREEN)
596 << inst->typeParam().toString()
597 << color(ANSI_COLOR_END)
599 if (hasExtra || isGuard) os << punc(",");
602 if (hasExtra) {
603 os << color(ANSI_COLOR_GREEN)
604 << showExtra(inst->op(), inst->rawExtra())
605 << color(ANSI_COLOR_END);
606 if (isGuard) os << punc(",");
609 if (isGuard) {
610 auto it = constraints->guards.find(inst);
611 os << (it == constraints->guards.end() ? "unused" : it->second.toString());
614 os << color(ANSI_COLOR_LIGHT_BLUE)
615 << '>'
616 << color(ANSI_COLOR_END);
619 void printSrcs(std::ostream& os, const IRInstruction* inst) {
620 bool first = true;
621 if (inst->op() == IncStat) {
622 os << " " << Stats::g_counterNames[inst->src(0)->intVal()];
623 return;
625 for (uint32_t i = 0, n = inst->numSrcs(); i < n; i++) {
626 if (!first) {
627 os << punc(", ");
628 } else {
629 os << " ";
630 first = false;
632 printSrc(os, inst, i);
636 void printDsts(std::ostream& os, const IRInstruction* inst) {
637 const char* sep = "";
638 for (unsigned i = 0, n = inst->numDsts(); i < n; i++) {
639 os << punc(sep);
640 print(os, inst->dst(i));
641 sep = ", ";
645 void printInstr(std::ostream& ostream, const IRInstruction* inst,
646 const GuardConstraints* guards) {
647 printDsts(ostream, inst);
648 if (inst->numDsts()) ostream << punc(" = ");
649 printOpcode(ostream, inst, guards);
650 printSrcs(ostream, inst);
653 void print(std::ostream& ostream, const IRInstruction* inst,
654 const GuardConstraints* guards) {
655 if (!inst->isTransient()) {
656 ostream << color(ANSI_COLOR_YELLOW);
657 ostream << folly::format("({:02d}) ", inst->id());
658 ostream << color(ANSI_COLOR_END);
660 printInstr(ostream, inst, guards);
661 if (Block* taken = inst->taken()) {
662 ostream << punc(" -> ");
663 printLabel(ostream, taken);
667 void print(const IRInstruction* inst) {
668 print(std::cerr, inst);
669 std::cerr << std::endl;
672 ///////////////////////////////////////////////////////////////////////////////
673 // SSATmp.
675 void print(std::ostream& os, const SSATmp* tmp) {
676 if (tmp->inst()->is(DefConst)) {
677 os << constToString(tmp->type());
678 return;
680 os << color(ANSI_COLOR_WHITE);
681 os << "t" << tmp->id();
682 os << color(ANSI_COLOR_END);
683 os << punc(":")
684 << color(ANSI_COLOR_GREEN)
685 << tmp->type().toString()
686 << color(ANSI_COLOR_END)
690 void print(const SSATmp* tmp) {
691 print(std::cerr, tmp);
692 std::cerr << std::endl;
695 ///////////////////////////////////////////////////////////////////////////////
696 // Block.
698 void disasmRange(std::ostream& os,
699 TransKind kind,
700 TCA begin,
701 TCA end,
702 bool useColor) {
703 assertx(begin <= end);
704 int const indent = kIndent + 4;
705 bool const printEncoding = dumpIREnabled(kind, kAsmEncodingLevel);
706 char const* colorStr = useColor ? color(ANSI_COLOR_BROWN) : "";
708 switch (arch()) {
709 case Arch::X64: {
710 Disasm disasm(Disasm::Options().indent(indent)
711 .printEncoding(printEncoding)
712 .color(colorStr));
713 disasm.disasm(os, begin, end);
714 return;
717 case Arch::ARM: {
718 vixl::Decoder dec;
719 vixl::PrintDisassembler disasm(os, indent, printEncoding, colorStr);
720 disasm.setShouldDereferencePCRelativeLiterals(true);
721 dec.AppendVisitor(&disasm);
722 for (; begin < end; begin += vixl::kInstructionSize) {
723 dec.Decode(vixl::Instruction::Cast(begin));
725 return;
728 case Arch::PPC64: {
729 ppc64_asm::Disassembler disasm(printEncoding, true, indent, colorStr);
730 for (; begin < end; begin += ppc64_asm::instr_size_in_bytes) {
731 disasm.disassembly(os, begin);
733 return;
736 not_reached();
739 template <typename T>
740 std::vector<TcaRange> makeSortedRanges(const T& itrPair) {
741 std::vector<TcaRange> ranges;
742 for (auto itr = itrPair.first; itr != itrPair.second; ++itr) {
743 ranges.push_back(itr->second);
745 std::sort(ranges.begin(),
746 ranges.end(),
747 [](const TcaRange& a, const TcaRange& b) {
748 return a.begin() < b.begin();
750 return ranges;
753 void printIRInstruction(std::ostream& os,
754 const IRInstruction& inst,
755 const GuardConstraints* guards,
756 BCMarker& curMarker,
757 const char*& markerEndl) {
758 if (inst.marker() != curMarker) {
759 std::ostringstream mStr;
760 auto const& newMarker = inst.marker();
761 if (!newMarker.hasFunc()) {
762 os << color(ANSI_COLOR_BLUE)
763 << std::string(kIndent, ' ')
764 << "--- invalid marker"
765 << color(ANSI_COLOR_END)
766 << '\n';
767 } else {
768 auto func = newMarker.func();
769 if (!curMarker.hasFunc() || func != curMarker.func()) {
770 func->prettyPrint(mStr, Func::PrintOpts());
772 mStr << std::string(kIndent, ' ')
773 << newMarker.show()
774 << '\n';
776 auto bcOffset = newMarker.bcOff();
777 func->unit()->prettyPrint(
778 mStr, Unit::PrintOpts()
779 .range(bcOffset, bcOffset+1)
780 .noLineNumbers()
781 .noFuncs()
782 .indent(0));
783 std::vector<std::string> vec;
784 folly::split('\n', mStr.str(), vec);
785 os << markerEndl;
786 markerEndl = "\n";
787 for (auto& s : vec) {
788 if (s.empty()) continue;
789 os << color(ANSI_COLOR_BLUE) << s << color(ANSI_COLOR_END) << '\n';
793 curMarker = newMarker;
796 if (inst.op() == DefLabel) {
797 // print phi pseudo-instructions
798 for (unsigned i = 0, n = inst.numDsts(); i < n; ++i) {
799 os << std::string(kIndent +
800 folly::format("({}) ", inst.id()).str().size(),
801 ' ');
802 auto dst = inst.dst(i);
803 jit::print(os, dst);
804 os << punc(" = ") << color(ANSI_COLOR_CYAN) << "phi "
805 << color(ANSI_COLOR_END);
806 bool first = true;
807 inst.block()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) {
808 if (!first) os << punc(", ");
809 first = false;
810 printSrc(os, jmp, i);
811 os << punc("@");
812 printLabel(os, jmp->block());
814 os << '\n';
818 os << std::string(kIndent, ' ');
819 jit::print(os, &inst, guards);
820 os << '\n';
823 void print(std::ostream& os, const Block* block, TransKind kind,
824 const AsmInfo* asmInfo, const GuardConstraints* guards,
825 BCMarker* markerPtr) {
826 BCMarker dummy;
827 BCMarker& curMarker = markerPtr ? *markerPtr : dummy;
829 os << '\n' << std::string(kIndent - 3, ' ');
830 printLabel(os, block);
831 os << punc(":") << " [profCount=" << block->profCount() << "]";
833 switch (block->hint()) {
834 case Block::Hint::Unused: os << "<Unused>"; break;
835 case Block::Hint::Unlikely: os << "<Unlikely>"; break;
836 case Block::Hint::Likely: os << "<Likely>"; break;
837 default: break;
840 auto& preds = block->preds();
841 if (!preds.empty()) {
842 os << " (preds";
843 for (auto const& edge : preds) {
844 os << " B" << edge.from()->id();
846 os << ')';
848 os << "\n";
850 if (block->empty()) {
851 os << std::string(kIndent, ' ') << "empty block\n";
852 return;
855 const char* markerEndl = "";
857 if (asmInfo) {
858 std::vector<const IRInstruction*> instrs;
859 std::vector<InstAreaRange> instRanges;
860 TcaRange lastRange[kNumAreas];
861 size_t instIdx = 0;
863 // Collect all the instruction ranges for the current block and sort
864 // them. Entries will be sorted by instruction index, area then machine
865 // code pc. This reflects the order in which code will be printed.
866 // IR instructions with no associated assembly will still get entries
867 // in the instRanges vector so they will be printed too (they just get
868 // an empty machine code range).
869 for (auto it = block->begin(); it != block->end(); ++it, ++instIdx) {
870 const auto& inst = *it;
871 const size_t lastInstRangesSize = instRanges.size();
872 instrs.push_back(&inst); // Map back to IRInstruction from index.
874 for (auto i = 0; i < kNumAreas; ++i) {
875 const auto instArea = static_cast<AreaIndex>(i);
876 auto const& areaRanges = asmInfo->instRangesForArea(instArea);
877 auto const& rngs = areaRanges[inst];
878 for (auto itr = rngs.first; itr != rngs.second; ++itr) {
879 auto range = TcaRange { itr->second.start() + areaRanges.offset,
880 itr->second.end() + areaRanges.offset };
881 instRanges.push_back(InstAreaRange(instIdx, instArea, range));
882 lastRange[(int)instArea] = range;
886 // Add an entry for IRInstructions that have no associated machine
887 // code. Use the end address of the last instruction range to assign
888 // an empty range to this element.
889 if (instRanges.size() == lastInstRangesSize) {
890 instRanges.push_back(
891 InstAreaRange(instIdx,
892 AreaIndex::Main,
893 TcaRange(lastRange[(int)AreaIndex::Main].end(),
894 lastRange[(int)AreaIndex::Main].end())));
898 std::sort(instRanges.begin(), instRanges.end());
900 const IRInstruction* lastInst = nullptr;
901 AreaIndex lastArea = AreaIndex::Main;
902 bool printArea = false;
903 for (auto itr = instRanges.begin(); itr != instRanges.end(); ++itr) {
904 auto currInstIdx = itr->m_instIdx;
905 auto currInst = instrs[currInstIdx];
906 auto currArea = itr->m_area;
907 auto instRange = itr->m_instRange;
908 if (lastInst != currInst) {
909 if (lastInst && !lastRange[(int)currArea].empty()) os << "\n";
910 printIRInstruction(os, *currInst, guards, curMarker, markerEndl);
911 printArea = true;
912 lastInst = currInst;
913 lastRange[(int)currArea] = TcaRange{nullptr,nullptr};
915 if (printArea || currArea != lastArea) {
916 if (!instRange.empty()) {
917 if (!printArea) os << "\n";
918 os << std::string(kIndent + 4, ' ') << areaAsString(currArea);
919 os << ":\n";
921 lastArea = currArea;
922 printArea = false;
923 lastRange[(int)currArea] = TcaRange{nullptr,nullptr};
926 const auto lastEnd = lastRange[(int)currArea].end();
928 if (lastEnd && lastEnd != instRange.begin()) {
929 // There may be gaps between instruction ranges that have been
930 // added by the relocator, e.g. adding nops. This check will
931 // determine if the gap belongs to another instruction or not.
932 // If it doesn't belong to any other instruction then print it.
933 auto const offset = asmInfo->instRangesForArea(currArea).offset;
934 if (!asmInfo->instRangeExists(currArea,
935 TcaRange(lastEnd - offset,
936 instRange.begin() - offset))) {
937 disasmRange(os, kind, lastEnd, instRange.begin(), true);
938 } else {
939 os << "\n";
942 disasmRange(os, kind, instRange.begin(), instRange.end(), true);
943 lastRange[(int)currArea] = instRange;
945 os << "\n";
946 } else {
947 for (auto it = block->begin(); it != block->end(); ++it) {
948 printIRInstruction(os, *it, guards, curMarker, markerEndl);
952 os << std::string(kIndent - 2, ' ');
953 auto next = block->empty() ? nullptr : block->next();
954 if (next) {
955 os << punc("-> ");
956 printLabel(os, next);
957 os << '\n';
958 } else {
959 os << "no fallthrough\n";
963 void print(const Block* block) {
964 print(std::cerr, block, TransKind::Optimize);
965 std::cerr << std::endl;
968 std::string Block::toString() const {
969 std::ostringstream out;
970 print(out, this, TransKind::Optimize);
971 return out.str();
974 ///////////////////////////////////////////////////////////////////////////////
975 // Unit.
977 void printOpcodeStats(std::ostream& os, const BlockList& blocks) {
978 uint32_t counts[kNumOpcodes];
979 memset(counts, 0, sizeof(counts));
981 for (auto block : blocks) {
982 for (auto& inst : *block) ++counts[static_cast<size_t>(inst.op())];
985 os << "\nopcode counts:\n";
986 for (unsigned i = 0; i < kNumOpcodes; ++i) {
987 if (counts[i] == 0) continue;
988 auto op = safe_cast<Opcode>(i);
989 os << folly::format("{:>5} {}\n", counts[i], opcodeName(op));
991 os << '\n';
994 void print(std::ostream& os, const IRUnit& unit, const AsmInfo* asmInfo,
995 const GuardConstraints* guards) {
996 // For nice-looking dumps, we want to remember curMarker between blocks.
997 BCMarker curMarker;
998 static bool dotBodies = getenv("HHIR_DOT_BODIES");
999 auto const kind = unit.context().kind;
1000 os << "TransKind: " << show(kind) << "\n";
1001 if (unit.context().kind == TransKind::Optimize) {
1002 os << "OptIndex : " << unit.context().optIndex << "\n";
1004 auto blocks = rpoSortCfg(unit);
1005 // Partition into main, cold and frozen, without changing relative order.
1006 auto cold = std::stable_partition(blocks.begin(), blocks.end(),
1007 [&] (Block* b) {
1008 return b->hint() == Block::Hint::Neither ||
1009 b->hint() == Block::Hint::Likely;
1012 auto frozen = std::stable_partition(cold, blocks.end(),
1013 [&] (Block* b) { return b->hint() == Block::Hint::Unlikely; }
1016 if (dumpIREnabled(kind, kExtraExtraLevel)) printOpcodeStats(os, blocks);
1018 // Print the block CFG above the actual code.
1020 auto const retreating_edges = findRetreatingEdges(unit);
1021 os << "digraph G {\n";
1022 for (auto block : blocks) {
1023 if (block->empty()) continue;
1024 if (dotBodies) {
1025 if (block->hint() != Block::Hint::Unlikely &&
1026 block->hint() != Block::Hint::Unused) {
1027 // Include the IR in the body of the node
1028 std::ostringstream out;
1029 print(out, block, kind, asmInfo, guards, &curMarker);
1030 auto bodyRaw = out.str();
1031 std::string body;
1032 body.reserve(bodyRaw.size() * 1.25);
1033 for (auto c : bodyRaw) {
1034 if (c == '\n') body += "\\n";
1035 else if (c == '"') body += "\\\"";
1036 else if (c == '\\') body += "\\\\";
1037 else body += c;
1039 os << folly::format("B{} [shape=box,label=\"{}\"]\n",
1040 block->id(), body);
1042 } else {
1043 const auto color = [&] {
1044 switch (block->hint()) {
1045 case Block::Hint::Likely : return "red";
1046 case Block::Hint::Neither: return "orange";
1047 case Block::Hint::Unlikely: return "blue";
1048 case Block::Hint::Unused: return "gray";
1050 not_reached();
1051 }();
1052 os << folly::format(
1053 "B{} [shape=box,color={},label=\"B{}\\ncount={}\"]\n",
1054 block->id(), color, block->id(), block->profCount()
1058 auto next = block->nextEdge();
1059 auto taken = block->takenEdge();
1060 if (!next && !taken) continue;
1061 auto edge_color = [&] (Edge* edge) {
1062 auto const target = edge->to();
1063 return
1064 target->isCatch() ? " [color=gray]" :
1065 target->hint() == Block::Hint::Unlikely ? " [color=blue]" :
1066 retreating_edges.count(edge) ? " [color=red]" : "";
1068 auto show_edge = [&] (Edge* edge) {
1069 os << folly::format(
1070 "B{} -> B{}{}",
1071 block->id(),
1072 edge->to()->id(),
1073 edge_color(edge)
1076 if (next) {
1077 show_edge(next);
1078 if (taken) os << "; ";
1080 if (taken) show_edge(taken);
1081 os << "\n";
1083 os << "}\n";
1085 curMarker = BCMarker();
1086 for (auto it = blocks.begin(); it != blocks.end(); ++it) {
1087 if (it == cold) {
1088 os << folly::format("\n{:-^60}", "cold blocks");
1090 if (it == frozen) {
1091 os << folly::format("\n{:-^60}", "frozen blocks");
1093 print(os, *it, kind, asmInfo, guards, &curMarker);
1097 void print(const IRUnit& unit) {
1098 print(std::cerr, unit);
1099 std::cerr << std::endl;
1102 std::string show(const IRUnit& unit) {
1103 std::ostringstream out;
1104 print(out, unit);
1105 return out.str();
1108 std::string banner(const char* caption) {
1109 return folly::sformat(
1110 "{}{:-^80}{}\n",
1111 color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN),
1112 caption,
1113 color(ANSI_COLOR_END)
1117 // Suggested captions: "before jiffy removal", "after goat saturation",
1118 // etc.
1119 void printUnit(int level, const IRUnit& unit, const char* caption,
1120 AsmInfo* ai,
1121 const GuardConstraints* guards, Annotations* annotations) {
1122 if (dumpIREnabled(unit.context().kind, level)) {
1123 std::ostringstream str;
1124 if (dumpPrettyIR(level)) {
1125 str << banner(caption);
1126 print(str, unit, ai, guards);
1127 str << banner("");
1128 if (HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir, level)) {
1129 HPHP::Trace::traceRelease("%s\n", str.str().c_str());
1131 } else if (dumpJsonIR(level)) {
1132 str << "json:" << get_json::getUnit(unit, ai, guards);
1133 if (HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir_json, level)) {
1134 HPHP::Trace::traceRelease("%s\n", str.str().c_str());
1137 if (annotations && dumpRuntimeIR(level)) {
1138 annotations->emplace_back(caption, str.str());
1143 bool dumpIREnabled(TransKind kind, int level /* = 1 */) {
1144 return HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir, level) ||
1145 HPHP::Trace::moduleEnabledRelease(HPHP::Trace::printir_json, level) ||
1146 (dumpRuntimeIR(level) &&
1147 mcgen::dumpTCAnnotation(kind));
1150 ///////////////////////////////////////////////////////////////////////////////