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