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/bytecode_circuit_builder.h"
17#include "ecmascript/compiler/circuit.h"
18#include "ecmascript/compiler/debug_info.h"
19#include "ecmascript/compiler/ecma_opcode_des.h"
20#include "ecmascript/compiler/gate_accessor.h"
21#include "ecmascript/platform/map.h"
22
23namespace panda::ecmascript::kungfu {
24Circuit::Circuit(NativeAreaAllocator* allocator, DebugInfo* debugInfo, const char* funcName,
25                 bool isArch64, panda::ecmascript::FrameType type)
26    : circuitSize_(0),
27      gateCount_(0),
28      time_(1),
29      frameType_(type),
30      isArch64_(isArch64),
31      chunk_(allocator),
32      root_(Circuit::NullGate()),
33      metaBuilder_(chunk()),
34      gateToDInfo_(chunk()),
35      debugInfo_(debugInfo)
36#ifndef NDEBUG
37      , allGates_(chunk())
38#endif
39{
40    if (funcName != nullptr && debugInfo_->IsEnable()) {
41        debugInfo_->AddFuncDebugInfo(funcName);
42    }
43    space_ = panda::ecmascript::PageMap(CIRCUIT_SPACE, PAGE_PROT_READWRITE).GetMem();
44    InitRoot();
45}
46
47Circuit::~Circuit()
48{
49    panda::ecmascript::PageUnmap(MemMap(space_, CIRCUIT_SPACE));
50    debugInfo_ = nullptr;
51    space_ = nullptr;
52}
53
54void Circuit::InitRoot()
55{
56    root_ = NewGate(metaBuilder_.CircuitRoot(), MachineType::NOVALUE, {}, GateType::Empty());
57    NewGate(metaBuilder_.StateEntry(), MachineType::NOVALUE, { root_ }, GateType::Empty());
58    NewGate(metaBuilder_.DependEntry(), MachineType::NOVALUE, { root_ }, GateType::Empty());
59    NewGate(metaBuilder_.ReturnList(), MachineType::NOVALUE, { root_ }, GateType::Empty());
60    NewGate(metaBuilder_.ArgList(), MachineType::NOVALUE, { root_ }, GateType::Empty());
61}
62
63uint8_t *Circuit::AllocateSpace(size_t gateSize)
64{
65    circuitSize_ += gateSize;
66    if (circuitSize_ > CIRCUIT_SPACE) {
67        return nullptr;  // abort compilation
68    }
69    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
70    return GetDataPtr(circuitSize_ - gateSize);
71}
72
73Gate *Circuit::AllocateGateSpace(size_t numIns)
74{
75    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
76    return reinterpret_cast<Gate *>(AllocateSpace(Gate::GetGateSize(numIns)) + Gate::GetOutListSize(numIns));
77}
78
79bool Circuit::AddComment(GateRef g, std::string &&str)
80{
81    if (debugInfo_ == nullptr) {
82        return false;
83    }
84    if (!debugInfo_->IsEnable()) {
85        return false;
86    }
87    auto it = gateToDInfo_.find(g);
88    if (it == gateToDInfo_.end()) {
89        size_t index = debugInfo_->AddComment(std::move(str));
90        gateToDInfo_[g] = index;
91    } else {
92        debugInfo_->AppendComment(it->second, std::move(str));
93    }
94    return true;
95}
96
97std::string_view Circuit::GetComment(GateRef gate) const
98{
99    if (debugInfo_ == nullptr || !debugInfo_->IsEnable()) {
100        return "";
101    }
102    size_t index;
103    if (!GetDebugInfo(gate, index)) {
104        return "";
105    }
106    return debugInfo_->GetComment(index);
107}
108
109bool Circuit::GetDebugInfo(GateRef g, size_t &index) const
110{
111    auto it = gateToDInfo_.find(g);
112    if (it != gateToDInfo_.end()) {
113        index = it->second;
114        return true;
115    } else {
116        return false;
117    }
118}
119
120// NOLINTNEXTLINE(modernize-avoid-c-arrays)
121GateRef Circuit::NewGate(const GateMetaData *meta, MachineType machineType, size_t numIns,
122                         const GateRef inList[], GateType type, const char* comment)
123{
124#ifndef NDEBUG
125    if (numIns != meta->GetNumIns()) {
126        LOG_COMPILER(FATAL) << "Invalid input list!"
127                            << " op=" << meta->GetOpCode()
128                            << " expected_num_in=" << meta->GetNumIns() << " actual_num_in=" << numIns;
129        UNREACHABLE();
130    }
131#endif
132    std::vector<Gate *> inPtrList(numIns);
133    auto gateSpace = AllocateGateSpace(numIns);
134    for (size_t idx = 0; idx < numIns; idx++) {
135        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
136        inPtrList[idx] = (inList[idx] == Circuit::NullGate()) ? nullptr : LoadGatePtr(inList[idx]);
137    }
138    auto newGate = new (gateSpace) Gate(meta, gateCount_++, inPtrList.data(), machineType, type);
139#ifndef NDEBUG
140    allGates_.push_back(GetGateRef(newGate));
141#endif
142    GateRef result = GetGateRef(newGate);
143    if (comment != nullptr) {
144        AddComment(result, std::string(comment));
145    }
146#ifndef NDEBUG
147    if (UNLIKELY(debugInfo_ != nullptr && !currentComment_.empty())) {
148        AddComment(result, std::string(currentComment_));
149    }
150#endif
151    return result;
152}
153
154GateRef Circuit::NewGate(const GateMetaData *meta, const std::vector<GateRef> &inList, const char* comment)
155{
156    return NewGate(meta, MachineType::NOVALUE, inList.size(), inList.data(), GateType::Empty(), comment);
157}
158
159GateRef Circuit::NewGate(const GateMetaData *meta, MachineType machineType,
160    const std::initializer_list<GateRef>& args, GateType type, const char* comment)
161{
162    return NewGate(meta, machineType, args.size(), args.begin(), type, comment);
163}
164
165GateRef Circuit::NewGate(const GateMetaData *meta, MachineType machineType,
166    const std::vector<GateRef>& inList, GateType type, const char* comment)
167{
168    return NewGate(meta, machineType, inList.size(), inList.data(), type, comment);
169}
170
171GateRef Circuit::NewGate(const GateMetaData *meta, MachineType machineType, GateType type, const char* comment)
172{
173    return NewGate(meta, machineType, {}, type, comment);
174}
175
176void Circuit::PrintAllGates() const
177{
178    std::vector<GateRef> gateList;
179    GetAllGates(gateList);
180    for (const auto &gate : gateList) {
181        LoadGatePtrConst(gate)->Print();
182    }
183}
184
185void Circuit::PrintAllGatesWithBytecode() const
186{
187    std::vector<GateRef> gateList;
188    GetAllGates(gateList);
189    for (const auto &gate : gateList) {
190        LoadGatePtrConst(gate)->PrintWithBytecode(GetComment(gate));
191    }
192}
193
194void Circuit::GetAllGates(std::vector<GateRef>& gateList) const
195{
196    gateList.clear();
197    for (size_t out = 0; out < circuitSize_;
198        out += Gate::GetGateSize(reinterpret_cast<const Out *>(LoadGatePtrConst(GateRef(out)))->GetIndex() + 1)) {
199        auto gatePtr = reinterpret_cast<const Out *>(LoadGatePtrConst(GateRef(out)))->GetGateConst();
200        if (!gatePtr->GetMetaData()->IsNop()) {
201            gateList.push_back(GetGateRef(gatePtr));
202        }
203    }
204}
205
206GateRef Circuit::GetGateRef(const Gate *gate) const
207{
208    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
209    return static_cast<GateRef>(reinterpret_cast<const uint8_t *>(gate) - GetDataPtrConst(0));
210}
211
212Gate *Circuit::LoadGatePtr(GateRef shift)
213{
214    ASSERT(shift != Circuit::NullGate());
215    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
216    return reinterpret_cast<Gate *>(GetDataPtr(shift));
217}
218
219const Gate *Circuit::LoadGatePtrConst(GateRef shift) const
220{
221    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
222    return reinterpret_cast<const Gate *>(GetDataPtrConst(shift));
223}
224
225void Circuit::AdvanceTime() const
226{
227    auto &curTime = const_cast<TimeStamp &>(time_);
228    curTime++;
229    if (curTime == 0) {
230        curTime = 1;
231        ResetAllGateTimeStamps();
232    }
233}
234
235void Circuit::ResetAllGateTimeStamps() const
236{
237    std::vector<GateRef> gateList;
238    GetAllGates(gateList);
239    for (auto &gate : gateList) {
240        const_cast<Gate *>(LoadGatePtrConst(gate))->SetMark(MarkCode::NO_MARK, 0);
241    }
242}
243
244TimeStamp Circuit::GetTime() const
245{
246    return time_;
247}
248
249MarkCode Circuit::GetMark(GateRef gate) const
250{
251    return LoadGatePtrConst(gate)->GetMark(GetTime());
252}
253
254void Circuit::SetMark(GateRef gate, MarkCode mark) const
255{
256    const_cast<Gate *>(LoadGatePtrConst(gate))->SetMark(mark, GetTime());
257}
258
259void Circuit::Verify(GateRef gate, const std::string& methodName) const
260{
261    LoadGatePtrConst(gate)->Verify(IsArch64(), methodName);
262}
263
264GateRef Circuit::NullGate()
265{
266    return Gate::InvalidGateRef;
267}
268
269bool Circuit::IsLoopHead(GateRef gate) const
270{
271    if (gate != NullGate()) {
272        const Gate *curGate = LoadGatePtrConst(gate);
273        return curGate->GetMetaData()->IsLoopHead();
274    }
275    return false;
276}
277
278bool Circuit::IsControlCase(GateRef gate) const
279{
280    if (gate != NullGate()) {
281        const Gate *curGate = LoadGatePtrConst(gate);
282        return curGate->GetMetaData()->IsControlCase();
283    }
284    return false;
285}
286
287bool Circuit::IsValueSelector(GateRef gate) const
288{
289    if (gate != NullGate()) {
290        const Gate *curGate = LoadGatePtrConst(gate);
291        return curGate->GetOpCode() == OpCode::VALUE_SELECTOR;
292    }
293    return false;
294}
295
296bool Circuit::IsSelector(GateRef gate) const
297{
298    if (gate != NullGate()) {
299        const Gate *curGate = LoadGatePtrConst(gate);
300        OpCode op = curGate->GetOpCode();
301        return (op == OpCode::VALUE_SELECTOR) || (op == OpCode::DEPEND_SELECTOR);
302    }
303    return false;
304}
305
306GateRef Circuit::GetIn(GateRef gate, size_t idx) const
307{
308    ASSERT(idx < LoadGatePtrConst(gate)->GetNumIns());
309    if (IsInGateNull(gate, idx)) {
310        return NullGate();
311    }
312    const Gate *curGate = LoadGatePtrConst(gate);
313    return GetGateRef(curGate->GetInGateConst(idx));
314}
315
316bool Circuit::IsInGateNull(GateRef gate, size_t idx) const
317{
318    const Gate *curGate = LoadGatePtrConst(gate);
319    return curGate->GetInConst(idx)->IsGateNull();
320}
321
322bool Circuit::IsFirstOutNull(GateRef gate) const
323{
324    const Gate *curGate = LoadGatePtrConst(gate);
325    return curGate->IsFirstOutNull();
326}
327
328std::vector<GateRef> Circuit::GetOutVector(GateRef gate) const
329{
330    std::vector<GateRef> result;
331    const Gate *curGate = LoadGatePtrConst(gate);
332    if (!curGate->IsFirstOutNull()) {
333        const Out *curOut = curGate->GetFirstOutConst();
334        result.push_back(GetGateRef(curOut->GetGateConst()));
335        while (!curOut->IsNextOutNull()) {
336            curOut = curOut->GetNextOutConst();
337            result.push_back(GetGateRef(curOut->GetGateConst()));
338        }
339    }
340    return result;
341}
342
343void Circuit::NewIn(GateRef gate, size_t idx, GateRef in)
344{
345#ifndef NDEBUG
346    ASSERT(idx < LoadGatePtrConst(gate)->GetNumIns());
347    ASSERT(Circuit::IsInGateNull(gate, idx));
348#endif
349    LoadGatePtr(gate)->NewIn(idx, LoadGatePtr(in));
350}
351
352void Circuit::ModifyIn(GateRef gate, size_t idx, GateRef in)
353{
354#ifndef NDEBUG
355    ASSERT(idx < LoadGatePtrConst(gate)->GetNumIns());
356    ASSERT(!Circuit::IsInGateNull(gate, idx) || (GetOpCode(gate) == OpCode::SAVE_REGISTER));
357#endif
358    LoadGatePtr(gate)->ModifyIn(idx, LoadGatePtr(in));
359}
360
361void Circuit::DeleteIn(GateRef gate, size_t idx)
362{
363    ASSERT(idx < LoadGatePtrConst(gate)->GetNumIns());
364    ASSERT(!Circuit::IsInGateNull(gate, idx));
365    LoadGatePtr(gate)->DeleteIn(idx);
366}
367
368void Circuit::DeleteGate(GateRef gate)
369{
370    // constant in constant cache, dont delete it.
371    if (GetOpCode(gate) != OpCode::CONSTANT) {
372        LoadGatePtr(gate)->DeleteGate();
373        LoadGatePtr(gate)->SetMetaData(Nop());
374    }
375}
376
377void Circuit::DecreaseIn(GateRef gate, size_t idx)
378{
379    auto numIns = LoadGatePtrConst(gate)->GetNumIns();
380    ASSERT(numIns > 0);
381    for (size_t i = idx; i < numIns - 1; i++) {
382        ModifyIn(gate, i, GetIn(gate, i + 1));
383    }
384    DeleteIn(gate, numIns - 1);
385    GateMetaData *meta = const_cast<GateMetaData *>(
386            LoadGatePtr(gate)->GetMetaData());
387    if (meta->GetKind() == GateMetaData::Kind::MUTABLE_WITH_SIZE) {
388        meta->DecreaseIn(idx);
389    } else {
390        meta = metaBuilder_.NewGateMetaData(meta);
391        meta->DecreaseIn(idx);
392        LoadGatePtr(gate)->SetMetaData(meta);
393    }
394}
395
396void Circuit::SetGateType(GateRef gate, GateType type)
397{
398    LoadGatePtr(gate)->SetGateType(type);
399}
400
401void Circuit::SetMachineType(GateRef gate, MachineType machineType)
402{
403    LoadGatePtr(gate)->SetMachineType(machineType);
404}
405
406GateType Circuit::GetGateType(GateRef gate) const
407{
408    return LoadGatePtrConst(gate)->GetGateType();
409}
410
411MachineType Circuit::GetMachineType(GateRef gate) const
412{
413    return LoadGatePtrConst(gate)->GetMachineType();
414}
415
416OpCode Circuit::GetOpCode(GateRef gate) const
417{
418    return LoadGatePtrConst(gate)->GetOpCode();
419}
420
421GateId Circuit::GetId(GateRef gate) const
422{
423    return LoadGatePtrConst(gate)->GetId();
424}
425
426#ifndef NDEBUG
427Circuit::ScopedComment::ScopedComment(std::string &&str, std::string_view *comment)
428    : old_(*comment), comment_(comment)
429{
430    if (comment->empty()) {
431        str_ = std::move(str);
432    } else {
433        str_ = std::string{*comment} + " " + std::move(str);
434    }
435    *comment_ = {str_};
436}
437
438Circuit::ScopedComment Circuit::VisitGateBegin(GateRef visitedGate)
439{
440    return ScopedComment("old " + std::to_string(GetId(visitedGate)), &currentComment_);
441}
442
443Circuit::ScopedComment Circuit::CommentBegin(std::string &&str)
444{
445    return ScopedComment(std::move(str), &currentComment_);
446}
447#endif
448
449void Circuit::Print(GateRef gate) const
450{
451    LoadGatePtrConst(gate)->Print();
452}
453
454size_t Circuit::GetCircuitDataSize() const
455{
456    return circuitSize_;
457}
458
459const void *Circuit::GetSpaceDataStartPtrConst() const
460{
461    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
462    return GetDataPtrConst(0);
463}
464
465const void *Circuit::GetSpaceDataEndPtrConst() const
466{
467    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
468    return GetDataPtrConst(circuitSize_);
469}
470
471const uint8_t *Circuit::GetDataPtrConst(size_t offset) const
472{
473    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
474    return static_cast<uint8_t *>(space_) + offset;
475}
476
477uint8_t *Circuit::GetDataPtr(size_t offset)
478{
479    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
480    return static_cast<uint8_t *>(space_) + offset;
481}
482
483panda::ecmascript::FrameType Circuit::GetFrameType() const
484{
485    return frameType_;
486}
487
488void Circuit::SetFrameType(panda::ecmascript::FrameType type)
489{
490    frameType_ = type;
491}
492
493GateRef Circuit::GetConstantGate(MachineType machineType, uint64_t value,
494                                 GateType type)
495{
496    auto search = constantCache_.find({machineType, value, type});
497    if (search != constantCache_.end()) {
498        return search->second;
499    }
500    auto gate = NewGate(metaBuilder_.Constant(value), machineType, type);
501    constantCache_[{machineType, value, type}] = gate;
502    return gate;
503}
504
505GateRef Circuit::GetConstantGateWithoutCache(MachineType machineType, uint64_t value, GateType type)
506{
507    auto gate = NewGate(metaBuilder_.Constant(value), machineType, type);
508    return gate;
509}
510
511void Circuit::ClearConstantCache(MachineType machineType, uint64_t value, GateType type)
512{
513    auto search = constantCache_.find({machineType, value, type});
514    if (search != constantCache_.end()) {
515        constantCache_.erase(search);
516    }
517}
518
519GateRef Circuit::GetConstantStringGate(MachineType machineType, std::string_view str,
520                                       GateType type)
521{
522    auto gate = NewGate(metaBuilder_.ConstString(str), machineType, type);
523    return gate;
524}
525
526GateRef Circuit::GetInitialEnvGate(GateRef depend, GateRef jsFunc)
527{
528    auto search = initialEnvCache_.find(jsFunc);
529    if (search != initialEnvCache_.end()) {
530        return initialEnvCache_.at(jsFunc);
531    }
532    auto gate = NewGate(GetEnv(), MachineType::I64, {depend, jsFunc}, GateType::AnyType());
533    initialEnvCache_[jsFunc] = gate;
534    return gate;
535}
536
537GateRef Circuit::NewArg(MachineType machineType, size_t index,
538                        GateType type, GateRef argRoot)
539{
540    return NewGate(metaBuilder_.Arg(index), machineType, { argRoot }, type);
541}
542
543size_t Circuit::GetGateCount() const
544{
545    return gateCount_;
546}
547
548GateRef Circuit::GetStateRoot() const
549{
550    const GateAccessor acc(const_cast<Circuit*>(this));
551    return acc.GetStateRoot();
552}
553
554GateRef Circuit::GetDependRoot() const
555{
556    const GateAccessor acc(const_cast<Circuit*>(this));
557    return acc.GetDependRoot();
558}
559
560GateRef Circuit::GetArgRoot() const
561{
562    const GateAccessor acc(const_cast<Circuit*>(this));
563    return acc.GetArgRoot();
564}
565
566GateRef Circuit::GetReturnRoot() const
567{
568    const GateAccessor acc(const_cast<Circuit*>(this));
569    return acc.GetReturnRoot();
570}
571}  // namespace panda::ecmascript::kungfu
572