1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "basicblock.h"
17 #include "graph.h"
18 #include "optimizer/analysis/linear_order.h"
19 #include "optimizer/analysis/loop_analyzer.h"
20 #include "dump.h"
21 
22 namespace panda::compiler {
23 
24 // indent constants for dump instructions
25 static const int INDENT_ID = 6;
26 static const int INDENT_TYPE = 5;
27 static const int INDENT_OPCODE = 27;
28 static const int HEX_PTR_SIZE = sizeof(void *);
29 
30 template <class T>
ToArenaString(T value, ArenaAllocator *allocator)31 std::enable_if_t<std::is_integral_v<T>, ArenaString> ToArenaString(T value, ArenaAllocator *allocator)
32 {
33     ArenaString res(std::to_string(value), allocator->Adapter());
34     return res;
35 }
36 
GetId(uint32_t id, ArenaAllocator *allocator)37 ArenaString GetId(uint32_t id, ArenaAllocator *allocator)
38 {
39     return (id == INVALID_ID ? ArenaString("XX", allocator->Adapter()) : ToArenaString(id, allocator));
40 }
41 
IdToString(uint32_t id, ArenaAllocator *allocator, bool v_reg, bool is_phi)42 ArenaString IdToString(uint32_t id, ArenaAllocator *allocator, bool v_reg, bool is_phi)
43 {
44     ArenaString reg(v_reg ? "v" : "", allocator->Adapter());
45     ArenaString phi(is_phi ? "p" : "", allocator->Adapter());
46     return reg + GetId(id, allocator) + phi;
47 }
48 
49 // If print without brackets, then we print with space.
PrintIfValidLocation(Location location, Arch arch, std::ostream *out, bool with_brackets = false)50 void PrintIfValidLocation(Location location, Arch arch, std::ostream *out, bool with_brackets = false)
51 {
52     if (!location.IsInvalid() && !location.IsUnallocatedRegister()) {
53         auto string = location.ToString(arch);
54         if (with_brackets) {
55             (*out) << "(" << string << ")";
56         } else {
57             (*out) << string << " ";
58         }
59     }
60 }
61 
InstId(const Inst *inst, ArenaAllocator *allocator)62 ArenaString InstId(const Inst *inst, ArenaAllocator *allocator)
63 {
64     if (inst != nullptr) {
65         if (inst->IsSaveState() && options.IsCompilerDumpCompact()) {
66             return ArenaString("ss", allocator->Adapter()) +
67                    ArenaString(std::to_string(inst->GetId()), allocator->Adapter());
68         }
69         return IdToString(static_cast<uint32_t>(inst->GetId()), allocator, true, inst->IsPhi());
70     }
71     ArenaString null("null", allocator->Adapter());
72     return null;
73 }
74 
BBId(const BasicBlock *block, ArenaAllocator *allocator)75 ArenaString BBId(const BasicBlock *block, ArenaAllocator *allocator)
76 {
77     if (block != nullptr) {
78         return IdToString(static_cast<uint32_t>(block->GetId()), allocator);
79     }
80     ArenaString null("null", allocator->Adapter());
81     return null;
82 }
83 
DumpUsers(const Inst *inst, std::ostream *out)84 void DumpUsers(const Inst *inst, std::ostream *out)
85 {
86     auto allocator = inst->GetBasicBlock()->GetGraph()->GetLocalAllocator();
87     auto arch = inst->GetBasicBlock()->GetGraph()->GetArch();
88     for (size_t i = 0; i < inst->GetDstCount(); ++i) {
89         PrintIfValidLocation(inst->GetDstLocation(), arch, out);
90     }
91     bool fl_first = true;
92     for (auto &node_inst : inst->GetUsers()) {
93         auto user = node_inst.GetInst();
94         (*out) << (fl_first ? "(" : ", ") << InstId(user, allocator);
95         if (fl_first) {
96             fl_first = false;
97         }
98     }
99     if (!fl_first) {
100         (*out) << ')';
101     }
102 }
103 
GetCondCodeToString(ConditionCode cc, ArenaAllocator *allocator)104 ArenaString GetCondCodeToString(ConditionCode cc, ArenaAllocator *allocator)
105 {
106     switch (cc) {
107         case ConditionCode::CC_EQ:
108             return ArenaString("EQ", allocator->Adapter());
109         case ConditionCode::CC_NE:
110             return ArenaString("NE", allocator->Adapter());
111 
112         case ConditionCode::CC_LT:
113             return ArenaString("LT", allocator->Adapter());
114         case ConditionCode::CC_LE:
115             return ArenaString("LE", allocator->Adapter());
116         case ConditionCode::CC_GT:
117             return ArenaString("GT", allocator->Adapter());
118         case ConditionCode::CC_GE:
119             return ArenaString("GE", allocator->Adapter());
120 
121         case ConditionCode::CC_B:
122             return ArenaString("B", allocator->Adapter());
123         case ConditionCode::CC_BE:
124             return ArenaString("BE", allocator->Adapter());
125         case ConditionCode::CC_A:
126             return ArenaString("A", allocator->Adapter());
127         case ConditionCode::CC_AE:
128             return ArenaString("AE", allocator->Adapter());
129 
130         case ConditionCode::CC_TST_EQ:
131             return ArenaString("TST_EQ", allocator->Adapter());
132         case ConditionCode::CC_TST_NE:
133             return ArenaString("TST_NE", allocator->Adapter());
134         default:
135             UNREACHABLE();
136     }
137 }
138 
PcToString(uint32_t pc, ArenaAllocator *allocator)139 ArenaString PcToString(uint32_t pc, ArenaAllocator *allocator)
140 {
141     std::ostringstream out_string;
142     out_string << "bc: 0x" << std::setfill('0') << std::setw(HEX_PTR_SIZE) << std::hex << pc;
143     return ArenaString(out_string.str(), allocator->Adapter());
144 }
145 
BBDependence(const char *type, const ArenaVector<BasicBlock *> &bb_vector, std::ostream *out, ArenaAllocator *allocator)146 void BBDependence(const char *type, const ArenaVector<BasicBlock *> &bb_vector, std::ostream *out,
147                   ArenaAllocator *allocator)
148 {
149     bool fl_first = true;
150     (*out) << type << ": [";
151     for (auto block_it : bb_vector) {
152         (*out) << (fl_first ? "" : ", ") << "bb " << BBId(block_it, allocator);
153         if (fl_first) {
154             fl_first = false;
155         }
156     }
157     (*out) << ']';
158 }
159 
DumpTypedFieldOpcode(std::ostream *out, Opcode opcode, uint32_t type_id, const ArenaString &field_name, ArenaAllocator *allocator)160 void DumpTypedFieldOpcode(std::ostream *out, Opcode opcode, uint32_t type_id, const ArenaString &field_name,
161                           ArenaAllocator *allocator)
162 {
163     const auto &adapter = allocator->Adapter();
164     ArenaString space(" ", adapter);
165     ArenaString opc(GetOpcodeString(opcode), adapter);
166     ArenaString id(IdToString(type_id, allocator), adapter);
167     (*out) << std::setw(INDENT_OPCODE) << opc + space + id + space + field_name + space;
168 }
169 
DumpTypedOpcode(std::ostream *out, Opcode opcode, uint32_t type_id, ArenaAllocator *allocator)170 void DumpTypedOpcode(std::ostream *out, Opcode opcode, uint32_t type_id, ArenaAllocator *allocator)
171 {
172     ArenaString space(" ", allocator->Adapter());
173     ArenaString opc(GetOpcodeString(opcode), allocator->Adapter());
174     ArenaString id(IdToString(type_id, allocator), allocator->Adapter());
175     (*out) << std::setw(INDENT_OPCODE) << opc + space + id;
176 }
177 
DumpInputs(std::ostream *out) const178 bool Inst::DumpInputs(std::ostream *out) const
179 {
180     const auto &allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
181     auto arch = GetBasicBlock()->GetGraph()->GetArch();
182     bool fl_first = true;
183     unsigned i = 0;
184     for (auto node_inst : GetInputs()) {
185         Inst *input = node_inst.GetInst();
186         (*out) << (fl_first ? "" : ", ") << InstId(input, allocator);
187         PrintIfValidLocation(GetLocation(i), arch, out, true);
188         i++;
189         fl_first = false;
190     }
191     return !fl_first;
192 }
193 
DumpInputs(std::ostream *out) const194 bool SaveStateInst::DumpInputs(std::ostream *out) const
195 {
196     const auto &allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
197     const char *sep = "";
198     for (size_t i = 0; i < GetInputsCount(); i++) {
199         (*out) << sep << std::dec << InstId(GetInput(i).GetInst(), allocator);
200         if (GetVirtualRegister(i).IsAccumulator()) {
201             (*out) << "(acc)";
202         } else {
203             (*out) << "(vr" << GetVirtualRegister(i).Value() << ")";
204         }
205         sep = ", ";
206     }
207     if (GetImmediatesCount() > 0) {
208         for (auto imm : *GetImmediates()) {
209             (*out) << sep << std::hex << "0x" << imm.value;
210             if (imm.is_acc) {
211                 (*out) << "(acc)";
212             } else {
213                 (*out) << std::dec << "(vr" << imm.vreg << ")";
214             }
215             sep = ", ";
216         }
217     }
218     return true;
219 }
220 
DumpInputs(std::ostream *out) const221 bool IfImmInst::DumpInputs(std::ostream *out) const
222 {
223     Inst::DumpInputs(out);
224     (*out) << ", 0x" << std::hex << GetImm() << std::dec;
225     return true;
226 }
227 
DumpInputs(std::ostream *out) const228 bool PhiInst::DumpInputs(std::ostream *out) const
229 {
230     const auto &allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
231     bool fl_first = true;
232     for (size_t idx = 0; idx < GetInputsCount(); ++idx) {
233         Inst *input = GetInput(idx).GetInst();
234         auto block = GetPhiInputBb(idx);
235         (*out) << (fl_first ? "" : ", ") << InstId(input, allocator) << "(bb" << BBId(block, allocator) << ")";
236         if (fl_first) {
237             fl_first = false;
238         }
239     }
240     return !fl_first;
241 }
242 
DumpInputs(std::ostream *out) const243 bool ConstantInst::DumpInputs(std::ostream *out) const
244 {
245     switch (GetType()) {
246         case DataType::Type::REFERENCE:
247         case DataType::Type::BOOL:
248         case DataType::Type::UINT8:
249         case DataType::Type::INT8:
250         case DataType::Type::UINT16:
251         case DataType::Type::INT16:
252         case DataType::Type::UINT32:
253         case DataType::Type::INT32:
254         case DataType::Type::UINT64:
255         case DataType::Type::INT64:
256             (*out) << "0x" << std::hex << GetIntValue() << std::dec;
257             break;
258         case DataType::Type::FLOAT32:
259             (*out) << GetFloatValue();
260             break;
261         case DataType::Type::FLOAT64:
262             (*out) << GetDoubleValue();
263             break;
264         case DataType::Type::ANY:
265             (*out) << "0x" << std::hex << GetRawValue();
266             break;
267         default:
268             UNREACHABLE();
269     }
270     return true;
271 }
272 
DumpInputs(std::ostream *out) const273 bool SpillFillInst::DumpInputs(std::ostream *out) const
274 {
275     bool first = true;
276     for (auto spill_fill : GetSpillFills()) {
277         if (!first) {
278             (*out) << ", ";
279         }
280         first = false;
281         (*out) << sf_data::ToString(spill_fill, GetBasicBlock()->GetGraph()->GetArch());
282     }
283     return true;
284 }
285 
DumpInputs(std::ostream *out) const286 bool ParameterInst::DumpInputs(std::ostream *out) const
287 {
288     (*out) << "arg " << IdToString(GetArgNumber(), GetBasicBlock()->GetGraph()->GetLocalAllocator());
289     return true;
290 }
291 
DumpOpcode(std::ostream *out) const292 void CompareInst::DumpOpcode(std::ostream *out) const
293 {
294     auto allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
295     const auto &adapter = allocator->Adapter();
296     ArenaString space(" ", adapter);
297     ArenaString opcode(GetOpcodeString(GetOpcode()), adapter);
298     ArenaString cc(GetCondCodeToString(GetCc(), allocator), adapter);
299     ArenaString type(DataType::ToString(GetOperandsType()), adapter);
300     (*out) << std::setw(INDENT_OPCODE) << opcode + space + cc + space + type;
301 }
302 
DumpOpcodeAnyTypeMixin(std::ostream &out, const Inst *inst)303 static void DumpOpcodeAnyTypeMixin(std::ostream &out, const Inst *inst)
304 {
305     const auto *mixin_inst = static_cast<const AnyTypeMixin<FixedInputsInst1> *>(inst);
306     ASSERT(mixin_inst != nullptr);
307     auto allocator = mixin_inst->GetBasicBlock()->GetGraph()->GetLocalAllocator();
308     const auto &adapter = allocator->Adapter();
309     ArenaString space(" ", adapter);
310     ArenaString opcode(GetOpcodeString(mixin_inst->GetOpcode()), adapter);
311     ArenaString any_base_type(AnyTypeTypeToString(mixin_inst->GetAnyType()), adapter);
312     out << std::setw(INDENT_OPCODE) << opcode + space + any_base_type + space;
313 }
314 
DumpOpcode(std::ostream *out) const315 void CompareAnyTypeInst::DumpOpcode(std::ostream *out) const
316 {
317     DumpOpcodeAnyTypeMixin(*out, this);
318 }
319 
DumpOpcode(std::ostream *out) const320 void CastAnyTypeValueInst::DumpOpcode(std::ostream *out) const
321 {
322     DumpOpcodeAnyTypeMixin(*out, this);
323 }
324 
DumpOpcode(std::ostream *out) const325 void CastValueToAnyTypeInst::DumpOpcode(std::ostream *out) const
326 {
327     DumpOpcodeAnyTypeMixin(*out, this);
328 }
329 
DumpOpcode(std::ostream *out) const330 void IfInst::DumpOpcode(std::ostream *out) const
331 {
332     auto allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
333     const auto &adapter = allocator->Adapter();
334     ArenaString space(" ", adapter);
335     ArenaString opcode(GetOpcodeString(GetOpcode()), adapter);
336     ArenaString cc(GetCondCodeToString(GetCc(), allocator), adapter);
337     ArenaString type(DataType::ToString(GetOperandsType()), adapter);
338     (*out) << std::setw(INDENT_OPCODE) << opcode + space + cc + space + type;
339 }
340 
DumpOpcode(std::ostream *out) const341 void IfImmInst::DumpOpcode(std::ostream *out) const
342 {
343     auto allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
344     const auto &adapter = allocator->Adapter();
345     ArenaString space(" ", adapter);
346     ArenaString opcode(GetOpcodeString(GetOpcode()), adapter);
347     ArenaString cc(GetCondCodeToString(GetCc(), allocator), adapter);
348     ArenaString type(DataType::ToString(GetOperandsType()), adapter);
349     (*out) << std::setw(INDENT_OPCODE) << opcode + space + cc + space + type;
350 }
351 
DumpOpcode(std::ostream *out) const352 void CmpInst::DumpOpcode(std::ostream *out) const
353 {
354     const auto &adapter = GetBasicBlock()->GetGraph()->GetLocalAllocator()->Adapter();
355     auto type = GetOperandsType();
356     ArenaString suffix = ArenaString(" ", adapter) + ArenaString(DataType::ToString(type), adapter);
357     if (IsFloatType(type)) {
358         (*out) << std::setw(INDENT_OPCODE) << ArenaString(IsFcmpg() ? "Fcmpg" : "Fcmpl", adapter) + suffix;
359     } else if (IsTypeSigned(type)) {
360         (*out) << std::setw(INDENT_OPCODE) << ArenaString("Cmp", adapter) + ArenaString(" ", adapter) + suffix;
361     } else {
362         (*out) << std::setw(INDENT_OPCODE) << ArenaString("Ucmp", adapter) + suffix;
363     }
364 }
365 
DumpOpcode(std::ostream *out) const366 void LoadFromPool::DumpOpcode(std::ostream *out) const
367 {
368     DumpTypedOpcode(out, GetOpcode(), GetTypeId(), GetBasicBlock()->GetGraph()->GetLocalAllocator());
369 }
370 
DumpOpcode(std::ostream *out) const371 void IntrinsicInst::DumpOpcode(std::ostream *out) const
372 {
373     const auto &adapter = GetBasicBlock()->GetGraph()->GetLocalAllocator()->Adapter();
374     ArenaString intrinsic(ArenaString("Intrinsic.", adapter));
375     ArenaString opcode(GetIntrinsicOpcodeName(), adapter);
376     (*out) << std::setw(INDENT_OPCODE) << intrinsic + opcode << " ";
377 }
378 
DumpOpcode(std::ostream *out) const379 void Inst::DumpOpcode(std::ostream *out) const
380 {
381     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
382     (*out) << std::setw(INDENT_OPCODE) << GetOpcodeString(opcode_);
383 }
384 
Dump(std::ostream *out, bool new_line) const385 void Inst::Dump(std::ostream *out, bool new_line) const
386 {
387     if (options.IsCompilerDumpCompact() && IsSaveState()) {
388         return;
389     }
390     auto allocator = GetBasicBlock()->GetGraph()->GetLocalAllocator();
391     // Id
392     (*out) << std::setw(INDENT_ID) << std::setfill(' ') << std::right
393            << IdToString(id_, allocator, false, IsPhi()) + '.';
394     // Type
395     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
396     (*out) << std::setw(INDENT_TYPE) << std::left << DataType::ToString(GetType());
397     // opcode
398     DumpOpcode(out);
399     auto operands_pos = out->tellp();
400     // inputs
401     bool has_input = DumpInputs(out);
402     // users
403     if (has_input && !GetUsers().Empty()) {
404         (*out) << " -> ";
405     }
406     DumpUsers(this, out);
407     // Align rest of the instruction info
408     static constexpr auto ALIGN_BUF_SIZE = 64;
409     if (auto pos_diff = out->tellp() - operands_pos; pos_diff < ALIGN_BUF_SIZE) {
410         pos_diff = ALIGN_BUF_SIZE - pos_diff;
411         static std::array<char, ALIGN_BUF_SIZE + 1> space_buf;
412         if (space_buf[0] != ' ') {
413             std::fill(space_buf.begin(), space_buf.end(), ' ');
414         }
415         space_buf[pos_diff] = 0;
416         (*out) << space_buf.data();
417         space_buf[pos_diff] = ' ';
418     }
419     // bytecode pointer
420     if (pc_ != INVALID_PC && !options.IsCompilerDumpCompact()) {
421         (*out) << ' ' << PcToString(pc_, allocator);
422     }
423     if (new_line) {
424         (*out) << '\n';
425     }
426     if (GetOpcode() == Opcode::Parameter) {
427         auto spill_fill = static_cast<const ParameterInst *>(this)->GetLocationData();
428         if (spill_fill.DstValue() != INVALID_REG) {
429             (*out) << sf_data::ToString(spill_fill, GetBasicBlock()->GetGraph()->GetArch());
430             if (new_line) {
431                 *out << std::endl;
432             }
433         }
434     }
435 }
436 
CheckPrintPropsFlag(std::ostream *out, bool *print_props_flag)437 void CheckPrintPropsFlag(std::ostream *out, bool *print_props_flag)
438 {
439     if (!(*print_props_flag)) {
440         (*out) << "prop: ";
441         (*print_props_flag) = true;
442     } else {
443         (*out) << ", ";
444     }
445 }
446 
BlockProps(const BasicBlock *block, std::ostream *out)447 void BlockProps(const BasicBlock *block, std::ostream *out)
448 {
449     bool print_props_flag = false;
450     if (block->IsStartBlock()) {
451         CheckPrintPropsFlag(out, &print_props_flag);
452         (*out) << "start";
453     }
454     if (block->IsEndBlock()) {
455         CheckPrintPropsFlag(out, &print_props_flag);
456         (*out) << "end";
457     }
458     if (block->IsLoopValid() && !block->GetLoop()->IsRoot()) {
459         if (block->IsLoopHeader()) {
460             CheckPrintPropsFlag(out, &print_props_flag);
461             (*out) << "head";
462         }
463         CheckPrintPropsFlag(out, &print_props_flag);
464         (*out) << "loop" << (block->GetLoop()->IsIrreducible() ? " (irreducible) " : " ") << block->GetLoop()->GetId();
465     }
466     if (block->IsTryBegin()) {
467         CheckPrintPropsFlag(out, &print_props_flag);
468         (*out) << "try_begin (id " << block->GetTryId() << ")";
469     }
470     if (block->IsTry()) {
471         CheckPrintPropsFlag(out, &print_props_flag);
472         (*out) << "try (id " << block->GetTryId() << ")";
473     }
474     if (block->IsTryEnd()) {
475         CheckPrintPropsFlag(out, &print_props_flag);
476         (*out) << "try_end (id " << block->GetTryId() << ")";
477     }
478     if (block->IsCatchBegin()) {
479         CheckPrintPropsFlag(out, &print_props_flag);
480         (*out) << "catch_begin";
481     }
482     if (block->IsCatch()) {
483         CheckPrintPropsFlag(out, &print_props_flag);
484         (*out) << "catch";
485     }
486     if (block->IsCatchEnd()) {
487         CheckPrintPropsFlag(out, &print_props_flag);
488         (*out) << "catch_end";
489     }
490 
491     if (block->GetGuestPc() != INVALID_PC) {
492         CheckPrintPropsFlag(out, &print_props_flag);
493         (*out) << PcToString(block->GetGuestPc(), block->GetGraph()->GetLocalAllocator());
494     }
495     if (print_props_flag) {
496         (*out) << std::endl;
497     }
498 }
499 
Dump(std::ostream *out) const500 void BasicBlock::Dump(std::ostream *out) const
501 {
502     const auto &allocator = GetGraph()->GetLocalAllocator();
503     (*out) << "BB " << IdToString(bb_id_, allocator);
504     // predecessors
505     if (!preds_.empty()) {
506         (*out) << "  ";
507         BBDependence("preds", preds_, out, allocator);
508     }
509     (*out) << '\n';
510     // properties
511     BlockProps(this, out);
512     // instructions
513     for (auto inst : this->AllInsts()) {
514         inst->Dump(out);
515     }
516     // successors
517     if (!succs_.empty()) {
518         BBDependence("succs", succs_, out, allocator);
519         (*out) << '\n';
520     }
521 }
522 
Dump(std::ostream *out) const523 void Graph::Dump(std::ostream *out) const
524 {
525     const auto &runtime = GetRuntime();
526     const auto &method = GetMethod();
527     const auto &adapter = GetLocalAllocator()->Adapter();
528     ArenaString return_type(DataType::ToString(DataType::Type::ANY), adapter);
529     (*out) << "Method: " << runtime->GetMethodFullName(method, true) << std::endl;
530     if (IsOsrMode()) {
531         (*out) << "OSR mode\n";
532     }
533     (*out) << std::endl;
534 
535     auto &blocks = GetAnalysis<LinearOrder>().IsValid() ? GetBlocksLinearOrder() : GetBlocksRPO();
536     for (const auto &block_it : blocks) {
537         block_it->Dump(out);
538         (*out) << '\n';
539     }
540 }
541 }  // namespace panda::compiler
542