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