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