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