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 <map>
16
17#include "ecmascript/stackmap/ark_stackmap_builder.h"
18#include "ecmascript/stackmap/ark_stackmap_parser.h"
19#include "ecmascript/stackmap/litecg/litecg_stackmap_type.h"
20#include "ecmascript/stackmap/llvm/llvm_stackmap_parser.h"
21
22namespace panda::ecmascript::kungfu {
23void BinaryBufferWriter::WriteBuffer(const uint8_t *src, uint32_t count, bool flag)
24{
25    uint8_t *dst = buffer_ + offset_;
26    if (flag) {
27        std::cout << "buffer_:0x" << std::hex << buffer_ << " offset_:0x" << offset_ << std::endl;
28    }
29    if (dst >= buffer_ && dst + count <= buffer_ + length_) {
30        if (memcpy_s(dst, buffer_ + length_ - dst, src, count) != EOK) {
31            LOG_FULL(FATAL) << "memcpy_s failed";
32            return;
33        };
34        offset_ = offset_ + count;
35    }  else {
36        LOG_FULL(FATAL) << "parse buffer error, length is 0 or overflow";
37    }
38}
39
40void ArkStackMapBuilder::Dump(const StackMapDumper& dumpInfo) const
41{
42    LOG_COMPILER(INFO) << "total callsite num: " << dumpInfo.callsiteNum
43                       << ", total ark stack map num: " << dumpInfo.stackmapNum
44                       << ", total deopt num: " << dumpInfo.deoptNum;
45    double callsiteHeadsSize = static_cast<double>(dumpInfo.callsiteHeadSize);
46    double stackMapsSize = static_cast<double>(dumpInfo.arkStackMapSize);
47    double deoptsSize = static_cast<double>(dumpInfo.deoptSize);
48    LOG_COMPILER(INFO) << "total callsite head size: "
49                       << std::fixed << std::setprecision(DECIMAL_LENS)
50                       << (callsiteHeadsSize / 1_KB) << "KB, total stackmap size: "
51                       << std::fixed << std::setprecision(DECIMAL_LENS)
52                       << (stackMapsSize / 1_KB) << "KB, total deopt size: "
53                       << std::fixed << std::setprecision(DECIMAL_LENS)
54                       << (deoptsSize / 1_KB) << "KB";
55}
56
57std::pair<std::shared_ptr<uint8_t>, uint32_t> ArkStackMapBuilder::Run(std::unique_ptr<uint8_t []> stackMapAddr,
58    uintptr_t hostCodeSectionAddr, Triple triple)
59{
60    LLVMStackMapInfo stackMapInfo;
61    LLVMStackMapParser parser(stackMapInfo);
62    auto result = parser.CalculateStackMap(std::move(stackMapAddr), hostCodeSectionAddr, 0);
63    if (!result) {
64        LOG_ECMA(FATAL) << "this branch is unreachable";
65        UNREACHABLE();
66    }
67    std::pair<std::shared_ptr<uint8_t>, uint32_t> info = GenerateArkStackMap(stackMapInfo, triple);
68    return info;
69}
70
71void ArkStackMapBuilder::Collect(
72    std::unique_ptr<uint8_t []> stackMapAddr,
73    uintptr_t hostCodeSectionAddr,
74    uintptr_t hostCodeSectionOffset,
75    CGStackMapInfo &stackMapInfo)
76{
77    LLVMStackMapInfo &llvmStackMapInfo = static_cast<LLVMStackMapInfo&>(stackMapInfo);
78    LLVMStackMapParser parser(llvmStackMapInfo);
79    auto result = parser.CalculateStackMap(std::move(stackMapAddr), hostCodeSectionAddr, hostCodeSectionOffset);
80    if (!result) {
81        LOG_ECMA(FATAL) << "this branch is unreachable";
82        UNREACHABLE();
83    }
84}
85
86std::pair<std::shared_ptr<uint8_t>, uint32_t> ArkStackMapBuilder::GenerateArkStackMap(
87    CGStackMapInfo &stackMapInfo, Triple triple)
88{
89    ARKCallsiteAOTFileInfo AOTFileInfo;
90    GenArkCallsiteAOTFileInfo(stackMapInfo, AOTFileInfo, triple);
91    uint32_t secSize = AOTFileInfo.secHead.secSize;
92    uint8_t *p = new(std::nothrow) uint8_t[secSize];
93    if (p == nullptr) {
94        LOG_FULL(FATAL) << "new secSize:0x" << std::hex << secSize << " failed";
95    }
96    std::shared_ptr<uint8_t> ptr(p, [](uint8_t *p) { delete []p;});
97    SaveArkCallsiteAOTFileInfo(ptr.get(), secSize, AOTFileInfo, triple);
98    if (traceStackMap_) {
99        Dump(dumper_);
100    }
101    return std::make_pair(ptr, secSize);
102}
103
104void ArkStackMapBuilder::SaveArkStackMap(const ARKCallsiteAOTFileInfo& info, BinaryBufferWriter& writer, Triple triple)
105{
106    size_t n = info.callsites.size();
107    for (size_t i = 0; i < n; i++) {
108        auto &callSite = info.callsites.at(i);
109        LLVMStackMapType::CallSiteInfo stackmaps = callSite.stackmaps;
110        size_t m = stackmaps.size();
111        for (size_t j = 0; j < m; j++) {
112            auto &stackmap = stackmaps.at(j);
113            LLVMStackMapType::DwarfRegType reg = stackmap.first;
114            LLVMStackMapType::OffsetType offset = stackmap.second;
115            if (j == 0) {
116                ASSERT(callSite.head.stackmapOffsetInSMSec == writer.GetOffset());
117            }
118            std::vector<uint8_t> regOffset;
119            size_t regOffsetSize = 0;
120            LLVMStackMapType::EncodeRegAndOffset(regOffset, regOffsetSize, reg, offset, triple);
121            writer.WriteBuffer(reinterpret_cast<const uint8_t *>(regOffset.data()), regOffset.size());
122            dumper_.arkStackMapSize += regOffsetSize;
123            if (j == m - 1) {
124                ASSERT((callSite.head.stackmapOffsetInSMSec + callSite.CalStackMapSize(triple)) == writer.GetOffset());
125            }
126        }
127    }
128    writer.AlignOffset();
129}
130
131void ArkStackMapBuilder::SaveArkDeopt(const ARKCallsiteAOTFileInfo& info, BinaryBufferWriter& writer, Triple triple)
132{
133    for (auto &it: info.callsites) {
134        auto& callsite2Deopt = it.callsite2Deopt;
135        size_t m = callsite2Deopt.size();
136        for (size_t j = 0; j < m; j++) {
137            auto &deopt = callsite2Deopt.at(j);
138            if (j == 0) {
139                ASSERT(it.head.deoptOffset == writer.GetOffset());
140            }
141            std::vector<uint8_t> vregsInfo;
142            size_t vregsInfoSize = 0;
143            LLVMStackMapType::EncodeVRegsInfo(vregsInfo, vregsInfoSize, deopt.id, deopt.kind);
144            writer.WriteBuffer(reinterpret_cast<const uint8_t *>(vregsInfo.data()), vregsInfoSize);
145            dumper_.deoptSize += vregsInfoSize;
146            auto& value = deopt.value;
147            if (std::holds_alternative<LLVMStackMapType::IntType>(value)) {
148                LLVMStackMapType::IntType v = std::get<LLVMStackMapType::IntType>(value);
149                std::vector<uint8_t> num;
150                size_t numSize = 0;
151                LLVMStackMapType::EncodeData(num, numSize, v);
152                writer.WriteBuffer(reinterpret_cast<const uint8_t *>(num.data()), numSize);
153                dumper_.deoptSize += numSize;
154            } else if (std::holds_alternative<LLVMStackMapType::LargeInt>(value)) {
155                LLVMStackMapType::LargeInt v = std::get<LLVMStackMapType::LargeInt>(value);
156                std::vector<uint8_t> num;
157                size_t numSize = 0;
158                LLVMStackMapType::EncodeData(num, numSize, v);
159                writer.WriteBuffer(reinterpret_cast<const uint8_t *>(num.data()), numSize);
160                dumper_.deoptSize += numSize;
161            } else if (std::holds_alternative<LLVMStackMapType::DwarfRegAndOffsetType>(value)) {
162                LLVMStackMapType::DwarfRegAndOffsetType v = std::get<LLVMStackMapType::DwarfRegAndOffsetType>(value);
163                std::vector<uint8_t> regOffset;
164                size_t regOffsetSize = 0;
165                LLVMStackMapType::EncodeRegAndOffset(regOffset, regOffsetSize, v.first, v.second, triple);
166                writer.WriteBuffer(reinterpret_cast<const uint8_t *>(regOffset.data()), regOffset.size());
167                dumper_.arkStackMapSize += regOffsetSize;
168            } else {
169                LOG_ECMA(FATAL) << "this branch is unreachable";
170                UNREACHABLE();
171            }
172        }
173    }
174}
175
176void ArkStackMapBuilder::SaveArkCallsiteAOTFileInfo(uint8_t *ptr, uint32_t length,
177    const ARKCallsiteAOTFileInfo& info, Triple triple)
178{
179    BinaryBufferWriter writer(ptr, length);
180    ASSERT(length >= info.secHead.secSize);
181    writer.WriteBuffer(reinterpret_cast<const uint8_t *>(&(info.secHead)), sizeof(ArkStackMapHeader));
182    dumper_.callsiteHeadSize += sizeof(ArkStackMapHeader);
183    for (auto &it: info.callsites) {
184        writer.WriteBuffer(reinterpret_cast<const uint8_t *>(&(it.head)), sizeof(CallsiteHeader));
185        dumper_.callsiteHeadSize += sizeof(CallsiteHeader);
186    }
187    SaveArkStackMap(info, writer, triple);
188    SaveArkDeopt(info, writer, triple);
189#ifndef NDEBUG
190    ArkStackMapParser parser;
191    parser.ParseArkStackMapAndDeopt(ptr, length);
192#endif
193}
194
195template <class Vec>
196void ArkStackMapBuilder::SortCallSite(
197    const std::vector<std::unordered_map<uintptr_t, Vec>> &infos,
198    std::vector<std::pair<uintptr_t, Vec>>& result)
199{
200    for (auto &info: infos) {
201        for (auto &it: info) {
202            result.emplace_back(it);
203        }
204    }
205    std::sort(result.begin(), result.end(),
206        [](const std::pair<uintptr_t, Vec> &x, const std::pair<uintptr_t, Vec> &y) {
207            return x.first < y.first;
208        });
209}
210
211void ArkStackMapBuilder::CalcCallsitePc(std::vector<std::pair<uintptr_t, LLVMStackMapType::DeoptInfoType>> &pc2Deopt,
212    std::vector<std::pair<uintptr_t, LLVMStackMapType::CallSiteInfo>> &pc2StackMap, std::vector<intptr_t> &callsitePcs)
213{
214    std::set<uintptr_t> pcSet;
215    for (auto &it: pc2Deopt) {
216        pcSet.insert(it.first);
217    }
218    for (auto &it: pc2StackMap) {
219        pcSet.insert(it.first);
220    }
221    callsitePcs.assign(pcSet.begin(), pcSet.end());
222}
223
224int ArkStackMapBuilder::FindLoc(std::vector<intptr_t> &CallsitePcs, intptr_t pc)
225{
226    for (size_t i = 0; i < CallsitePcs.size(); i++) {
227        if (CallsitePcs[i] == pc) {
228            return i;
229        }
230    }
231    return -1;
232}
233
234void ArkStackMapBuilder::GenARKDeopt(const LLVMStackMapType::DeoptInfoType& deopt, std::pair<uint32_t,
235                                     std::vector<ARKDeopt>> &sizeAndArkDeopt, Triple triple)
236{
237    ASSERT(deopt.size() % DEOPT_ENTRY_SIZE == 0); // 2:<id, value>
238    uint32_t total = 0;
239    ARKDeopt v;
240    for (size_t i = 0; i < deopt.size(); i += 2) { // 2:<id, value>
241        ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(deopt[i]));
242        LLVMStackMapType::VRegId id = static_cast<LLVMStackMapType::VRegId>(
243            std::get<LLVMStackMapType::IntType>(deopt[i]));
244        v.id = id;
245        auto value = deopt[i + 1];
246        if (std::holds_alternative<LLVMStackMapType::IntType>(value)) {
247            v.kind = LocationTy::Kind::CONSTANT;
248            v.value = std::get<LLVMStackMapType::IntType>(value);
249            std::vector<uint8_t> vregsInfo;
250            size_t vregsInfoSize = 0;
251            LLVMStackMapType::EncodeVRegsInfo(vregsInfo, vregsInfoSize, v.id, v.kind);
252            size_t valueSize = panda::leb128::SignedEncodingSize(std::get<LLVMStackMapType::IntType>(value));
253            total += (vregsInfoSize + valueSize);
254        } else if (std::holds_alternative<LLVMStackMapType::LargeInt>(value)) {
255            v.kind = LocationTy::Kind::CONSTANTNDEX;
256            v.value = std::get<LLVMStackMapType::LargeInt>(value);
257            std::vector<uint8_t> vregsInfo;
258            size_t vregsInfoSize = 0;
259            LLVMStackMapType::EncodeVRegsInfo(vregsInfo, vregsInfoSize, v.id, v.kind);
260            size_t valueSize = panda::leb128::SignedEncodingSize(std::get<LLVMStackMapType::LargeInt>(value));
261            total += (vregsInfoSize + valueSize);
262        } else if (std::holds_alternative<LLVMStackMapType::DwarfRegAndOffsetType>(value)) {
263            v.kind = LocationTy::Kind::INDIRECT;
264            v.value = std::get<LLVMStackMapType::DwarfRegAndOffsetType>(value);
265            std::vector<uint8_t> vregsInfo;
266            size_t vregsInfoSize = 0;
267            LLVMStackMapType::EncodeVRegsInfo(vregsInfo, vregsInfoSize, v.id, v.kind);
268            LLVMStackMapType::DwarfRegType reg = std::get<LLVMStackMapType::DwarfRegAndOffsetType>(value).first;
269            LLVMStackMapType::OffsetType offset = std::get<LLVMStackMapType::DwarfRegAndOffsetType>(value).second;
270            std::vector<uint8_t> regOffset;
271            size_t regOffsetSize = 0;
272            LLVMStackMapType::EncodeRegAndOffset(regOffset, regOffsetSize, reg, offset, triple);
273            total += (vregsInfoSize + regOffsetSize);
274        } else {
275            LOG_ECMA(FATAL) << "this branch is unreachable";
276            UNREACHABLE();
277        }
278        sizeAndArkDeopt.second.emplace_back(v);
279    }
280    std::sort(sizeAndArkDeopt.second.begin(), sizeAndArkDeopt.second.end(),
281        [](const ARKDeopt &a, const ARKDeopt &b) {
282            return a.id < b.id;
283        });
284    sizeAndArkDeopt.first = total;
285}
286
287void ArkStackMapBuilder::GenArkCallsiteAOTFileInfo(const CGStackMapInfo &stackMapInfo,
288                                                   ARKCallsiteAOTFileInfo &result, Triple triple)
289{
290    std::vector<std::pair<uintptr_t, LLVMStackMapType::CallSiteInfo>> pc2StackMaps;
291    std::vector<std::pair<uintptr_t, LLVMStackMapType::DeoptInfoType>> pc2Deopts;
292    if (stackMapInfo.GetStackMapKind() == CGStackMapInfo::kLiteCGStackMapInfo) {
293        std::vector<LLVMStackMapType::Pc2CallSiteInfo> pc2StackMapsVec;
294        std::vector<LLVMStackMapType::Pc2Deopt> pc2DeoptInfoVec;
295        const auto &liteCGStackMapInfo = static_cast<const LiteCGStackMapInfo&>(stackMapInfo);
296        liteCGStackMapInfo.ConvertToLLVMStackMapInfo(pc2StackMapsVec, pc2DeoptInfoVec, triple);
297        SortCallSite(pc2StackMapsVec, pc2StackMaps);
298        SortCallSite(pc2DeoptInfoVec, pc2Deopts);
299    } else {
300        const auto &llvmStackMapInfo = static_cast<const LLVMStackMapInfo&>(stackMapInfo);
301        SortCallSite(llvmStackMapInfo.GetCallSiteInfoVec(), pc2StackMaps);
302        SortCallSite(llvmStackMapInfo.GetDeoptInfoVec(), pc2Deopts);
303    }
304    ARKCallsite callsite;
305    uint32_t secSize = 0;
306
307    std::vector<intptr_t> CallsitePcs;
308
309    CalcCallsitePc(pc2Deopts, pc2StackMaps, CallsitePcs);
310    uint32_t callsiteNum = CallsitePcs.size();
311    dumper_.callsiteNum = callsiteNum;
312    result.callsites.resize(callsiteNum);
313    uint32_t stackmapOffset = sizeof(ArkStackMapHeader) + sizeof(CallsiteHeader) * callsiteNum;
314    for (auto &x: pc2StackMaps) {
315        LLVMStackMapType::CallSiteInfo i = x.second;
316        callsite.head.calliteOffsetInTxtSec = x.first;
317        ASSERT(std::numeric_limits<uint16_t>::min() <= i.size() && i.size() <= std::numeric_limits<uint16_t>::max());
318        callsite.head.stackmapNum = i.size();
319        callsite.head.stackmapOffsetInSMSec = stackmapOffset;
320        callsite.head.deoptOffset = 0;
321        callsite.head.deoptNum = 0;
322        callsite.stackmaps = i;
323        stackmapOffset += callsite.CalStackMapSize(triple);
324        int loc = FindLoc(CallsitePcs, x.first);
325        ASSERT(loc >= 0 && loc < static_cast<int>(callsiteNum));
326        result.callsites[static_cast<uint32_t>(loc)] = callsite;
327        dumper_.stackmapNum += i.size();
328    }
329    stackmapOffset = AlignUp(stackmapOffset, LLVMStackMapType::STACKMAP_ALIGN_BYTES);
330    secSize = stackmapOffset;
331    for (auto &x: pc2Deopts) {
332        int loc = FindLoc(CallsitePcs, x.first);
333        ASSERT(loc >= 0 && loc < static_cast<int>(callsiteNum));
334        LLVMStackMapType::DeoptInfoType deopt = x.second;
335        result.callsites[static_cast<uint32_t>(loc)].head.calliteOffsetInTxtSec = x.first;
336        ASSERT(std::numeric_limits<uint16_t>::min() <= deopt.size()
337            && deopt.size() <= std::numeric_limits<uint16_t>::max());
338        result.callsites[static_cast<uint32_t>(loc)].head.deoptNum = deopt.size();
339        result.callsites[static_cast<uint32_t>(loc)].head.deoptOffset = secSize;
340        std::pair<uint32_t, std::vector<ARKDeopt>> sizeAndArkDeopt;
341        GenARKDeopt(deopt, sizeAndArkDeopt, triple);
342        secSize += sizeAndArkDeopt.first;
343        result.callsites[static_cast<uint32_t>(loc)].callsite2Deopt = sizeAndArkDeopt.second;
344        dumper_.deoptNum += deopt.size();
345    }
346    result.secHead.callsiteNum = callsiteNum;
347    result.secHead.secSize = secSize;
348}
349} // namespace panda::ecmascript::kungfu
350