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].calliteOffset;
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, 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, 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, LLVMStackMapType::ConstInfo& info,
70 uint8_t *stackmapAddr) const
71 {
72 std::vector<ARKDeopt> deopts;
73 GetArkDeopt(callSiteAddr, stackmapAddr, deopts);
74 if (deopts.empty()) {
75 return;
76 }
77
78 ARKDeopt target;
79 LLVMStackMapType::VRegId id = static_cast<LLVMStackMapType::VRegId>(SpecVregIndex::PC_OFFSET_INDEX);
80 target.id = id;
81 auto it = std::lower_bound(deopts.begin(), deopts.end(), target,
82 [](const ARKDeopt& a, const ARKDeopt& b) {
83 return a.id < b.id;
84 });
85 if (it == deopts.end() || (it->id > id)) {
86 return;
87 }
88 ASSERT(it->kind == LocationTy::Kind::CONSTANT);
89 ASSERT(std::holds_alternative<LLVMStackMapType::IntType>(it->value));
90 auto v = std::get<LLVMStackMapType::IntType>(it->value);
91 info.emplace_back(v);
92 }
93
GetStackSlotAddress(const LLVMStackMapType::DwarfRegAndOffsetType info,uintptr_t callSiteSp,uintptr_t callsiteFp) const94 uintptr_t ArkStackMapParser::GetStackSlotAddress(const LLVMStackMapType::DwarfRegAndOffsetType info,
95 uintptr_t callSiteSp, uintptr_t callsiteFp) const
96 {
97 uintptr_t address = 0;
98 if (info.first == GCStackMapRegisters::SP) {
99 address = callSiteSp + info.second;
100 } else if (info.first == GCStackMapRegisters::FP) {
101 address = callsiteFp + info.second;
102 } else {
103 LOG_ECMA(FATAL) << "this branch is unreachable";
104 UNREACHABLE();
105 }
106 return address;
107 }
108
IteratorStackMap(const RootVisitor & visitor,const RootBaseAndDerivedVisitor & derivedVisitor,uintptr_t callSiteAddr,uintptr_t callsiteFp,uintptr_t callSiteSp,uint8_t * stackmapAddr) const109 bool ArkStackMapParser::IteratorStackMap(const RootVisitor& visitor, const RootBaseAndDerivedVisitor& derivedVisitor,
110 uintptr_t callSiteAddr, uintptr_t callsiteFp, uintptr_t callSiteSp, uint8_t *stackmapAddr) const
111 {
112 ArkStackMapHeader *head = reinterpret_cast<ArkStackMapHeader *>(stackmapAddr);
113 ASSERT(head != nullptr);
114 uint32_t callsiteNum = head->callsiteNum;
115 ArkStackMap arkStackMap;
116 // BuiltinsStub current only sample stub, don't have stackmap&deopt.
117 if (callsiteNum == 0) {
118 return false;
119 }
120
121 CallsiteHeader *callsiteHead = reinterpret_cast<CallsiteHeader *>(stackmapAddr + sizeof(ArkStackMapHeader));
122 int mid = BinaraySearch(callsiteHead, callsiteNum, callSiteAddr);
123 if (mid == -1) {
124 return false;
125 }
126 CallsiteHeader *found = callsiteHead + mid;
127 ParseArkStackMap(*found, stackmapAddr, arkStackMap);
128 if (arkStackMap.size() == 0) {
129 return false;
130 }
131 ASSERT(callsiteFp != callSiteSp);
132 std::map<uintptr_t, uintptr_t> baseSet;
133 for (size_t i = 0; i < arkStackMap.size(); i += 2) { // 2:base and derive
134 const LLVMStackMapType::DwarfRegAndOffsetType baseInfo = arkStackMap.at(i);
135 const LLVMStackMapType::DwarfRegAndOffsetType derivedInfo = arkStackMap.at(i + 1);
136 uintptr_t base = GetStackSlotAddress(baseInfo, callSiteSp, callsiteFp);
137 uintptr_t derived = GetStackSlotAddress(derivedInfo, callSiteSp, callsiteFp);
138 if (*reinterpret_cast<uintptr_t *>(base) == 0) {
139 base = derived;
140 }
141 if (*reinterpret_cast<uintptr_t *>(base) != 0) {
142 // The base address may be marked repeatedly
143 if (baseSet.find(base) == baseSet.end()) {
144 baseSet.emplace(base, *reinterpret_cast<uintptr_t *>(base));
145 visitor(Root::ROOT_FRAME, ObjectSlot(base));
146 }
147
148 if (base != derived) {
149 derivedVisitor(Root::ROOT_FRAME, ObjectSlot(base), ObjectSlot(derived), baseSet[base]);
150 }
151 }
152 }
153 baseSet.clear();
154 return true;
155 }
156
ParseArkStackMap(const CallsiteHeader & callsiteHead,uint8_t * ptr,ArkStackMap & arkStackMaps) const157 void ArkStackMapParser::ParseArkStackMap(const CallsiteHeader& callsiteHead, uint8_t *ptr,
158 ArkStackMap& arkStackMaps) const
159 {
160 LLVMStackMapType::DwarfRegType reg;
161 LLVMStackMapType::OffsetType offsetType;
162 uint32_t offset = callsiteHead.stackmapOffset;
163 uint16_t stackmapNum = callsiteHead.stackmapNum;
164 ASSERT(stackmapNum % 2 == 0); // 2:base and derive
165 for (uint32_t j = 0; j < stackmapNum; j++) {
166 auto [regOffset, regOffsetSize, is_full] =
167 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + offset);
168 LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
169 offset += regOffsetSize;
170 LOG_COMPILER(VERBOSE) << " reg: " << std::dec << reg << " offset:" << offsetType;
171 arkStackMaps.emplace_back(std::make_pair(reg, offsetType));
172 }
173 offset = AlignUp(offset, LLVMStackMapType::STACKMAP_ALIGN_BYTES);
174 }
175
ParseArkDeopt(const CallsiteHeader & callsiteHead,uint8_t * ptr,std::vector<ARKDeopt> & deopts) const176 void ArkStackMapParser::ParseArkDeopt(const CallsiteHeader& callsiteHead, uint8_t *ptr,
177 std::vector<ARKDeopt> &deopts) const
178 {
179 ARKDeopt deopt;
180 uint32_t deoptOffset = callsiteHead.deoptOffset;
181 uint16_t deoptNum = callsiteHead.deoptNum;
182 LLVMStackMapType::KindType kindType;
183 LLVMStackMapType::DwarfRegType reg;
184 LLVMStackMapType::OffsetType offsetType;
185 ASSERT(deoptNum % 2 == 0); // 2:<id, value>
186 for (uint32_t j = 0; j < deoptNum; j += 2) { // 2:<id, value>
187 auto [vregsInfo, vregsInfoSize, InfoIsFull] =
188 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
189 LLVMStackMapType::DecodeVRegsInfo(vregsInfo, deopt.id, kindType);
190 deoptOffset += vregsInfoSize;
191 ASSERT(kindType == LLVMStackMapType::CONSTANT_TYPE || kindType == LLVMStackMapType::OFFSET_TYPE);
192 if (kindType == LLVMStackMapType::CONSTANT_TYPE) {
193 auto [constant, constantSize, constIsFull] =
194 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
195 if (constant > INT32_MAX || constant < INT32_MIN) {
196 deopt.kind = LocationTy::Kind::CONSTANTNDEX;
197 deopt.value = static_cast<LLVMStackMapType::LargeInt>(constant);
198 } else {
199 deopt.kind = LocationTy::Kind::CONSTANT;
200 deopt.value = static_cast<LLVMStackMapType::IntType>(constant);
201 }
202 deoptOffset += constantSize;
203 } else {
204 auto [regOffset, regOffsetSize, regOffIsFull] =
205 panda::leb128::DecodeSigned<LLVMStackMapType::SLeb128Type>(ptr + deoptOffset);
206 LLVMStackMapType::DecodeRegAndOffset(regOffset, reg, offsetType);
207 deopt.kind = LocationTy::Kind::INDIRECT;
208 deopt.value = std::make_pair(reg, offsetType);
209 deoptOffset += regOffsetSize;
210 }
211 deopts.emplace_back(deopt);
212 }
213 }
214
ParseArkStackMapAndDeopt(uint8_t * ptr,uint32_t length) const215 void ArkStackMapParser::ParseArkStackMapAndDeopt(uint8_t *ptr, uint32_t length) const
216 {
217 CallsiteHeader callsiteHead;
218 ArkStackMapHeader secHead;
219 BinaryBufferParser binBufparser(ptr, length);
220 binBufparser.ParseBuffer(&secHead, sizeof(ArkStackMapHeader));
221 for (uint32_t i = 0; i < secHead.callsiteNum; i++) {
222 binBufparser.ParseBuffer(&callsiteHead, sizeof(CallsiteHeader));
223 std::vector<ARKDeopt> deopts;
224 ArkStackMap arkStackMaps;
225 LOG_COMPILER(VERBOSE) << " calliteOffset:0x" << std::hex << callsiteHead.calliteOffset
226 << " stackmap offset:0x" << std::hex << callsiteHead.stackmapOffset
227 << " num:" << callsiteHead.stackmapNum
228 << " deopt Offset:0x" << std::hex << callsiteHead.deoptOffset
229 << " num:" << callsiteHead.deoptNum;
230 ParseArkStackMap(callsiteHead, ptr, arkStackMaps);
231 ParseArkDeopt(callsiteHead, ptr, deopts);
232 }
233 }
234 } // namespace panda::ecmascript::kungfu