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 21namespace panda::ecmascript::kungfu { 22// implement simple binary-search is improve performance. if use std api, it'll trigger copy CallsiteHeader. 23int 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 43void ArkStackMapParser::GetArkDeopt(uint8_t *stackmapAddr, 44 const CallsiteHeader& callsiteHead, 45 std::vector<ARKDeopt>& deopts) const 46{ 47 ParseArkDeopt(callsiteHead, stackmapAddr, deopts); 48} 49 50void 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 70void 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 96void 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' 127uintptr_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 153bool 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 205void 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 225void 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 266void 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