1/*
2 * Copyright (c) 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#include "ecmascript/stackmap/ark_stackmap_parser.h"
16
17#include "ecmascript/compiler/aot_file/aot_file_manager.h"
18#include "ecmascript/compiler/assembler/assembler.h"
19#include "ecmascript/deoptimizer/deoptimizer.h"
20
21namespace panda::ecmascript::kungfu {
22// implement simple binary-search is improve performance. if use std api, it'll trigger copy CallsiteHeader.
23int ArkStackMapParser::BinaraySearch(CallsiteHeader *callsiteHead, uint32_t callsiteNum, uintptr_t callSiteAddr) const
24{
25    int slow = 0;
26    int high = static_cast<int>(callsiteNum) - 1;
27    int mid = 0;
28    uint32_t v = 0;
29    while (slow <= high) {
30        mid = (slow + high) >> 1;
31        v = callsiteHead[mid].calliteOffsetInTxtSec;
32        if (v == callSiteAddr) {
33            return mid;
34        } else if (v > callSiteAddr) {
35            high = mid - 1;
36        } else {
37            slow = mid + 1;
38        }
39    }
40    return -1;
41}
42
43void ArkStackMapParser::GetArkDeopt(uint8_t *stackmapAddr,
44                                    const CallsiteHeader& callsiteHead,
45                                    std::vector<ARKDeopt>& deopts) const
46{
47    ParseArkDeopt(callsiteHead, stackmapAddr, deopts);
48}
49
50void ArkStackMapParser::GetArkDeopt(uintptr_t callSiteAddr,
51                                    uint8_t *stackmapAddr,
52                                    std::vector<ARKDeopt>& deopts) const
53{
54    ArkStackMapHeader *head = reinterpret_cast<ArkStackMapHeader *>(stackmapAddr);
55    ASSERT(head != nullptr);
56    if (head == nullptr) {
57        return;
58    }
59    uint32_t callsiteNum = head->callsiteNum;
60
61    CallsiteHeader *callsiteHead = reinterpret_cast<CallsiteHeader *>(stackmapAddr + sizeof(ArkStackMapHeader));
62    int mid = BinaraySearch(callsiteHead, callsiteNum, callSiteAddr);
63    if (mid == -1) {
64        return;
65    }
66    CallsiteHeader *found = callsiteHead + mid;
67    GetArkDeopt(stackmapAddr, *found, deopts);
68}
69
70void ArkStackMapParser::GetConstInfo(uintptr_t callSiteAddr,
71                                     LLVMStackMapType::ConstInfo& info,
72                                     uint8_t *stackmapAddr) const
73{
74    std::vector<ARKDeopt> deopts;
75    GetArkDeopt(callSiteAddr, stackmapAddr, deopts);
76    if (deopts.empty()) {
77        return;
78    }
79
80    ARKDeopt target;
81    LLVMStackMapType::VRegId id = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::PC_OFFSET_INDEX);
82    target.id = id;
83    auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
84        [](const ARKDeopt& a, const ARKDeopt& b) {
85            return a.id < b.id;
86        });
87    if (it == deopts.end() || (it->id > id)) {
88        return;
89    }
90    ASSERT(it->kind == LocationTy::Kind::CONSTANT);
91    ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(it->value));
92    auto v = std::get<LLVMStackMapType::IntType>(it->value);
93    info.emplace_back(v);
94}
95
96void ArkStackMapParser::GetMethodOffsetInfo(uintptr_t callSiteAddr,
97                                            std::map<uint32_t, uint32_t>& info,
98                                            uint8_t *stackmapAddr) const
99{
100    std::vector<ARKDeopt> deopts;
101    GetArkDeopt(callSiteAddr, stackmapAddr, deopts);
102    if (deopts.empty()) {
103        return;
104    }
105
106    ARKDeopt target;
107    size_t shift = Deoptimizier::ComputeShift(MAX_METHOD_OFFSET_NUM);
108    LLVMStackMapType::VRegId startId = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX);
109    for (int i = MAX_METHOD_OFFSET_NUM - 1; i >= 0; i--) {
110        LLVMStackMapType::VRegId id = startId - i;
111        target.id = Deoptimizier::EncodeDeoptVregIndex(id, i, shift);
112        auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
113            [](const ARKDeopt& a, const ARKDeopt& b) {
114                return a.id < b.id;
115            });
116        if (it == deopts.end() || (it->id > target.id)) {
117            continue;
118        }
119        ASSERT(it->kind == LocationTy::Kind::CONSTANT);
120        ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(it->value));
121        auto v = std::get<LLVMStackMapType::IntType>(it->value);
122        info[static_cast<int32_t>(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX) - id] = static_cast<uint32_t>(v);
123    }
124}
125
126// this function will increase the value of 'offset'
127uintptr_t ArkStackMapParser::GetStackSlotAddress(uint8_t *stackmapAddr, uintptr_t callSiteSp, uintptr_t callsiteFp,
128                                                 uint32_t &offset) const
129{
130    LLVMStackMapType::DwarfRegType regType;
131    LLVMStackMapType::OffsetType offsetType;
132    LLVMStackMapType::SLeb128Type regOffset;
133    size_t regOffsetSize;
134    [[maybe_unused]] bool isFull;
135    uintptr_t address = 0;
136
137    std::tie(regOffset, regOffsetSize, isFull) =
138        panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(stackmapAddr + offset);
139    LLVMStackMapType::DecodeRegAndOffset(regOffset, regType, offsetType);
140    if (regType == GCStackMapRegisters::SP) {
141        address = callSiteSp + offsetType;
142    } else if (regType == GCStackMapRegisters::FP) {
143        address = callsiteFp + offsetType;
144    } else {
145        LOG_ECMA(FATAL) << "this branch is unreachable";
146        UNREACHABLE();
147    }
148    offset += regOffsetSize;
149
150    return address;
151}
152
153bool ArkStackMapParser::IteratorStackMap(const RootVisitor& visitor,
154                                         const RootBaseAndDerivedVisitor& derivedVisitor,
155                                         uintptr_t callSiteAddr,
156                                         uintptr_t callsiteFp,
157                                         uintptr_t callSiteSp,
158                                         uint8_t *stackmapAddr) const
159{
160    ArkStackMapHeader *head = reinterpret_cast<ArkStackMapHeader *>(stackmapAddr);
161    ASSERT(head != nullptr);
162    uint32_t callsiteNum = head->callsiteNum;
163    // BuiltinsStub current only sample stub, don't have stackmap&deopt.
164    if (callsiteNum == 0) {
165        return false;
166    }
167
168    CallsiteHeader *callsiteHead = reinterpret_cast<CallsiteHeader *>(stackmapAddr + sizeof(ArkStackMapHeader));
169    int mid = BinaraySearch(callsiteHead, callsiteNum, callSiteAddr);
170    if (mid == -1) {
171        return false;
172    }
173    CallsiteHeader *found = callsiteHead + mid;
174
175    uint32_t offset = found->stackmapOffsetInSMSec;
176    uint16_t stackmapNum = found->stackmapNum;
177    ASSERT(stackmapNum % GC_ENTRY_SIZE == 0);
178    if (stackmapNum == 0) {
179        return false;
180    }
181    ASSERT(callsiteFp != callSiteSp);
182    std::map<uintptr_t, uintptr_t> baseSet;
183    for (size_t i = 0; i < stackmapNum; i += GC_ENTRY_SIZE) { // GC_ENTRY_SIZE=<base, derive>
184        uintptr_t base = GetStackSlotAddress(stackmapAddr, callSiteSp, callsiteFp, offset);
185        uintptr_t derived = GetStackSlotAddress(stackmapAddr, callSiteSp, callsiteFp, offset);
186        if (*reinterpret_cast<uintptr_t *>(base) == 0) {
187            base = derived;
188        }
189        if (*reinterpret_cast<uintptr_t *>(base) != 0) {
190            // The base address may be marked repeatedly
191            if (baseSet.find(base) == baseSet.end()) {
192                baseSet.emplace(base, *reinterpret_cast<uintptr_t *>(base));
193                visitor(Root::ROOT_FRAME, ObjectSlot(base));
194            }
195
196            if (base != derived) {
197                derivedVisitor(Root::ROOT_FRAME, ObjectSlot(base), ObjectSlot(derived), baseSet[base]);
198            }
199        }
200    }
201    baseSet.clear();
202    return true;
203}
204
205void ArkStackMapParser::ParseArkStackMap(const CallsiteHeader& callsiteHead,
206                                         uint8_t *ptr,
207                                         ArkStackMap& arkStackMaps) const
208{
209    LLVMStackMapType::DwarfRegType reg;
210    LLVMStackMapType::OffsetType offsetType;
211    uint32_t offset = callsiteHead.stackmapOffsetInSMSec;
212    uint16_t stackmapNum = callsiteHead.stackmapNum;
213    ASSERT(stackmapNum % GC_ENTRY_SIZE == 0);
214    for (uint32_t j = 0; j < stackmapNum; j++) {
215        auto [regOffset, regOffsetSize, is_full] =
216            panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + offset);
217        LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
218        offset += regOffsetSize;
219        LOG_COMPILER(VERBOSE) << " reg: " << std::dec << reg << " offset:" <<  offsetType;
220        arkStackMaps.emplace_back(std::make_pair(reg, offsetType));
221    }
222    offset = AlignUp(offset, LLVMStackMapType::STACKMAP_ALIGN_BYTES);
223}
224
225void ArkStackMapParser::ParseArkDeopt(const CallsiteHeader& callsiteHead,
226                                      uint8_t *ptr,
227                                      std::vector<ARKDeopt> &deopts) const
228{
229    ARKDeopt deopt;
230    uint32_t deoptOffset = callsiteHead.deoptOffset;
231    uint16_t deoptNum = callsiteHead.deoptNum;
232    LLVMStackMapType::KindType kindType;
233    LLVMStackMapType::DwarfRegType reg;
234    LLVMStackMapType::OffsetType offsetType;
235    ASSERT(deoptNum % DEOPT_ENTRY_SIZE == 0); // 2:<id, value>
236    for (uint32_t j = 0; j < deoptNum; j += DEOPT_ENTRY_SIZE) { // DEOPT_ENTRY_SIZE:<id, value>
237        auto [vregsInfo, vregsInfoSize, InfoIsFull] =
238            panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
239        LLVMStackMapType::DecodeVRegsInfo(vregsInfo, deopt.id, kindType);
240        deoptOffset += vregsInfoSize;
241        ASSERT(kindType == LLVMStackMapType::CONSTANT_TYPE || kindType == LLVMStackMapType::OFFSET_TYPE);
242        if (kindType == LLVMStackMapType::CONSTANT_TYPE) {
243            auto [constant, constantSize, constIsFull] =
244                panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
245            if (constant > INT32_MAX || constant < INT32_MIN) {
246                deopt.kind = LocationTy::Kind::CONSTANTNDEX;
247                deopt.value = static_cast<LLVMStackMapType::LargeInt>(constant);
248            } else {
249                deopt.kind = LocationTy::Kind::CONSTANT;
250                deopt.value = static_cast<LLVMStackMapType::IntType>(constant);
251            }
252            deoptOffset += constantSize;
253        } else {
254            auto [regOffset, regOffsetSize, regOffIsFull] =
255                panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
256            LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
257            deopt.kind = LocationTy::Kind::INDIRECT;
258            deopt.value = std::make_pair(reg, offsetType);
259            deoptOffset += regOffsetSize;
260        }
261        deopts.emplace_back(deopt);
262    }
263}
264
265#ifndef NDEBUG
266void ArkStackMapParser::ParseArkStackMapAndDeopt(uint8_t *ptr, uint32_t length) const
267{
268    CallsiteHeader callsiteHead;
269    ArkStackMapHeader secHead;
270    BinaryBufferParser binBufparser(ptr, length);
271    binBufparser.ParseBuffer(&secHead, sizeof(ArkStackMapHeader));
272    for (uint32_t i = 0; i < secHead.callsiteNum; i++) {
273        binBufparser.ParseBuffer(&callsiteHead, sizeof(CallsiteHeader));
274        std::vector<ARKDeopt> deopts;
275        ArkStackMap arkStackMaps;
276        LOG_COMPILER(VERBOSE) << " calliteOffsetInTxtSec: 0x" << std::hex << callsiteHead.calliteOffsetInTxtSec
277                              << " stackmap offset: 0x" << std::hex << callsiteHead.stackmapOffsetInSMSec
278                              << " num:" << callsiteHead.stackmapNum
279                              << " deopt Offset: 0x" << std::hex << callsiteHead.deoptOffset
280                              << " num:" << callsiteHead.deoptNum;
281        ParseArkStackMap(callsiteHead, ptr, arkStackMaps);
282        ParseArkDeopt(callsiteHead, ptr, deopts);
283    }
284}
285#endif
286} // namespace panda::ecmascript::kungfu