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
ParseArkDeopt(const CallsiteHeader & callsiteHead,uint8_t * ptr,std::vector<ARKDeopt> & deopts) const22 void ArkStackMapParser::ParseArkDeopt(const CallsiteHeader& callsiteHead,
23 uint8_t *ptr,
24 std::vector<ARKDeopt> &deopts) const
25 {
26 ARKDeopt deopt;
27 uint32_t deoptOffset = callsiteHead.deoptOffset;
28 uint16_t deoptNum = callsiteHead.deoptNum;
29 LLVMStackMapType::KindType kindType;
30 LLVMStackMapType::DwarfRegType reg;
31 LLVMStackMapType::OffsetType offsetType;
32 ASSERT(deoptNum % DEOPT_ENTRY_SIZE == 0); // 2:<id, value>
33 for (uint32_t j = 0; j < deoptNum; j += DEOPT_ENTRY_SIZE) { // DEOPT_ENTRY_SIZE:<id, value>
34 auto [vregsInfo, vregsInfoSize, InfoIsFull] =
35 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
36 LLVMStackMapType::DecodeVRegsInfo(vregsInfo, deopt.id, kindType);
37 deoptOffset += vregsInfoSize;
38 ASSERT(kindType == LLVMStackMapType::CONSTANT_TYPE || kindType == LLVMStackMapType::OFFSET_TYPE);
39 if (kindType == LLVMStackMapType::CONSTANT_TYPE) {
40 auto [constant, constantSize, constIsFull] =
41 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
42 if (constant > INT32_MAX || constant < INT32_MIN) {
43 deopt.kind = LocationTy::Kind::CONSTANTNDEX;
44 deopt.value = static_cast<LLVMStackMapType::LargeInt>(constant);
45 } else {
46 deopt.kind = LocationTy::Kind::CONSTANT;
47 deopt.value = static_cast<LLVMStackMapType::IntType>(constant);
48 }
49 deoptOffset += constantSize;
50 } else {
51 auto [regOffset, regOffsetSize, regOffIsFull] =
52 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
53 LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
54 deopt.kind = LocationTy::Kind::INDIRECT;
55 deopt.value = std::make_pair(reg, offsetType);
56 deoptOffset += regOffsetSize;
57 }
58 deopts.emplace_back(deopt);
59 }
60 }
61
GetArkDeopt(uint8_t * stackmapAddr,const CallsiteHeader & callsiteHead,std::vector<ARKDeopt> & deopts) const62 void ArkStackMapParser::GetArkDeopt(uint8_t *stackmapAddr,
63 const CallsiteHeader& callsiteHead,
64 std::vector<ARKDeopt>& deopts) const
65 {
66 ParseArkDeopt(callsiteHead, stackmapAddr, deopts);
67 }
68
69 // 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) const70 int ArkStackMapParser::BinaraySearch(CallsiteHeader *callsiteHead, uint32_t callsiteNum, uintptr_t callSiteAddr) const
71 {
72 int low = 0;
73 int high = static_cast<int>(callsiteNum) - 1;
74 int mid = 0;
75 uint32_t v = 0;
76 while (low <= high) {
77 mid = (low + high) / BINARY_SEARCH_DIVISOR;
78 v = callsiteHead[mid].calliteOffsetInTxtSec;
79 if (v == callSiteAddr) {
80 return mid;
81 } else if (v > callSiteAddr) {
82 high = mid - 1;
83 } else {
84 low = mid + 1;
85 }
86 }
87 return -1;
88 }
89
GetArkDeopt(uintptr_t callSiteAddr,uint8_t * stackmapAddr,std::vector<ARKDeopt> & deopts) const90 void ArkStackMapParser::GetArkDeopt(uintptr_t callSiteAddr,
91 uint8_t *stackmapAddr,
92 std::vector<ARKDeopt>& deopts) const
93 {
94 ArkStackMapHeader *head = reinterpret_cast<ArkStackMapHeader *>(stackmapAddr);
95 ASSERT(head != nullptr);
96 if (head == nullptr) {
97 return;
98 }
99 uint32_t callsiteNum = head->callsiteNum;
100
101 CallsiteHeader *callsiteHead = reinterpret_cast<CallsiteHeader *>(stackmapAddr + sizeof(ArkStackMapHeader));
102 int mid = BinaraySearch(callsiteHead, callsiteNum, callSiteAddr);
103 ASSERT(mid != -1);
104 if (mid == -1) {
105 return;
106 }
107 CallsiteHeader *found = callsiteHead + mid;
108 GetArkDeopt(stackmapAddr, *found, deopts);
109 }
110
GetStackSlotAddress(const LLVMStackMapType::DwarfRegAndOffsetType info,uintptr_t callSiteSp,uintptr_t callsiteFp) const111 uintptr_t ArkStackMapParser::GetStackSlotAddress(const LLVMStackMapType::DwarfRegAndOffsetType info,
112 uintptr_t callSiteSp,
113 uintptr_t callsiteFp) const
114 {
115 uintptr_t address = 0;
116 if (info.first == GCStackMapRegisters::SP) {
117 address = callSiteSp + info.second;
118 } else if (info.first == GCStackMapRegisters::FP) {
119 address = callsiteFp + info.second;
120 } else {
121 LOG_ECMA(FATAL) << "this branch is unreachable";
122 UNREACHABLE();
123 }
124 return address;
125 }
126
127 // this function will increase the value of 'offset'
GetStackSlotAddress(uint8_t * stackmapAddr,uintptr_t callSiteSp,uintptr_t callsiteFp,uint32_t & offset,bool & nextIsBase,size_t & regOffsetSize) const128 uintptr_t ArkStackMapParser::GetStackSlotAddress(uint8_t *stackmapAddr, uintptr_t callSiteSp, uintptr_t callsiteFp,
129 uint32_t &offset, bool &nextIsBase, size_t ®OffsetSize) const
130 {
131 LLVMStackMapType::DwarfRegType regType;
132 LLVMStackMapType::OffsetType offsetType;
133 LLVMStackMapType::SLeb128Type regOffset;
134 [[maybe_unused]] bool isFull;
135 uintptr_t address = 0;
136 std::tie(regOffset, regOffsetSize, isFull) =
137 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(stackmapAddr + offset);
138 bool isBaseDerivedEq = 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 nextIsBase = isBaseDerivedEq;
148 offset += regOffsetSize;
149
150 return address;
151 }
152
GetDeoptStackSlotAddress(uint8_t * stackmapAddr,uintptr_t callSiteSp,uintptr_t callsiteFp,uint32_t & offset) const153 uintptr_t ArkStackMapParser::GetDeoptStackSlotAddress(uint8_t *stackmapAddr,
154 uintptr_t callSiteSp,
155 uintptr_t callsiteFp,
156 uint32_t &offset) const
157 {
158 ARKDeopt deopt;
159 LLVMStackMapType::KindType kindType;
160 LLVMStackMapType::DwarfRegType reg;
161 LLVMStackMapType::OffsetType offsetType;
162 auto [vregsInfo, vregsInfoSize, InfoIsFull] =
163 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(stackmapAddr + offset);
164 LLVMStackMapType::DecodeVRegsInfo(vregsInfo, deopt.id, kindType);
165 offset += vregsInfoSize;
166 ASSERT(kindType == LLVMStackMapType::CONSTANT_TYPE || kindType == LLVMStackMapType::OFFSET_TYPE);
167 if (kindType == LLVMStackMapType::CONSTANT_TYPE) {
168 auto [constant, constantSize, constIsFull] =
169 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(stackmapAddr + offset);
170 offset += constantSize;
171 return 0;
172 } else {
173 auto [regOffset, regOffsetSize, regOffIsFull] =
174 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(stackmapAddr + offset);
175 LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
176 offset += regOffsetSize;
177 LLVMStackMapType::DwarfRegAndOffsetType info = std::make_pair(reg, offsetType);
178 return GetStackSlotAddress(info, callSiteSp, callsiteFp);
179 }
180 }
181
GetPcOffset(const std::vector<ARKDeopt> & deopts,size_t currentDepth,size_t shift) const182 int32_t ArkStackMapParser::GetPcOffset(const std::vector<ARKDeopt> &deopts, size_t currentDepth, size_t shift) const
183 {
184 ARKDeopt target;
185 LLVMStackMapType::VRegId pcId = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::PC_OFFSET_INDEX);
186 target.id = Deoptimizier::EncodeDeoptVregIndex(pcId, currentDepth, shift);
187 auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
188 [](const ARKDeopt& a, const ARKDeopt& b) {
189 return a.id < b.id;
190 });
191 if (it == deopts.end() || (it->id > target.id)) {
192 return -1;
193 }
194 ASSERT(it->kind == LocationTy::Kind::CONSTANT);
195 ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(it->value));
196 auto v = std::get<LLVMStackMapType::IntType>(it->value);
197 return static_cast<int32_t>(v);
198 }
199
GetFunction(const std::vector<ARKDeopt> & deopts,size_t currentDepth,size_t shift,uintptr_t callsiteSp,uintptr_t callsiteFp) const200 JSTaggedType ArkStackMapParser::GetFunction(const std::vector<ARKDeopt> &deopts, size_t currentDepth, size_t shift,
201 uintptr_t callsiteSp, uintptr_t callsiteFp) const
202 {
203 ARKDeopt target;
204 LLVMStackMapType::VRegId pcId = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::FUNC_INDEX);
205 target.id = Deoptimizier::EncodeDeoptVregIndex(pcId, currentDepth, shift);
206 auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
207 [](const ARKDeopt& a, const ARKDeopt& b) {
208 return a.id < b.id;
209 });
210 if (it == deopts.end() || (it->id > target.id)) {
211 return 0;
212 }
213 ASSERT(it->kind == LocationTy::Kind::INDIRECT);
214 ASSERT(std::holds_alternative<LLVMStackMapType::DwarfRegAndOffsetType>(it->value));
215 auto value = std::get<LLVMStackMapType::DwarfRegAndOffsetType>(it->value);
216 uintptr_t addr = GetStackSlotAddress(value, callsiteSp, callsiteFp);
217 JSTaggedType v = *(reinterpret_cast<JSTaggedType *>(addr));
218 return v;
219 }
220
CollectStackTraceInfos(uintptr_t callSiteAddr,std::vector<std::pair<JSTaggedType,uint32_t>> & info,uintptr_t callsiteSp,uintptr_t callsiteFp,uint8_t * stackmapAddr) const221 void ArkStackMapParser::CollectStackTraceInfos(uintptr_t callSiteAddr,
222 std::vector<std::pair<JSTaggedType, uint32_t>> &info,
223 uintptr_t callsiteSp,
224 uintptr_t callsiteFp,
225 uint8_t *stackmapAddr) const
226 {
227 std::vector<ARKDeopt> deopts;
228 GetArkDeopt(callSiteAddr, stackmapAddr, deopts);
229 if (deopts.empty()) {
230 return;
231 }
232 size_t depth = GetInlineDepth(deopts);
233 size_t shift = Deoptimizier::ComputeShift(depth);
234 for (int i = depth; i >= 0; i--) {
235 int32_t pcOffset = GetPcOffset(deopts, i, shift);
236 JSTaggedType function = GetFunction(deopts, i, shift, callsiteSp, callsiteFp);
237 if (pcOffset < 0 || function == 0) {
238 continue;
239 }
240 info.push_back(std::make_pair(function, pcOffset));
241 }
242 }
243
GetInlineDepth(uintptr_t callSiteAddr,uint8_t * stackmapAddr) const244 size_t ArkStackMapParser::GetInlineDepth(uintptr_t callSiteAddr, uint8_t *stackmapAddr) const
245 {
246 std::vector<ARKDeopt> deopts;
247 GetArkDeopt(callSiteAddr, stackmapAddr, deopts);
248 return GetInlineDepth(deopts);
249 }
250
GetInlineDepth(const std::vector<ARKDeopt> & deopts) const251 size_t ArkStackMapParser::GetInlineDepth(const std::vector<ARKDeopt> &deopts) const
252 {
253 if (deopts.empty()) {
254 return 0;
255 }
256
257 ARKDeopt target;
258 LLVMStackMapType::VRegId id = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::INLINE_DEPTH);
259 target.id = id;
260 auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
261 [](const ARKDeopt& a, const ARKDeopt& b) {
262 return a.id < b.id;
263 });
264 if (it == deopts.end() || (it->id > target.id)) {
265 LOG_ECMA(ERROR) << "Miss inline depth";
266 return 0;
267 }
268 ASSERT(it->kind == LocationTy::Kind::CONSTANT);
269 ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(it->value));
270 auto v = std::get<LLVMStackMapType::IntType>(it->value);
271 return static_cast<size_t>(v);
272 }
273
IteratorStackMap(RootVisitor & visitor,uintptr_t callsiteFp,uintptr_t callSiteSp,uint8_t * stackmapAddr,uint32_t offset,uint16_t stackmapNum,std::map<uintptr_t,uintptr_t> & baseSet) const274 void ArkStackMapParser::IteratorStackMap(RootVisitor& visitor, uintptr_t callsiteFp,
275 uintptr_t callSiteSp, uint8_t *stackmapAddr,
276 uint32_t offset, uint16_t stackmapNum,
277 std::map<uintptr_t, uintptr_t> &baseSet) const
278 {
279 ASSERT(callsiteFp != callSiteSp);
280 // Original layout: <base, base>..<base, derive>..
281 // +----------------------+ --
282 // |regNo: 6 offset: -40| |
283 // +----------------------+ | <-- base ref1 Get base address anhd derived address (equal)
284 // |regNo: 6 offset: -40| |
285 // +----------------------+ <-- When iterate, i+=2
286 // +----------------------+ --
287 // |regNo: 7 offset: -32| |
288 // +----------------------+ | <-- derived ref1 Get base address anhd derived address (different)
289 // |regNo: 7 offset: -24| |
290 // +----------------------+ <--
291 // ==========>after optimization, removed the repeated info for base reference in stackmap builder
292 // New layout: <base>..<base, derived>.., stackmap info are not in pair
293 // +----------------------+ --
294 // |regNo: 6 offset: -40| | When base ref, i+=1
295 // +----------------------+ <--
296 // +----------------------+ -- When derived ref, i+=2
297 // |regNo: 7 offset: -32| |
298 // +----------------------+ |
299 // |regNo: 7 offset: -24| |
300 // +----------------------+ <--
301 for (size_t i = 0; i < stackmapNum; i++) {
302 size_t regOffsetSize;
303 // nextIsBase decoded (updated) from DecodeRegAndOffset inside GetStackSlotAddress
304 bool nextIsBase = true;
305 uintptr_t base = GetStackSlotAddress(stackmapAddr, callSiteSp, callsiteFp, offset, nextIsBase, regOffsetSize);
306 uintptr_t derived;
307 if (nextIsBase) {
308 derived = base; // because the base=derive and the derived one have been removed.
309 } else {
310 derived = GetStackSlotAddress(stackmapAddr, callSiteSp, callsiteFp, offset, nextIsBase, regOffsetSize);
311 i++; // move to next entry
312 }
313 if (*reinterpret_cast<uintptr_t *>(base) == 0) {
314 base = derived;
315 }
316 if (*reinterpret_cast<uintptr_t *>(base) != 0) {
317 // The base address may be marked repeatedly
318 if (baseSet.find(base) == baseSet.end()) {
319 baseSet.emplace(base, *reinterpret_cast<uintptr_t *>(base));
320 visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(base));
321 }
322
323 if (base != derived) {
324 baseSet.emplace(derived, *reinterpret_cast<uintptr_t *>(derived));
325 visitor.VisitBaseAndDerivedRoot(Root::ROOT_FRAME, ObjectSlot(base), ObjectSlot(derived),
326 baseSet[base]);
327 }
328 }
329 }
330 }
331
IteratorDeopt(RootVisitor & visitor,uintptr_t callsiteFp,uintptr_t callSiteSp,uint8_t * stackmapAddr,uint32_t offset,uint16_t num,std::map<uintptr_t,uintptr_t> & baseSet) const332 void ArkStackMapParser::IteratorDeopt(RootVisitor& visitor, uintptr_t callsiteFp,
333 uintptr_t callSiteSp, uint8_t *stackmapAddr,
334 uint32_t offset, uint16_t num,
335 std::map<uintptr_t, uintptr_t> &baseSet) const
336 {
337 ASSERT(num % DEOPT_ENTRY_SIZE == 0);
338 ASSERT(callsiteFp != callSiteSp);
339 for (size_t i = 0; i < num; i += DEOPT_ENTRY_SIZE) { // DEOPT_ENTRY_SIZE=<id, value>
340 uintptr_t base = GetDeoptStackSlotAddress(stackmapAddr, callSiteSp, callsiteFp, offset);
341 if (base != 0 && *reinterpret_cast<uintptr_t *>(base) != 0) {
342 // The base address may be marked repeatedly
343 if (baseSet.find(base) == baseSet.end()) {
344 baseSet.emplace(base, *reinterpret_cast<uintptr_t *>(base));
345 visitor.VisitRoot(Root::ROOT_FRAME, ObjectSlot(base));
346 }
347 }
348 }
349 }
350
IteratorStackMapAndDeopt(RootVisitor & visitor,uintptr_t callSiteAddr,uintptr_t callsiteFp,uintptr_t callSiteSp,uint8_t * stackmapAddr) const351 bool ArkStackMapParser::IteratorStackMapAndDeopt(RootVisitor& visitor,
352 uintptr_t callSiteAddr,
353 uintptr_t callsiteFp,
354 uintptr_t callSiteSp,
355 uint8_t *stackmapAddr) const
356 {
357 ArkStackMapHeader *head = reinterpret_cast<ArkStackMapHeader *>(stackmapAddr);
358 ASSERT(head != nullptr);
359 uint32_t callsiteNum = head->callsiteNum;
360 // BuiltinsStub current only sample stub, don't have stackmap&deopt.
361 if (callsiteNum == 0) {
362 return false;
363 }
364
365 CallsiteHeader *callsiteHead = reinterpret_cast<CallsiteHeader *>(stackmapAddr + sizeof(ArkStackMapHeader));
366 int mid = BinaraySearch(callsiteHead, callsiteNum, callSiteAddr);
367 if (mid == -1) {
368 return false;
369 }
370 CallsiteHeader *found = callsiteHead + mid;
371 std::map<uintptr_t, uintptr_t> baseSet;
372
373 uint32_t offset = found->stackmapOffsetInSMSec;
374 uint16_t num = found->stackmapNum;
375 if (num == 0) {
376 ASSERT(found->deoptNum == 0);
377 return false;
378 }
379 IteratorStackMap(visitor, callsiteFp, callSiteSp, stackmapAddr, offset, num, baseSet);
380
381 // Not sure if this is necessary, but add it just to be on the safe side.
382 offset = found->deoptOffset;
383 num = found->deoptNum;
384 IteratorDeopt(visitor, callsiteFp, callSiteSp, stackmapAddr, offset, num, baseSet);
385
386 baseSet.clear();
387 return true;
388 }
389
390 #ifndef NDEBUG
ParseArkStackMap(const CallsiteHeader & callsiteHead,uint8_t * ptr,ArkStackMap & arkStackMaps) const391 void ArkStackMapParser::ParseArkStackMap(const CallsiteHeader& callsiteHead,
392 uint8_t *ptr,
393 ArkStackMap& arkStackMaps) const
394 {
395 LLVMStackMapType::DwarfRegType reg;
396 LLVMStackMapType::OffsetType offsetType;
397 uint32_t offset = callsiteHead.stackmapOffsetInSMSec;
398 uint16_t stackmapNum = callsiteHead.stackmapNum;
399 for (uint32_t j = 0; j < stackmapNum; j++) {
400 auto [regOffset, regOffsetSize, is_full] =
401 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + offset);
402 bool isBaseDerivedEq = LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
403 offset += regOffsetSize;
404 LOG_COMPILER(VERBOSE) << " reg: " << std::dec << reg << " offset:" << offsetType;
405 if (isBaseDerivedEq) {
406 LOG_COMPILER(VERBOSE) << " reg(base): " << std::dec << reg << " offset(base):" << offsetType;
407 }
408 arkStackMaps.emplace_back(std::make_pair(reg, offsetType));
409 }
410 offset = AlignUp(offset, LLVMStackMapType::STACKMAP_ALIGN_BYTES);
411 }
412
ParseArkStackMapAndDeopt(uint8_t * ptr,uint32_t length) const413 void ArkStackMapParser::ParseArkStackMapAndDeopt(uint8_t *ptr, uint32_t length) const
414 {
415 CallsiteHeader callsiteHead;
416 ArkStackMapHeader secHead;
417 BinaryBufferParser binBufparser(ptr, length);
418 binBufparser.ParseBuffer(&secHead, sizeof(ArkStackMapHeader));
419 for (uint32_t i = 0; i < secHead.callsiteNum; i++) {
420 binBufparser.ParseBuffer(&callsiteHead, sizeof(CallsiteHeader));
421 std::vector<ARKDeopt> deopts;
422 ArkStackMap arkStackMaps;
423 LOG_COMPILER(VERBOSE) << " calliteOffsetInTxtSec: 0x" << std::hex << callsiteHead.calliteOffsetInTxtSec
424 << " stackmap offset: 0x" << std::hex << callsiteHead.stackmapOffsetInSMSec
425 << " num:" << callsiteHead.stackmapNum
426 << " deopt Offset: 0x" << std::hex << callsiteHead.deoptOffset
427 << " num:" << callsiteHead.deoptNum;
428 ParseArkStackMap(callsiteHead, ptr, arkStackMaps);
429 ParseArkDeopt(callsiteHead, ptr, deopts);
430 }
431 }
432 #endif
433 } // namespace panda::ecmascript::kungfu