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