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