2 +----------------------------------------------------------------------+
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>
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 ///////////////////////////////////////////////////////////////////////////////
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
)
78 << color(ANSI_COLOR_END
);
82 void printSrc(std::ostream
& ostream
, const IRInstruction
* inst
, uint32_t i
) {
83 SSATmp
* src
= inst
->src(i
);
87 ostream
<< color(ANSI_COLOR_RED
)
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()) {
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;
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
,
134 : m_instIdx(instIdx
),
136 m_instRange(instRange
)
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;
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() :
187 const dynamic extra
= inst
->hasExtra() ?
188 showExtra(inst
->op(), inst
->rawExtra()) :
191 const bool isGuard
= constraints
&&
192 !inst
->isTransient() &&
193 isGuardOp(inst
->op());
196 auto const it
= constraints
->guards
.find(inst
);
197 guard
= (it
== constraints
->guards
.end() ?
199 it
->second
.toString());
201 guard
= dynamic(nullptr);
204 return dynamic::object("opcodeName", opcodeName(inst
->op()))
205 ("typeParam", typeParam
)
210 dynamic
getSrcs(const IRInstruction
* inst
) {
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
)));
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
)));
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);
241 auto func
= newMarker
.func();
242 func
->prettyPrint(mStr
, Func::PrintOpts());
243 mStr
<< std::string(kIndent
, ' ')
247 auto const bcOffset
= newMarker
.bcOff();
249 func
->unit()->prettyPrint(
250 mStr
, Unit::PrintOpts()
251 .range(bcOffset
, bcOffset
+1)
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
)
293 ("srcs", getSrcs(&inst
))
294 ("dsts", getDsts(&inst
))
295 ("offset", offset
)));
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
))
309 ("disasm", disasmStr
.str());
313 // TODO(T52856776) - move into AnnotationData class
314 struct TargetProfileVisitor
{
315 explicit TargetProfileVisitor(const TransContext
& ctx
) : ctx(ctx
) {}
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
))
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
));
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());;
341 dynamic
operator() (T
&&) const {
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
;
375 std::vector
<const IRInstruction
*> instrs
;
376 std::vector
<InstAreaRange
> instRanges
;
377 std::array
<TcaRange
, kNumAreas
> lastRange
;
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
,
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
);
424 auto& prevRange
= collatedInstRanges
.back();
425 if (prevRange
.isContiguous(inst
)) {
426 prevRange
.m_instRange
= TcaRange(prevRange
.m_instRange
.begin(),
427 inst
.m_instRange
.end());
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",
454 lastRange
[(int)currArea
] = TcaRange(nullptr,nullptr);
456 if (printArea
|| currArea
!= lastArea
) {
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
,
478 currInstrObj
["tc_ranges"].push_back(getTCRange(currArea
,
481 lastRange
[(int)currArea
] = instRange
;
483 if (!currInstrObj
.isNull()) {
484 result
["instrs"].push_back(currInstrObj
);
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",
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
;
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
))
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() :
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(),
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
) {
560 currArea
= AreaIndex::Cold
;
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
;
576 ///////////////////////////////////////////////////////////////////////////////
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();
589 constraints
&& !inst
->isTransient() && isGuardOp(inst
->op());
591 if (!hasTypeParam
&& !hasExtra
&& !isGuard
) return;
592 os
<< color(ANSI_COLOR_LIGHT_BLUE
) << '<' << color(ANSI_COLOR_END
);
595 os
<< color(ANSI_COLOR_GREEN
)
596 << inst
->typeParam().toString()
597 << color(ANSI_COLOR_END
)
599 if (hasExtra
|| isGuard
) os
<< punc(",");
603 os
<< color(ANSI_COLOR_GREEN
)
604 << showExtra(inst
->op(), inst
->rawExtra())
605 << color(ANSI_COLOR_END
);
606 if (isGuard
) os
<< punc(",");
610 auto it
= constraints
->guards
.find(inst
);
611 os
<< (it
== constraints
->guards
.end() ? "unused" : it
->second
.toString());
614 os
<< color(ANSI_COLOR_LIGHT_BLUE
)
616 << color(ANSI_COLOR_END
);
619 void printSrcs(std::ostream
& os
, const IRInstruction
* inst
) {
621 if (inst
->op() == IncStat
) {
622 os
<< " " << Stats::g_counterNames
[inst
->src(0)->intVal()];
625 for (uint32_t i
= 0, n
= inst
->numSrcs(); i
< n
; i
++) {
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
++) {
640 print(os
, inst
->dst(i
));
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 ///////////////////////////////////////////////////////////////////////////////
675 void print(std::ostream
& os
, const SSATmp
* tmp
) {
676 if (tmp
->inst()->is(DefConst
)) {
677 os
<< constToString(tmp
->type());
680 os
<< color(ANSI_COLOR_WHITE
);
681 os
<< "t" << tmp
->id();
682 os
<< color(ANSI_COLOR_END
);
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 ///////////////////////////////////////////////////////////////////////////////
698 void disasmRange(std::ostream
& os
,
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
) : "";
710 Disasm
disasm(Disasm::Options().indent(indent
)
711 .printEncoding(printEncoding
)
713 disasm
.disasm(os
, begin
, end
);
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
));
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
);
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(),
747 [](const TcaRange
& a
, const TcaRange
& b
) {
748 return a
.begin() < b
.begin();
753 void printIRInstruction(std::ostream
& os
,
754 const IRInstruction
& inst
,
755 const GuardConstraints
* guards
,
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
)
768 auto func
= newMarker
.func();
769 if (!curMarker
.hasFunc() || func
!= curMarker
.func()) {
770 func
->prettyPrint(mStr
, Func::PrintOpts());
772 mStr
<< std::string(kIndent
, ' ')
776 auto bcOffset
= newMarker
.bcOff();
777 func
->unit()->prettyPrint(
778 mStr
, Unit::PrintOpts()
779 .range(bcOffset
, bcOffset
+1)
783 std::vector
<std::string
> vec
;
784 folly::split('\n', mStr
.str(), vec
);
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(),
802 auto dst
= inst
.dst(i
);
804 os
<< punc(" = ") << color(ANSI_COLOR_CYAN
) << "phi "
805 << color(ANSI_COLOR_END
);
807 inst
.block()->forEachSrc(i
, [&](IRInstruction
* jmp
, SSATmp
*) {
808 if (!first
) os
<< punc(", ");
810 printSrc(os
, jmp
, i
);
812 printLabel(os
, jmp
->block());
818 os
<< std::string(kIndent
, ' ');
819 jit::print(os
, &inst
, guards
);
823 void print(std::ostream
& os
, const Block
* block
, TransKind kind
,
824 const AsmInfo
* asmInfo
, const GuardConstraints
* guards
,
825 BCMarker
* markerPtr
) {
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;
840 auto& preds
= block
->preds();
841 if (!preds
.empty()) {
843 for (auto const& edge
: preds
) {
844 os
<< " B" << edge
.from()->id();
850 if (block
->empty()) {
851 os
<< std::string(kIndent
, ' ') << "empty block\n";
855 const char* markerEndl
= "";
858 std::vector
<const IRInstruction
*> instrs
;
859 std::vector
<InstAreaRange
> instRanges
;
860 TcaRange lastRange
[kNumAreas
];
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
,
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
);
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
);
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);
942 disasmRange(os
, kind
, instRange
.begin(), instRange
.end(), true);
943 lastRange
[(int)currArea
] = instRange
;
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();
956 printLabel(os
, next
);
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
);
974 ///////////////////////////////////////////////////////////////////////////////
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
));
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.
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(),
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;
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();
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
+= "\\\\";
1039 os
<< folly::format("B{} [shape=box,label=\"{}\"]\n",
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";
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();
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(
1078 if (taken
) os
<< "; ";
1080 if (taken
) show_edge(taken
);
1085 curMarker
= BCMarker();
1086 for (auto it
= blocks
.begin(); it
!= blocks
.end(); ++it
) {
1088 os
<< folly::format("\n{:-^60}", "cold blocks");
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
;
1108 std::string
banner(const char* caption
) {
1109 return folly::sformat(
1111 color(ANSI_COLOR_BLACK
, ANSI_BGCOLOR_GREEN
),
1113 color(ANSI_COLOR_END
)
1117 // Suggested captions: "before jiffy removal", "after goat saturation",
1119 void printUnit(int level
, const IRUnit
& unit
, const char* caption
,
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
);
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 ///////////////////////////////////////////////////////////////////////////////