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