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