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#include "ecmascript/stackmap/llvm/llvm_stackmap_parser.h"
16
17using namespace panda::ecmascript;
18
19namespace panda::ecmascript::kungfu {
20std::string LocationTy::TypeToString(Kind loc) const
21{
22    switch (loc) {
23        case Kind::REGISTER:
24            return "Register	Reg	Value in a register";
25        case Kind::DIRECT:
26            return "Direct	Reg + Offset	Frame index value";
27        case Kind::INDIRECT:
28            return "Indirect	[Reg + Offset]	Spilled value";
29        case Kind::CONSTANT:
30            return "Constant	Offset	Small constant";
31        case Kind::CONSTANTNDEX:
32            return "ConstIndex	constants[Offset]	Large constant";
33        default:
34            return "no know location";
35    }
36}
37
38void LLVMStackMapParser::FilterCallSiteInfo(LLVMStackMapType::CallSiteInfo &info)
39{
40    ASSERT(GC_PAIR_SIZE == 2); // 2 : The expected value of GC_PAIR_SIZE is 2
41    ASSERT(info.size() % GC_PAIR_SIZE == 0);
42    for (auto it = info.begin(); it != info.end();) {
43        auto base = it;
44        auto deri = ++it;
45        bool baseIsConst = (base->first == LLVMStackMapType::INVALID_DWARF_REG);
46        bool deriIsConst = (deri->first == LLVMStackMapType::INVALID_DWARF_REG);
47        if (baseIsConst && deriIsConst) {
48            it = info.erase(base, base + GC_PAIR_SIZE);
49        } else if (baseIsConst && !deriIsConst) {
50            base->first = deri->first;
51            base->second = deri->second;
52            it++;
53        } else if (!baseIsConst && deriIsConst) {
54            deri->first = base->first;
55            deri->second = base->second;
56            it++;
57        } else {
58            it++;
59        }
60    }
61    ASSERT(info.size() % GC_PAIR_SIZE == 0);
62}
63
64void LLVMStackMapParser::CalcCallSite()
65{
66    uint64_t recordNum = 0;
67    LLVMStackMapType::Pc2CallSiteInfo pc2CallSiteInfo;
68    LLVMStackMapType::Pc2Deopt deoptbundles;
69
70    auto calStkMapRecordFunc = [this, &recordNum, &pc2CallSiteInfo, &deoptbundles](uintptr_t address,
71                                                                                   uint32_t recordId) {
72        struct StkMapRecordTy &record = llvmStackMap_.stkMapRecord[recordNum + recordId];
73        struct StkMapRecordHeadTy &recordHead = record.head;
74        uint32_t instructionOffset = recordHead.instructionOffset;
75        uintptr_t pc = address + instructionOffset;
76        uint64_t pID = recordHead.patchPointID;
77
78        if (pc2CallSiteInfo.find(pc) == pc2CallSiteInfo.end()) {
79            auto p = std::pair<uintptr_t, LLVMStackMapType::CallSiteInfo>(pc, {});
80            pc2CallSiteInfo.insert(p);
81        }
82        LLVMStackMapType::CallSiteInfo& callSiteInfo = pc2CallSiteInfo.find(pc)->second;
83
84        ASSERT(recordHead.numLocations > LocationTy::CONSTANT_DEOPT_CNT_INDEX);
85        const int lastDeoptIndex = record.locations[LocationTy::CONSTANT_DEOPT_CNT_INDEX].offsetOrSmallConstant +
86                                   LocationTy::CONSTANT_DEOPT_CNT_INDEX;
87
88        for (int j = LocationTy::CONSTANT_FIRST_ELEMENT_INDEX; j < recordHead.numLocations; j++) {
89            const struct LocationTy &loc = record.locations[j];
90            if (j <= lastDeoptIndex) {
91                switch (loc.location) {
92                    case LocationTy::Kind::REGISTER:
93                    case LocationTy::Kind::DIRECT: {
94                        LOG_ECMA(FATAL) << "this branch is unreachable";
95                        UNREACHABLE();
96                        break;
97                    }
98                    case LocationTy::Kind::INDIRECT: {
99                        OPTIONAL_LOG_COMPILER(DEBUG) << "DwarfRegNum:" << loc.dwarfRegNum
100                                                     << " loc.OffsetOrSmallConstant:" << loc.offsetOrSmallConstant
101                                                     << " address:" << address
102                                                     << " instructionOffset:" << instructionOffset
103                                                     << " callsite:" << "  patchPointID :" << std::hex
104                                                     << pID << pc;
105                        LLVMStackMapType::DwarfRegAndOffsetType info(loc.dwarfRegNum, loc.offsetOrSmallConstant);
106                        deoptbundles[pc].push_back(info);
107                        break;
108                    }
109                    case LocationTy::Kind::CONSTANT: {
110                        deoptbundles[pc].push_back(loc.offsetOrSmallConstant);
111                        break;
112                    }
113                    case LocationTy::Kind::CONSTANTNDEX: {
114                        auto v = llvmStackMap_.constants[loc.offsetOrSmallConstant].largeConstant;
115                        deoptbundles[pc].push_back(static_cast<LLVMStackMapType::LargeInt>(v));
116                        break;
117                    }
118                    default: {
119                        LOG_ECMA(FATAL) << "this branch is unreachable";
120                        UNREACHABLE();
121                        break;
122                    }
123                }
124            } else {
125                switch (loc.location) {
126                    case LocationTy::Kind::REGISTER:
127                    case LocationTy::Kind::DIRECT: {
128                        LOG_ECMA(FATAL) << "this branch is unreachable";
129                        UNREACHABLE();
130                        break;
131                    }
132                    case LocationTy::Kind::INDIRECT:
133                    case LocationTy::Kind::CONSTANT:
134                    case LocationTy::Kind::CONSTANTNDEX: {
135                        OPTIONAL_LOG_COMPILER(DEBUG) << "DwarfRegNum:" << loc.dwarfRegNum
136                                                     << " loc.OffsetOrSmallConstant:" << loc.offsetOrSmallConstant
137                                                     << " address:" << address
138                                                     << " instructionOffset:" << instructionOffset
139                                                     << " callsite:" << "  patchPointID :" << std::hex
140                                                     << pID << pc;
141                        uint16_t regNum = (loc.location == LocationTy::Kind::INDIRECT)
142                                          ? loc.dwarfRegNum
143                                          : LLVMStackMapType::INVALID_DWARF_REG;
144                        int offset = (loc.location == LocationTy::Kind::INDIRECT) ? loc.offsetOrSmallConstant : 0;
145                        LLVMStackMapType::DwarfRegAndOffsetType info(regNum, offset);
146                        callSiteInfo.emplace_back(info);
147                        break;
148                    }
149                    default: {
150                        LOG_ECMA(FATAL) << "this branch is unreachable";
151                        UNREACHABLE();
152                        break;
153                    }
154                }
155            }
156        }
157        FilterCallSiteInfo(callSiteInfo);
158    };
159
160    const size_t count = llvmStackMap_.stkSizeRecords.size();
161    for (size_t i = 0; i < count; i++) {
162        // relative offset
163        struct StkMapSizeRecordTy &sizeRec = llvmStackMap_.stkSizeRecords[i];
164        uintptr_t address = sizeRec.functionAddress;
165        uint64_t recordCount = sizeRec.recordCount;
166        fun2RecordNum_.emplace_back(std::make_pair(address, recordCount));
167        for (uint64_t k = 0; k < recordCount; k++) {
168            calStkMapRecordFunc(address, k);
169        }
170        recordNum += recordCount;
171    }
172
173    stackMapInfo.AppendCallSiteInfo(pc2CallSiteInfo);
174    stackMapInfo.AppendDeoptInfo(deoptbundles);
175}
176
177bool LLVMStackMapParser::CalculateStackMap(std::unique_ptr<uint8_t []> stackMapAddr)
178{
179    if (!stackMapAddr) {
180        LOG_COMPILER(ERROR) << "stackMapAddr nullptr error ! ";
181        return false;
182    }
183    dataInfo_ = std::make_unique<DataInfo>(std::move(stackMapAddr));
184    llvmStackMap_.head = dataInfo_->Read<struct Header>();
185    uint32_t numFunctions, numConstants, numRecords;
186    numFunctions = dataInfo_->Read<uint32_t>();
187    numConstants = dataInfo_->Read<uint32_t>();
188    numRecords = dataInfo_->Read<uint32_t>();
189    for (uint32_t i = 0; i < numFunctions; i++) {
190        auto stkRecord = dataInfo_->Read<struct StkMapSizeRecordTy>();
191        llvmStackMap_.stkSizeRecords.push_back(stkRecord);
192    }
193
194    for (uint32_t i = 0; i < numConstants; i++) {
195        auto val = dataInfo_->Read<struct ConstantsTy>();
196        llvmStackMap_.constants.push_back(val);
197    }
198    for (uint32_t i = 0; i < numRecords; i++) {
199        struct StkMapRecordTy stkSizeRecord;
200        auto head = dataInfo_->Read<struct StkMapRecordHeadTy>();
201        stkSizeRecord.head = head;
202        for (uint16_t j = 0; j < head.numLocations; j++) {
203            auto location = dataInfo_->Read<struct LocationTy>();
204            stkSizeRecord.locations.push_back(location);
205        }
206        while (dataInfo_->GetOffset() & 7) { // 7: 8 byte align
207            dataInfo_->Read<uint16_t>();
208        }
209        uint32_t numLiveOuts = dataInfo_->Read<uint32_t>();
210        if (numLiveOuts > 0) {
211            for (uint32_t j = 0; j < numLiveOuts; j++) {
212                auto liveOut = dataInfo_->Read<struct LiveOutsTy>();
213                stkSizeRecord.liveOuts.push_back(liveOut);
214            }
215        }
216        while (dataInfo_->GetOffset() & 7) { // 7: 8 byte align
217            dataInfo_->Read<uint16_t>();
218        }
219        llvmStackMap_.stkMapRecord.push_back(stkSizeRecord);
220    }
221    CalcCallSite();
222    return true;
223}
224
225uint32_t ARKCallsite::CalHeadSize() const
226{
227    uint32_t headSize = sizeof(head);
228    return headSize;
229}
230
231uint32_t ARKCallsite::CalStackMapSize(Triple triple) const
232{
233    size_t stackmapSize = 0;
234    for (auto &x : stackmaps) {
235        std::vector<uint8_t> value;
236        size_t valueSize = 0;
237        LLVMStackMapType::EncodeRegAndOffset(value, valueSize, x.first, x.second, triple);
238        stackmapSize += value.size();
239    }
240    return stackmapSize;
241}
242
243bool LLVMStackMapParser::CalculateStackMap(std::unique_ptr<uint8_t []> stackMapAddr,
244    uintptr_t hostCodeSectionAddr, uintptr_t hostCodeSectionOffset)
245{
246    bool ret = CalculateStackMap(std::move(stackMapAddr));
247    if (!ret) {
248        return false;
249    }
250
251    OPTIONAL_LOG_COMPILER(DEBUG) << "stackmap calculate update funcitonaddress ";
252
253    for (size_t i = 0; i < llvmStackMap_.stkSizeRecords.size(); i++) {
254        uintptr_t hostAddr = llvmStackMap_.stkSizeRecords[i].functionAddress;
255        uintptr_t offset = hostAddr - hostCodeSectionAddr + hostCodeSectionOffset;
256        llvmStackMap_.stkSizeRecords[i].functionAddress = offset;
257        OPTIONAL_LOG_COMPILER(DEBUG) << std::dec << i << "th function " << std::hex << hostAddr << " ---> "
258                                     << " offset:" << offset;
259    }
260    stackMapInfo.PopCallSiteInfo();
261    stackMapInfo.PopDeoptInfo();
262    fun2RecordNum_.clear();
263    CalcCallSite();
264    return true;
265}
266}  // namespace panda::ecmascript::kungfu
267