1/*
2 * Copyright (c) 2021 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 "ecmascript/compiler/gate.h"
17#include <iostream>
18#include <sstream>
19
20namespace panda::ecmascript::kungfu {
21void Gate::CheckNullInput() const
22{
23    const auto numIns = GetNumIns();
24    for (size_t idx = 0; idx < numIns; idx++) {
25        if (IsInGateNull(idx)) {
26            CheckFailed("In list contains null", idx);
27        }
28    }
29}
30
31void Gate::CheckFailed(std::string errorString, size_t highlightIdx) const
32{
33    LOG_COMPILER(ERROR) << "[Verifier][Error] Gate level input list schema verify failed";
34    Print("", true, highlightIdx);
35    LOG_COMPILER(FATAL) << "Note: " << errorString;
36}
37
38void Gate::CheckInputOpcode(size_t idx, OpCode expected) const
39{
40    OpCode actual = GetInGateConst(idx)->GetOpCode();
41    if (actual != expected) {
42        CheckFailed("State input does not match (expected:" + GateMetaData::Str(expected) +
43                    " actual:" + GateMetaData::Str(actual) + ")", idx);
44    }
45}
46
47void Gate::CheckInputMachineType(size_t idx, MachineType expected, bool isArch64) const
48{
49    MachineType actual = GetInGateConst(idx)->GetMachineType();
50    if (expected == MachineType::FLEX) {
51        expected = GetMachineType();
52    }
53    if (expected == MachineType::ARCH) {
54        expected = isArch64 ? MachineType::I64 : MachineType::I32;
55    }
56    if (actual == MachineType::ARCH) {
57        actual = isArch64 ? MachineType::I64 : MachineType::I32;
58    }
59    if (actual != expected) {
60        CheckFailed("Value input does not match (expected:" +
61                    MachineTypeToStr(expected) + " actual:" + MachineTypeToStr(actual) + ")", idx);
62    }
63}
64
65void Gate::CheckNotInputMachineType(size_t idx, MachineType notExpected) const
66{
67    MachineType actual = GetInGateConst(idx)->GetMachineType();
68    if (actual == notExpected) {
69        CheckFailed("Value input does not match (notExpected:" +
70                    MachineTypeToStr(notExpected) + " actual:" + MachineTypeToStr(actual) + ")", idx);
71    }
72}
73
74void Gate::CheckGeneralState(size_t idx) const
75{
76    auto gatePtr = GetInGateConst(idx);
77    OpCode actual = gatePtr->GetOpCode();
78    if (!gatePtr->meta_->IsGeneralState()) {
79        CheckFailed("State input does not match (expected:<General State> actual:" +
80                    GateMetaData::Str(actual) + ")", idx);
81    }
82}
83
84void Gate::CheckState(size_t idx) const
85{
86    auto gatePtr = GetInGateConst(idx);
87    OpCode actual = gatePtr->GetOpCode();
88    if ((actual != OpCode::STATE_ENTRY) && (!gatePtr->meta_->IsState())) {
89        CheckFailed("State input does not match (expected:<State> actual:" +
90                    GateMetaData::Str(actual) + ")", idx);
91    }
92}
93
94void Gate::CheckStateInput() const
95{
96    size_t stateStart = 0;
97    size_t stateEnd = GetStateCount();
98    for (size_t idx = stateStart; idx < stateEnd; idx++) {
99        bool needCheck = true;
100        switch (GetOpCode()) {
101            case OpCode::IF_TRUE:
102            case OpCode::IF_FALSE:
103                ASSERT(idx == stateStart);
104                CheckInputOpcode(idx, OpCode::IF_BRANCH);
105                needCheck = false;
106                break;
107            case OpCode::SWITCH_CASE:
108            case OpCode::DEFAULT_CASE:
109                ASSERT(idx == stateStart);
110                CheckInputOpcode(idx, OpCode::SWITCH_BRANCH);
111                needCheck = false;
112                break;
113            default:
114                break;
115        }
116        if (needCheck) {
117            CheckState(idx);
118        }
119    }
120}
121
122void Gate::CheckValueInput(bool isArch64) const
123{
124    size_t valueStart = GetInValueStarts();
125    size_t valueEnd = valueStart + GetInValueCount();
126    for (size_t idx = valueStart; idx < valueEnd; idx++) {
127        switch (GetOpCode()) {
128            case OpCode::IF_BRANCH:
129                ASSERT(idx == valueStart);
130                CheckInputMachineType(idx, MachineType::I1, isArch64);
131                break;
132            case OpCode::VALUE_SELECTOR:
133            case OpCode::ADD:
134            case OpCode::SUB:
135            case OpCode::MUL:
136            case OpCode::EXP:
137            case OpCode::SDIV:
138            case OpCode::SMOD:
139            case OpCode::UDIV:
140            case OpCode::UMOD:
141            case OpCode::FDIV:
142            case OpCode::FMOD:
143            case OpCode::AND:
144            case OpCode::XOR:
145            case OpCode::OR:
146            case OpCode::LSL:
147            case OpCode::LSR:
148            case OpCode::ASR:
149                CheckInputMachineType(idx, MachineType::FLEX, isArch64);
150                break;
151            case OpCode::REV:
152                ASSERT(idx == valueStart);
153                CheckInputMachineType(idx, MachineType::I1, isArch64);
154                break;
155            case OpCode::LOAD:
156                ASSERT(idx == valueStart);
157                CheckInputMachineType(idx, MachineType::ARCH, isArch64);
158                break;
159            case OpCode::STORE:
160                if ((idx == valueStart + 1) || (idx == valueStart + 2)) { // 1:base, 2:offset
161                    CheckInputMachineType(idx, MachineType::ARCH, isArch64);
162                }
163                break;
164            case OpCode::STORE_WITHOUT_BARRIER:
165                if (idx == valueStart) {
166                    CheckInputMachineType(idx, MachineType::ARCH, isArch64);
167                }
168                break;
169            case OpCode::HEAP_ALLOC: {
170                if (idx == valueStart + 1) { // 1: size offset
171                    CheckInputMachineType(idx, MachineType::I64, isArch64);
172                }
173                break;
174            }
175            case OpCode::TAGGED_TO_INT64:
176            case OpCode::INT64_TO_TAGGED:
177                ASSERT(idx == valueStart);
178                CheckInputMachineType(valueStart, MachineType::I64, isArch64);
179                break;
180            case OpCode::OBJECT_TYPE_CHECK:
181            case OpCode::LOAD_ELEMENT:
182            case OpCode::STORE_ELEMENT:
183                if (idx == valueStart) { // 1: idx 1
184                    CheckInputMachineType(idx, MachineType::I64, isArch64);
185                }
186                break;
187            case OpCode::FCMP:
188                CheckInputMachineType(idx, MachineType::F64, isArch64);
189                break;
190            case OpCode::ICMP:
191                CheckNotInputMachineType(idx, MachineType::F64);
192                break;
193            default:
194                break;
195        }
196    }
197}
198
199void Gate::CheckDependInput() const
200{
201    size_t dependStart = GetStateCount();
202    size_t dependEnd = dependStart + GetDependCount();
203    for (size_t idx = dependStart; idx < dependEnd; idx++) {
204        if (GetInGateConst(idx)->GetDependCount() == 0 &&
205            GetInGateConst(idx)->GetOpCode() != OpCode::DEPEND_ENTRY) {
206            LOG_COMPILER(ERROR) << "depend in of " << GetId() << GateMetaData::Str(GetOpCode()) << "is "
207                << GetInGateConst(idx)->GetId() << GateMetaData::Str(GetInGateConst(idx)->GetOpCode());
208            CheckFailed("Depend input is side-effect free", idx);
209        }
210    }
211}
212
213void Gate::CheckRootInput() const
214{
215    size_t rootStart = GetInValueStarts() + GetInValueCount();
216    if (meta_->HasRoot()) {
217        switch (GetOpCode()) {
218            case OpCode::STATE_ENTRY:
219            case OpCode::DEPEND_ENTRY:
220            case OpCode::RETURN_LIST:
221            case OpCode::ARG_LIST:
222                CheckInputOpcode(rootStart, OpCode::CIRCUIT_ROOT);
223                break;
224            case OpCode::ARG:
225                CheckInputOpcode(rootStart, OpCode::ARG_LIST);
226                break;
227            case OpCode::RETURN:
228            case OpCode::RETURN_VOID:
229                CheckInputOpcode(rootStart, OpCode::RETURN_LIST);
230                break;
231            default:
232                break;
233        }
234    }
235}
236
237void Gate::CheckFrameStateInput() const
238{
239    size_t frameStateStart = GetInFrameStateStarts();
240    if (meta_->HasFrameState()) {
241        CheckInputOpcode(frameStateStart, OpCode::FRAME_STATE);
242    }
243}
244
245std::string Gate::GetValueInAndOut(bool inListPreview, size_t highlightIdx) const
246{
247    auto opcode = GetOpCode();
248    if (opcode != OpCode::NOP && opcode != OpCode::DEAD) {
249        std::ostringstream log("{\"id\":");
250        log << std::to_string(id_) << ", \"op\":\"" << GateMetaData::Str(opcode) << "\", ";
251        log << "\",\"in\":[";
252        size_t idx = 0;
253        auto stateSize = GetStateCount();
254        auto dependSize = GetDependCount();
255        auto valueSize = GetInValueCount();
256        auto frameStateSize = GetInFrameStateCount();
257        auto rootSize = GetRootCount();
258        size_t start = 0;
259        size_t end = stateSize;
260        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
261        end += dependSize;
262        start += stateSize;
263        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
264        end += valueSize;
265        start += dependSize;
266        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
267        end += frameStateSize;
268        start += valueSize;
269        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
270        end += rootSize;
271        start += frameStateSize;
272        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log, true);
273
274        log << "], \"out\":[";
275
276        if (!IsFirstOutNull()) {
277            const Out *curOut = GetFirstOutConst();
278            opcode = curOut->GetGateConst()->GetOpCode();
279            log << std::to_string(curOut->GetGateConst()->GetId()) +
280                    (inListPreview ? std::string(":" + GateMetaData::Str(opcode)) : std::string(""));
281
282            while (!curOut->IsNextOutNull()) {
283                curOut = curOut->GetNextOutConst();
284                log << ", " << std::to_string(curOut->GetGateConst()->GetId()) <<
285                       (inListPreview ? std::string(":" + GateMetaData::Str(opcode))
286                                       : std::string(""));
287            }
288        }
289        log << "]},";
290        return log.str();
291    }
292    return "";
293}
294
295void Gate::CheckStateOutput(const std::string& methodName) const
296{
297    if (!GetMetaData()->IsState()) {
298        return;
299    }
300    size_t cnt = 0;
301    const Gate *curGate = this;
302    if (!curGate->IsFirstOutNull()) {
303        const Out *curOut = curGate->GetFirstOutConst();
304        auto meta = curOut->GetGateConst()->GetMetaData();
305        if (curOut->IsStateEdge() && meta->IsState()) {
306            cnt++;
307        }
308        while (!curOut->IsNextOutNull()) {
309            curOut = curOut->GetNextOutConst();
310            meta = curOut->GetGateConst()->GetMetaData();
311            if (curOut->IsStateEdge() && meta->IsState()) {
312                cnt++;
313            }
314        }
315    }
316    size_t expected = 0;
317    bool needCheck = true;
318    if (GetMetaData()->IsTerminalState()) {
319        expected = 0;
320    } else if (GetOpCode() == OpCode::IF_BRANCH || GetOpCode() == OpCode::JS_BYTECODE) {
321        expected = 2; // 2: expected number of state out branches
322    } else if (GetOpCode() == OpCode::SWITCH_BRANCH) {
323        needCheck = false;
324    } else {
325        expected = 1;
326    }
327    if (needCheck && cnt != expected) {
328        curGate->Print();
329        std::string log = curGate->GetValueInAndOut(true);
330        CheckFailed("Number of state out branches is not valid (expected:" + std::to_string(expected) +
331            " actual:" + std::to_string(cnt) + ") methodName:" + methodName + " gateValue:" + log, -1);
332    }
333}
334
335void Gate::CheckBranchOutput() const
336{
337    std::map<std::pair<OpCode, BitField>, size_t> setOfOps;
338    if (GetOpCode() == OpCode::IF_BRANCH) {
339        size_t cnt = 0;
340        const Gate *curGate = this;
341        if (!curGate->IsFirstOutNull()) {
342            const Out *curOut = curGate->GetFirstOutConst();
343            if (curOut->GetGateConst()->GetMetaData()->IsState() && curOut->IsStateEdge()) {
344                ASSERT(!curOut->GetGateConst()->GetMetaData()->IsFixed());
345                setOfOps[{curOut->GetGateConst()->GetOpCode(), curOut->GetGateConst()->GetStateCount()}]++;
346                cnt++;
347            }
348            while (!curOut->IsNextOutNull()) {
349                curOut = curOut->GetNextOutConst();
350                if (curOut->GetGateConst()->GetMetaData()->IsState() && curOut->IsStateEdge()) {
351                    ASSERT(!curOut->GetGateConst()->GetMetaData()->IsFixed());
352                    setOfOps[{curOut->GetGateConst()->GetOpCode(), curOut->GetGateConst()->GetStateCount()}]++;
353                    cnt++;
354                }
355            }
356        }
357        if (setOfOps.size() != cnt) {
358            CheckFailed("Duplicate state out branches", -1);
359        }
360    }
361}
362
363void Gate::CheckNOP() const
364{
365    if (GetOpCode() == OpCode::NOP || GetOpCode() == OpCode::DEAD) {
366        if (!IsFirstOutNull()) {
367            CheckFailed("NOP gate used by other gates", -1);
368        }
369    }
370}
371
372void Gate::CheckSelector() const
373{
374    if (GetOpCode() == OpCode::VALUE_SELECTOR || GetOpCode() == OpCode::DEPEND_SELECTOR) {
375        auto stateOp = GetInGateConst(0)->GetOpCode();
376        if (stateOp == OpCode::MERGE || stateOp == OpCode::LOOP_BEGIN) {
377            ASSERT(GetNumIns() > 0);
378            if (GetInGateConst(0)->GetNumIns() != GetNumIns() - 1) {
379                if (GetOpCode() == OpCode::DEPEND_SELECTOR) {
380                    CheckFailed("Number of depend flows does not match control flows (expected:" +
381                            std::to_string(GetInGateConst(0)->GetNumIns()) +
382                            " actual:" + std::to_string(GetNumIns() - 1) + ")",
383                        -1);
384                } else {
385                    CheckFailed("Number of data flows does not match control flows (expected:" +
386                            std::to_string(GetInGateConst(0)->GetNumIns()) +
387                            " actual:" + std::to_string(GetNumIns() - 1) + ")",
388                        -1);
389                }
390            }
391        } else {
392            CheckFailed(
393                "State input does not match (expected:[MERGE|LOOP_BEGIN] actual:" +
394                GateMetaData::Str(stateOp) + ")", 0);
395        }
396    }
397}
398
399void Gate::CheckRelay() const
400{
401    if (GetOpCode() == OpCode::DEPEND_RELAY) {
402        auto stateOp = GetInGateConst(0)->GetOpCode();
403        switch (stateOp) {
404            case OpCode::IF_TRUE:
405            case OpCode::IF_FALSE:
406            case OpCode::SWITCH_CASE:
407            case OpCode::DEFAULT_CASE:
408            case OpCode::IF_SUCCESS:
409            case OpCode::IF_EXCEPTION:
410            case OpCode::ORDINARY_BLOCK:
411            case OpCode::DEOPT_CHECK:
412                break;
413            default:
414                CheckFailed("State input does not match ("
415                    "expected:[IF_TRUE|IF_FALSE|SWITCH_CASE|DEFAULT_CASE|"
416                    "IF_SUCCESS|IF_EXCEPTION|ORDINARY_BLOCK|DEOPT_CHECK] actual:" +
417                    GateMetaData::Str(stateOp) + ")", 0);
418                break;
419        }
420    }
421}
422
423void Gate::Verify(bool isArch64, const std::string& methodName) const
424{
425    CheckNullInput();
426    CheckStateInput();
427    CheckValueInput(isArch64);
428    CheckDependInput();
429    CheckFrameStateInput();
430    CheckRootInput();
431    CheckStateOutput(methodName);
432    CheckBranchOutput();
433    CheckNOP();
434    CheckSelector();
435    CheckRelay();
436}
437
438void Out::SetNextOut(const Out *ptr)
439{
440    nextOut_ =
441        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
442        static_cast<GateRef>((reinterpret_cast<const uint8_t *>(ptr)) - (reinterpret_cast<const uint8_t *>(this)));
443}
444
445Out *Out::GetNextOut()
446{
447    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
448    return reinterpret_cast<Out *>((reinterpret_cast<uint8_t *>(this)) + nextOut_);
449}
450
451const Out *Out::GetNextOutConst() const
452{
453    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
454    return reinterpret_cast<const Out *>((reinterpret_cast<const uint8_t *>(this)) + nextOut_);
455}
456
457void Out::SetPrevOut(const Out *ptr)
458{
459    prevOut_ =
460        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
461        static_cast<GateRef>((reinterpret_cast<const uint8_t *>(ptr)) - (reinterpret_cast<const uint8_t *>(this)));
462}
463
464Out *Out::GetPrevOut()
465{
466    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
467    return reinterpret_cast<Out *>((reinterpret_cast<uint8_t *>(this)) + prevOut_);
468}
469
470const Out *Out::GetPrevOutConst() const
471{
472    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
473    return reinterpret_cast<const Out *>((reinterpret_cast<const uint8_t *>(this)) + prevOut_);
474}
475
476void Out::SetIndex(OutIdx idx)
477{
478    idx_ = idx;
479}
480
481OutIdx Out::GetIndex() const
482{
483    return idx_;
484}
485
486Gate *Out::GetGate()
487{
488    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
489    return reinterpret_cast<Gate *>(&this[idx_ + 1]);
490}
491
492const Gate *Out::GetGateConst() const
493{
494    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
495    return reinterpret_cast<const Gate *>(&this[idx_ + 1]);
496}
497
498void Out::SetPrevOutNull()
499{
500    prevOut_ = 0;
501}
502
503bool Out::IsPrevOutNull() const
504{
505    return prevOut_ == 0;
506}
507
508void Out::SetNextOutNull()
509{
510    nextOut_ = 0;
511}
512
513bool Out::IsNextOutNull() const
514{
515    return nextOut_ == 0;
516}
517
518bool Out::IsStateEdge() const
519{
520    return idx_ < GetGateConst()->GetStateCount();
521}
522
523void In::SetGate(const Gate *ptr)
524{
525    gatePtr_ =
526        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
527        static_cast<GateRef>((reinterpret_cast<const uint8_t *>(ptr)) - (reinterpret_cast<const uint8_t *>(this)));
528}
529
530Gate *In::GetGate()
531{
532    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
533    return reinterpret_cast<Gate *>((reinterpret_cast<uint8_t *>(this)) + gatePtr_);
534}
535
536const Gate *In::GetGateConst() const
537{
538    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
539    return reinterpret_cast<const Gate *>((reinterpret_cast<const uint8_t *>(this)) + gatePtr_);
540}
541
542void In::SetGateNull()
543{
544    gatePtr_ = Gate::InvalidGateRef;
545}
546
547bool In::IsGateNull() const
548{
549    return gatePtr_ == Gate::InvalidGateRef;
550}
551
552// NOLINTNEXTLINE(modernize-avoid-c-arrays)
553Gate::Gate(const GateMetaData* meta, GateId id, Gate *inList[], MachineType machineType, GateType type)
554    : meta_(meta), id_(id), type_(type), machineType_(machineType)
555{
556    auto numIns = GetNumIns();
557    if (numIns == 0) {
558        auto curOut = GetOut(0);
559        curOut->SetIndex(0);
560        return;
561    }
562    for (size_t idx = 0; idx < numIns; idx++) {
563        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
564        auto in = inList[idx];
565        if (in == nullptr) {
566            GetIn(idx)->SetGateNull();
567        } else {
568            NewIn(idx, in);
569        }
570        auto curOut = GetOut(idx);
571        curOut->SetIndex(idx);
572    }
573}
574
575void Gate::NewIn(size_t idx, Gate *in)
576{
577    GetIn(idx)->SetGate(in);
578    auto curOut = GetOut(idx);
579    if (in->IsFirstOutNull()) {
580        curOut->SetNextOutNull();
581    } else {
582        curOut->SetNextOut(in->GetFirstOut());
583        in->GetFirstOut()->SetPrevOut(curOut);
584    }
585    curOut->SetPrevOutNull();
586    in->SetFirstOut(curOut);
587}
588
589void Gate::ModifyIn(size_t idx, Gate *in)
590{
591    DeleteIn(idx);
592    NewIn(idx, in);
593}
594
595void Gate::DeleteIn(size_t idx)
596{
597    if (!GetOut(idx)->IsNextOutNull() && !GetOut(idx)->IsPrevOutNull()) {
598        GetOut(idx)->GetPrevOut()->SetNextOut(GetOut(idx)->GetNextOut());
599        GetOut(idx)->GetNextOut()->SetPrevOut(GetOut(idx)->GetPrevOut());
600    } else if (GetOut(idx)->IsNextOutNull() && !GetOut(idx)->IsPrevOutNull()) {
601        GetOut(idx)->GetPrevOut()->SetNextOutNull();
602    } else if (!GetOut(idx)->IsNextOutNull()) {  // then GetOut(idx)->IsPrevOutNull() is true
603        GetIn(idx)->GetGate()->SetFirstOut(GetOut(idx)->GetNextOut());
604        GetOut(idx)->GetNextOut()->SetPrevOutNull();
605    } else {  // only this out now
606        GetIn(idx)->GetGate()->SetFirstOutNull();
607    }
608    GetIn(idx)->SetGateNull();
609}
610
611void Gate::DeleteGate()
612{
613    auto numIns = GetNumIns();
614    for (size_t idx = 0; idx < numIns; idx++) {
615        DeleteIn(idx);
616    }
617}
618
619Out *Gate::GetOut(size_t idx)
620{
621    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
622    return &reinterpret_cast<Out *>(this)[-1 - idx];
623}
624
625const Out *Gate::GetOutConst(size_t idx) const
626{
627    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
628    return &reinterpret_cast<const Out *>(this)[-1 - idx];
629}
630
631Out *Gate::GetFirstOut()
632{
633    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
634    return reinterpret_cast<Out *>((reinterpret_cast<uint8_t *>(this)) + firstOut_);
635}
636
637const Out *Gate::GetFirstOutConst() const
638{
639    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
640    return reinterpret_cast<const Out *>((reinterpret_cast<const uint8_t *>(this)) + firstOut_);
641}
642
643void Gate::SetFirstOutNull()
644{
645    firstOut_ = 0;
646}
647
648bool Gate::IsFirstOutNull() const
649{
650    return firstOut_ == 0;
651}
652
653void Gate::SetFirstOut(const Out *firstOut)
654{
655    firstOut_ =
656        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
657        static_cast<GateRef>(reinterpret_cast<const uint8_t *>(firstOut) - reinterpret_cast<const uint8_t *>(this));
658}
659
660In *Gate::GetIn(size_t idx)
661{
662#ifndef NDEBUG
663    if (idx >= GetNumIns()) {
664        LOG_COMPILER(INFO) << std::dec << "Gate In access out-of-bound! (idx=" << idx << ")";
665        Print();
666        ASSERT(false);
667    }
668#endif
669    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
670    return &reinterpret_cast<In *>(this + 1)[idx];
671}
672
673const In *Gate::GetInConst(size_t idx) const
674{
675#ifndef NDEBUG
676    if (idx >= GetNumIns()) {
677        LOG_COMPILER(INFO) << std::dec << "Gate In access out-of-bound! (idx=" << idx << ")";
678        Print();
679        ASSERT(false);
680    }
681#endif
682    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
683    return &reinterpret_cast<const In *>(this + 1)[idx];
684}
685
686Gate *Gate::GetInGate(size_t idx)
687{
688    return GetIn(idx)->GetGate();
689}
690
691const Gate *Gate::GetInGateConst(size_t idx) const
692{
693    return GetInConst(idx)->GetGateConst();
694}
695
696bool Gate::IsInGateNull(size_t idx) const
697{
698    return GetInConst(idx)->IsGateNull();
699}
700
701GateId Gate::GetId() const
702{
703    return id_;
704}
705
706OpCode Gate::GetOpCode() const
707{
708    return meta_->GetOpCode();
709}
710
711size_t Gate::GetNumIns() const
712{
713    return meta_->GetNumIns();
714}
715
716size_t Gate::GetInValueStarts() const
717{
718    return meta_->GetInValueStarts();
719}
720
721size_t Gate::GetInFrameStateStarts() const
722{
723    return meta_->GetInFrameStateStarts();
724}
725
726size_t Gate::GetStateCount() const
727{
728    return meta_->GetStateCount();
729}
730
731size_t Gate::GetDependCount() const
732{
733    return meta_->GetDependCount();
734}
735
736size_t Gate::GetInValueCount() const
737{
738    return meta_->GetInValueCount();
739}
740
741size_t Gate::GetInFrameStateCount() const
742{
743    return meta_->GetInFrameStateCount();
744}
745
746size_t Gate::GetRootCount() const
747{
748    return meta_->GetRootCount();
749}
750
751std::string Gate::MachineTypeStr(MachineType machineType) const
752{
753    const std::map<MachineType, const char *> strMap = {
754        {NOVALUE, "NOVALUE"},
755        {ANYVALUE, "ANYVALUE"},
756        {ARCH, "ARCH"},
757        {FLEX, "FLEX"},
758        {I1, "I1"},
759        {I8, "I8"},
760        {I16, "I16"},
761        {I32, "I32"},
762        {I64, "I64"},
763        {F32, "F32"},
764        {F64, "F64"},
765    };
766    if (strMap.count(machineType) > 0) {
767        return strMap.at(machineType);
768    }
769    return "MachineType-" + std::to_string(machineType);
770}
771
772std::string Gate::GateTypeStr(GateType gateType) const
773{
774    static const std::map<GateType, const char *> strMap = {
775        {GateType::NJSValue(), "NJS_VALUE"},
776        {GateType::TaggedValue(), "TAGGED_VALUE"},
777        {GateType::TaggedPointer(), "TAGGED_POINTER"},
778        {GateType::TaggedNPointer(), "TAGGED_NPOINTER"},
779        {GateType::Empty(), "EMPTY"},
780        {GateType::AnyType(), "ANY_TYPE"},
781    };
782
783    std::string name = "";
784    if (strMap.count(gateType) > 0) {
785        name = strMap.at(gateType);
786    }
787    uint32_t r = gateType.GetType();
788    return name + std::string("-gateType(") + std::to_string(r) + std::string(")");
789}
790
791void Gate::Print(std::string additionOp, bool inListPreview, size_t highlightIdx, std::string_view comment) const
792{
793    LOG_COMPILER(INFO) << ToString(additionOp, inListPreview, highlightIdx, comment);
794}
795
796void Gate::DumpHeader(std::ostringstream &oss, const std::string& additionOp) const
797{
798    auto opcode = GetOpCode();
799    ASSERT(opcode != OpCode::NOP && opcode != OpCode::DEAD);
800
801    oss << "{\"id\":" << std::to_string(id_) << ", \"op\":\"" << GateMetaData::Str(opcode) << "\", ";
802    if (additionOp.compare("") != 0) {
803        auto additionOpName = (opcode == OpCode::JS_BYTECODE) ? "bytecode" : "typedop";
804        oss << "\"" << additionOpName << "\":\"" << additionOp;
805        oss << "\", ";
806    }
807    oss << "\"MType\":\"" << MachineTypeStr(GetMachineType()) << ", ";
808
809    oss << "bitfield=0x" << std::hex << TryGetValue() << std::dec << ", ";
810    oss << "type=" << GateTypeStr(type_) << ", ";
811    oss << "stamp=" << std::to_string(static_cast<uint32_t>(stamp_)) << ", ";
812    oss << "mark=" << std::to_string(static_cast<uint32_t>(mark_)) << ", ";
813}
814
815void Gate::DumpInputs(std::ostringstream &oss, bool inListPreview, size_t highlightIdx) const
816{
817    [[maybe_unused]] auto opcode = GetOpCode();
818    ASSERT(opcode != OpCode::NOP && opcode != OpCode::DEAD);
819
820    size_t idx = 0;
821    auto stateSize = GetStateCount();
822    auto dependSize = GetDependCount();
823    auto valueSize = GetInValueCount();
824    auto frameStateSize = GetInFrameStateCount();
825    auto rootSize = GetRootCount();
826    size_t start = 0;
827    size_t end = stateSize;
828
829    oss << "\",\"in\":[";
830    idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, oss);
831    end += dependSize;
832    start += stateSize;
833    idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, oss);
834    end += valueSize;
835    start += dependSize;
836    idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, oss);
837    end += frameStateSize;
838    start += valueSize;
839    idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, oss);
840    end += rootSize;
841    start += frameStateSize;
842    idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, oss, true);
843    oss << "]";
844}
845
846void Gate::DumpOutputs(std::ostringstream &oss, bool inListPreview) const
847{
848    auto opcode = GetOpCode();
849    ASSERT(opcode != OpCode::NOP && opcode != OpCode::DEAD);
850
851    oss << ", \"out\":[";
852    if (!IsFirstOutNull()) {
853        const Out *curOut = GetFirstOutConst();
854        opcode = curOut->GetGateConst()->GetOpCode();
855        oss << std::to_string(curOut->GetGateConst()->GetId()) +
856                (inListPreview ? std::string(":" + GateMetaData::Str(opcode)) : std::string(""));
857
858        while (!curOut->IsNextOutNull()) {
859            curOut = curOut->GetNextOutConst();
860            oss << ", " +  std::to_string(curOut->GetGateConst()->GetId()) +
861                    (inListPreview ? std::string(":" + GateMetaData::Str(opcode))
862                                    : std::string(""));
863        }
864    }
865    oss << "]";
866}
867
868static void DumpComment(std::ostringstream &oss, std::string_view comment)
869{
870    oss << ", \"comment\":\"" << comment << "\"";
871}
872
873std::string Gate::ToString(std::string additionOp, bool inListPreview, size_t highlightIdx,
874    std::string_view comment) const
875{
876    auto opcode = GetOpCode();
877    if (opcode == OpCode::NOP || opcode == OpCode::DEAD) {
878        return "";
879    }
880
881    std::ostringstream oss;
882    oss << std::dec;
883    DumpHeader(oss, additionOp);
884    DumpInputs(oss, inListPreview, highlightIdx);
885    DumpOutputs(oss, inListPreview);
886    if (!comment.empty()) {
887        DumpComment(oss, comment);
888    }
889    oss << "},";
890    return oss.str();
891}
892
893void Gate::ShortPrint(std::string bytecode, bool inListPreview, size_t highlightIdx) const
894{
895    auto opcode = GetOpCode();
896    if (opcode != OpCode::NOP && opcode != OpCode::DEAD) {
897        std::ostringstream log;
898        log << "(\"id\"=" << std::to_string(id_) << ", \"op\"=\"" << GateMetaData::Str(opcode) << "\", ";
899        log << ((bytecode.compare("") == 0) ? "" : "bytecode=") << bytecode;
900        log << ((bytecode.compare("") == 0) ? "" : ", ");
901        log << "\"MType\"=\"" + MachineTypeStr(GetMachineType()) + ", ";
902        log << "bitfield=0x" << std::hex << TryGetValue() << std::dec << ", ";
903        log << "type=" + GateTypeStr(type_) + ", ";
904        log << "\", in=[";
905
906        size_t idx = 0;
907        auto stateSize = GetStateCount();
908        auto dependSize = GetDependCount();
909        auto valueSize = GetInValueCount();
910        auto frameStateSize = GetInFrameStateCount();
911        auto rootSize = GetRootCount();
912        size_t start = 0;
913        size_t end = stateSize;
914        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
915        end += dependSize;
916        start += stateSize;
917        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
918        end += valueSize;
919        start += dependSize;
920        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
921        end += frameStateSize;
922        start += valueSize;
923        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log);
924        end += rootSize;
925        start += frameStateSize;
926        idx = PrintInGate(end, idx, start, inListPreview, highlightIdx, log, true);
927
928        log << "], out=[";
929
930        if (!IsFirstOutNull()) {
931            const Out *curOut = GetFirstOutConst();
932            opcode = curOut->GetGateConst()->GetOpCode();
933            log << std::to_string(curOut->GetGateConst()->GetId()) <<
934                   (inListPreview ? std::string(":" + GateMetaData::Str(opcode)) : std::string(""));
935
936            while (!curOut->IsNextOutNull()) {
937                curOut = curOut->GetNextOutConst();
938                log << ", " <<  std::to_string(curOut->GetGateConst()->GetId()) <<
939                       (inListPreview ? std::string(":" + GateMetaData::Str(opcode))
940                                      : std::string(""));
941            }
942        }
943        log << "])";
944        LOG_COMPILER(INFO) << std::dec << log.str();
945    }
946}
947
948size_t Gate::PrintInGate(size_t numIns, size_t idx, size_t size, bool inListPreview, size_t highlightIdx,
949                         std::ostringstream &log, bool isEnd) const
950{
951    log << "[";
952    for (; idx < numIns; idx++) {
953        log << ((idx == size) ? "" : ", ");
954        log << ((idx == highlightIdx) ? "\033[4;31m" : "");
955        log << ((IsInGateNull(idx)
956                ? "N"
957                : (std::to_string(GetInGateConst(idx)->GetId()) +
958                    (inListPreview ? std::string(":" + GateMetaData::Str(GetInGateConst(idx)->GetOpCode()))
959                                   : std::string("")))));
960        log << ((idx == highlightIdx) ? "\033[0m" : "");
961    }
962    log << "]";
963    log << ((isEnd) ? "" : ", ");
964    return idx;
965}
966
967std::string Gate::GetBytecodeStr() const
968{
969    switch (GetOpCode()) {
970        case OpCode::JS_BYTECODE: {
971            return GetJSBytecodeMetaData()->Str();
972        }
973        case OpCode::TYPED_BINARY_OP: {
974            auto typedOp = TypedBinaryAccessor(GetOneParameterMetaData()->GetValue()).GetTypedBinOp();
975            return GateMetaData::Str(typedOp);
976        }
977        case OpCode::TYPED_UNARY_OP: {
978            auto typedOp = TypedUnaryAccessor(GetOneParameterMetaData()->GetValue()).GetTypedUnOp();
979            return GateMetaData::Str(typedOp);
980        }
981        case OpCode::TYPED_CONDITION_JUMP: {
982            auto typedOp = TypedJumpAccessor(GetOneParameterMetaData()->GetValue()).GetTypedJumpOp();
983            return GateMetaData::Str(typedOp);
984        }
985        case OpCode::LOAD_ELEMENT: {
986            auto typedOp = static_cast<TypedLoadOp>(GetOneParameterMetaData()->GetValue());
987            return GateMetaData::Str(typedOp);
988        }
989        case OpCode::STORE_ELEMENT: {
990            auto typedOp = static_cast<TypedStoreOp>(GetOneParameterMetaData()->GetValue());
991            return GateMetaData::Str(typedOp);
992        }
993        case OpCode::TYPED_CALLTARGETCHECK_OP: {
994            TypedCallTargetCheckAccessor accessor(GetOneParameterMetaData()->GetValue());
995            auto typedOp = accessor.GetCallTargetCheckOp();
996            return GateMetaData::Str(typedOp);
997        }
998        case OpCode::CONVERT:
999        case OpCode::CHECK_AND_CONVERT: {
1000            ValuePairTypeAccessor accessor(GetOneParameterMetaData()->GetValue());
1001            return GateMetaData::Str(accessor.GetSrcType()) + "_TO_" +
1002                GateMetaData::Str(accessor.GetDstType());
1003        }
1004        default:
1005            return "";
1006    }
1007    return "";
1008}
1009
1010void Gate::PrintWithBytecode(std::string_view comment) const
1011{
1012    PrintGateWithAdditionOp(GetBytecodeStr(), comment);
1013}
1014
1015void Gate::PrintGateWithAdditionOp(std::string additionOp, std::string_view comment) const
1016{
1017    Print(additionOp, false, -1, comment);
1018}
1019
1020MarkCode Gate::GetMark(TimeStamp stamp) const
1021{
1022    return (stamp_ == stamp) ? mark_ : MarkCode::NO_MARK;
1023}
1024
1025void Gate::SetMark(MarkCode mark, TimeStamp stamp)
1026{
1027    stamp_ = stamp;
1028    mark_ = mark;
1029}
1030}  // namespace panda::ecmascript::kungfu
1031