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 22namespace panda::compiler { 23 24// indent constants for dump instructions 25static const int INDENT_ID = 6; 26static const int INDENT_TYPE = 5; 27static const int INDENT_OPCODE = 27; 28static const int HEX_PTR_SIZE = sizeof(void *); 29 30template <class T> 31std::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 37ArenaString GetId(uint32_t id, ArenaAllocator *allocator) 38{ 39 return (id == INVALID_ID ? ArenaString("XX", allocator->Adapter()) : ToArenaString(id, allocator)); 40} 41 42ArenaString 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. 50void 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 62ArenaString 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 75ArenaString 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 84void 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 104ArenaString 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 139ArenaString 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 146void 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 160void 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 170void 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 178bool 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 194bool 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 221bool IfImmInst::DumpInputs(std::ostream *out) const 222{ 223 Inst::DumpInputs(out); 224 (*out) << ", 0x" << std::hex << GetImm() << std::dec; 225 return true; 226} 227 228bool 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 243bool 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 273bool 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 286bool ParameterInst::DumpInputs(std::ostream *out) const 287{ 288 (*out) << "arg " << IdToString(GetArgNumber(), GetBasicBlock()->GetGraph()->GetLocalAllocator()); 289 return true; 290} 291 292void 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 303static 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 315void CompareAnyTypeInst::DumpOpcode(std::ostream *out) const 316{ 317 DumpOpcodeAnyTypeMixin(*out, this); 318} 319 320void CastAnyTypeValueInst::DumpOpcode(std::ostream *out) const 321{ 322 DumpOpcodeAnyTypeMixin(*out, this); 323} 324 325void CastValueToAnyTypeInst::DumpOpcode(std::ostream *out) const 326{ 327 DumpOpcodeAnyTypeMixin(*out, this); 328} 329 330void 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 341void 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 352void 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 366void LoadFromPool::DumpOpcode(std::ostream *out) const 367{ 368 DumpTypedOpcode(out, GetOpcode(), GetTypeId(), GetBasicBlock()->GetGraph()->GetLocalAllocator()); 369} 370 371void 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 379void 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 385void 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 437void 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 447void 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 500void 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 523void 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