1 //===- StackMapParser.h - StackMap Parsing Support --------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CODEGEN_STACKMAPPARSER_H 10 #define LLVM_CODEGEN_STACKMAPPARSER_H 11 12 #include "llvm/ADT/ArrayRef.h" 13 #include "llvm/ADT/iterator_range.h" 14 #include "llvm/Support/Endian.h" 15 #include <cassert> 16 #include <cstddef> 17 #include <cstdint> 18 #include <vector> 19 20 namespace llvm { 21 22 /// A parser for the latest stackmap format. At the moment, latest=V3. 23 template <support::endianness Endianness> 24 class StackMapParser { 25 public: 26 template <typename AccessorT> 27 class AccessorIterator { 28 public: AccessorIterator(AccessorT A)29 AccessorIterator(AccessorT A) : A(A) {} 30 31 AccessorIterator& operator++() { A = A.next(); return *this; } 32 AccessorIterator operator++(int) { 33 auto tmp = *this; 34 ++*this; 35 return tmp; 36 } 37 38 bool operator==(const AccessorIterator &Other) { 39 return A.P == Other.A.P; 40 } 41 42 bool operator!=(const AccessorIterator &Other) { return !(*this == Other); } 43 44 AccessorT& operator*() { return A; } 45 AccessorT* operator->() { return &A; } 46 47 private: 48 AccessorT A; 49 }; 50 51 /// Accessor for function records. 52 class FunctionAccessor { 53 friend class StackMapParser; 54 55 public: 56 /// Get the function address. getFunctionAddress()57 uint64_t getFunctionAddress() const { 58 return read<uint64_t>(P); 59 } 60 61 /// Get the function's stack size. getStackSize()62 uint64_t getStackSize() const { 63 return read<uint64_t>(P + sizeof(uint64_t)); 64 } 65 66 /// Get the number of callsite records. getRecordCount()67 uint64_t getRecordCount() const { 68 return read<uint64_t>(P + (2 * sizeof(uint64_t))); 69 } 70 71 private: FunctionAccessor(const uint8_t * P)72 FunctionAccessor(const uint8_t *P) : P(P) {} 73 74 const static int FunctionAccessorSize = 3 * sizeof(uint64_t); 75 next()76 FunctionAccessor next() const { 77 return FunctionAccessor(P + FunctionAccessorSize); 78 } 79 80 const uint8_t *P; 81 }; 82 83 /// Accessor for constants. 84 class ConstantAccessor { 85 friend class StackMapParser; 86 87 public: 88 /// Return the value of this constant. getValue()89 uint64_t getValue() const { return read<uint64_t>(P); } 90 91 private: ConstantAccessor(const uint8_t * P)92 ConstantAccessor(const uint8_t *P) : P(P) {} 93 94 const static int ConstantAccessorSize = sizeof(uint64_t); 95 next()96 ConstantAccessor next() const { 97 return ConstantAccessor(P + ConstantAccessorSize); 98 } 99 100 const uint8_t *P; 101 }; 102 103 enum class LocationKind : uint8_t { 104 Register = 1, Direct = 2, Indirect = 3, Constant = 4, ConstantIndex = 5 105 }; 106 107 /// Accessor for location records. 108 class LocationAccessor { 109 friend class StackMapParser; 110 friend class RecordAccessor; 111 112 public: 113 /// Get the Kind for this location. getKind()114 LocationKind getKind() const { 115 return LocationKind(P[KindOffset]); 116 } 117 118 /// Get the Size for this location. getSizeInBytes()119 unsigned getSizeInBytes() const { 120 return read<uint16_t>(P + SizeOffset); 121 122 } 123 124 /// Get the Dwarf register number for this location. getDwarfRegNum()125 uint16_t getDwarfRegNum() const { 126 return read<uint16_t>(P + DwarfRegNumOffset); 127 } 128 129 /// Get the small-constant for this location. (Kind must be Constant). getSmallConstant()130 uint32_t getSmallConstant() const { 131 assert(getKind() == LocationKind::Constant && "Not a small constant."); 132 return read<uint32_t>(P + SmallConstantOffset); 133 } 134 135 /// Get the constant-index for this location. (Kind must be ConstantIndex). getConstantIndex()136 uint32_t getConstantIndex() const { 137 assert(getKind() == LocationKind::ConstantIndex && 138 "Not a constant-index."); 139 return read<uint32_t>(P + SmallConstantOffset); 140 } 141 142 /// Get the offset for this location. (Kind must be Direct or Indirect). getOffset()143 int32_t getOffset() const { 144 assert((getKind() == LocationKind::Direct || 145 getKind() == LocationKind::Indirect) && 146 "Not direct or indirect."); 147 return read<int32_t>(P + SmallConstantOffset); 148 } 149 150 private: LocationAccessor(const uint8_t * P)151 LocationAccessor(const uint8_t *P) : P(P) {} 152 next()153 LocationAccessor next() const { 154 return LocationAccessor(P + LocationAccessorSize); 155 } 156 157 static const int KindOffset = 0; 158 static const int SizeOffset = KindOffset + sizeof(uint16_t); 159 static const int DwarfRegNumOffset = SizeOffset + sizeof(uint16_t); 160 static const int SmallConstantOffset = DwarfRegNumOffset + sizeof(uint32_t); 161 static const int LocationAccessorSize = sizeof(uint64_t) + sizeof(uint32_t); 162 163 const uint8_t *P; 164 }; 165 166 /// Accessor for stackmap live-out fields. 167 class LiveOutAccessor { 168 friend class StackMapParser; 169 friend class RecordAccessor; 170 171 public: 172 /// Get the Dwarf register number for this live-out. getDwarfRegNum()173 uint16_t getDwarfRegNum() const { 174 return read<uint16_t>(P + DwarfRegNumOffset); 175 } 176 177 /// Get the size in bytes of live [sub]register. getSizeInBytes()178 unsigned getSizeInBytes() const { 179 return read<uint8_t>(P + SizeOffset); 180 } 181 182 private: LiveOutAccessor(const uint8_t * P)183 LiveOutAccessor(const uint8_t *P) : P(P) {} 184 next()185 LiveOutAccessor next() const { 186 return LiveOutAccessor(P + LiveOutAccessorSize); 187 } 188 189 static const int DwarfRegNumOffset = 0; 190 static const int SizeOffset = 191 DwarfRegNumOffset + sizeof(uint16_t) + sizeof(uint8_t); 192 static const int LiveOutAccessorSize = sizeof(uint32_t); 193 194 const uint8_t *P; 195 }; 196 197 /// Accessor for stackmap records. 198 class RecordAccessor { 199 friend class StackMapParser; 200 201 public: 202 using location_iterator = AccessorIterator<LocationAccessor>; 203 using liveout_iterator = AccessorIterator<LiveOutAccessor>; 204 205 /// Get the patchpoint/stackmap ID for this record. getID()206 uint64_t getID() const { 207 return read<uint64_t>(P + PatchpointIDOffset); 208 } 209 210 /// Get the instruction offset (from the start of the containing function) 211 /// for this record. getInstructionOffset()212 uint32_t getInstructionOffset() const { 213 return read<uint32_t>(P + InstructionOffsetOffset); 214 } 215 216 /// Get the number of locations contained in this record. getNumLocations()217 uint16_t getNumLocations() const { 218 return read<uint16_t>(P + NumLocationsOffset); 219 } 220 221 /// Get the location with the given index. getLocation(unsigned LocationIndex)222 LocationAccessor getLocation(unsigned LocationIndex) const { 223 unsigned LocationOffset = 224 LocationListOffset + LocationIndex * LocationSize; 225 return LocationAccessor(P + LocationOffset); 226 } 227 228 /// Begin iterator for locations. location_begin()229 location_iterator location_begin() const { 230 return location_iterator(getLocation(0)); 231 } 232 233 /// End iterator for locations. location_end()234 location_iterator location_end() const { 235 return location_iterator(getLocation(getNumLocations())); 236 } 237 238 /// Iterator range for locations. locations()239 iterator_range<location_iterator> locations() const { 240 return make_range(location_begin(), location_end()); 241 } 242 243 /// Get the number of liveouts contained in this record. getNumLiveOuts()244 uint16_t getNumLiveOuts() const { 245 return read<uint16_t>(P + getNumLiveOutsOffset()); 246 } 247 248 /// Get the live-out with the given index. getLiveOut(unsigned LiveOutIndex)249 LiveOutAccessor getLiveOut(unsigned LiveOutIndex) const { 250 unsigned LiveOutOffset = 251 getNumLiveOutsOffset() + sizeof(uint16_t) + LiveOutIndex * LiveOutSize; 252 return LiveOutAccessor(P + LiveOutOffset); 253 } 254 255 /// Begin iterator for live-outs. liveouts_begin()256 liveout_iterator liveouts_begin() const { 257 return liveout_iterator(getLiveOut(0)); 258 } 259 260 /// End iterator for live-outs. liveouts_end()261 liveout_iterator liveouts_end() const { 262 return liveout_iterator(getLiveOut(getNumLiveOuts())); 263 } 264 265 /// Iterator range for live-outs. liveouts()266 iterator_range<liveout_iterator> liveouts() const { 267 return make_range(liveouts_begin(), liveouts_end()); 268 } 269 270 private: RecordAccessor(const uint8_t * P)271 RecordAccessor(const uint8_t *P) : P(P) {} 272 getNumLiveOutsOffset()273 unsigned getNumLiveOutsOffset() const { 274 unsigned LocOffset = 275 ((LocationListOffset + LocationSize * getNumLocations()) + 7) & ~0x7; 276 return LocOffset + sizeof(uint16_t); 277 } 278 getSizeInBytes()279 unsigned getSizeInBytes() const { 280 unsigned RecordSize = 281 getNumLiveOutsOffset() + sizeof(uint16_t) + getNumLiveOuts() * LiveOutSize; 282 return (RecordSize + 7) & ~0x7; 283 } 284 next()285 RecordAccessor next() const { 286 return RecordAccessor(P + getSizeInBytes()); 287 } 288 289 static const unsigned PatchpointIDOffset = 0; 290 static const unsigned InstructionOffsetOffset = 291 PatchpointIDOffset + sizeof(uint64_t); 292 static const unsigned NumLocationsOffset = 293 InstructionOffsetOffset + sizeof(uint32_t) + sizeof(uint16_t); 294 static const unsigned LocationListOffset = 295 NumLocationsOffset + sizeof(uint16_t); 296 static const unsigned LocationSize = sizeof(uint64_t) + sizeof(uint32_t); 297 static const unsigned LiveOutSize = sizeof(uint32_t); 298 299 const uint8_t *P; 300 }; 301 302 /// Construct a parser for a version-3 stackmap. StackMap data will be read 303 /// from the given array. StackMapParser(ArrayRef<uint8_t> StackMapSection)304 StackMapParser(ArrayRef<uint8_t> StackMapSection) 305 : StackMapSection(StackMapSection) { 306 ConstantsListOffset = FunctionListOffset + getNumFunctions() * FunctionSize; 307 308 assert(StackMapSection[0] == 3 && 309 "StackMapParser can only parse version 3 stackmaps"); 310 311 unsigned CurrentRecordOffset = 312 ConstantsListOffset + getNumConstants() * ConstantSize; 313 314 for (unsigned I = 0, E = getNumRecords(); I != E; ++I) { 315 StackMapRecordOffsets.push_back(CurrentRecordOffset); 316 CurrentRecordOffset += 317 RecordAccessor(&StackMapSection[CurrentRecordOffset]).getSizeInBytes(); 318 } 319 } 320 321 using function_iterator = AccessorIterator<FunctionAccessor>; 322 using constant_iterator = AccessorIterator<ConstantAccessor>; 323 using record_iterator = AccessorIterator<RecordAccessor>; 324 325 /// Get the version number of this stackmap. (Always returns 3). getVersion()326 unsigned getVersion() const { return 3; } 327 328 /// Get the number of functions in the stack map. getNumFunctions()329 uint32_t getNumFunctions() const { 330 return read<uint32_t>(&StackMapSection[NumFunctionsOffset]); 331 } 332 333 /// Get the number of large constants in the stack map. getNumConstants()334 uint32_t getNumConstants() const { 335 return read<uint32_t>(&StackMapSection[NumConstantsOffset]); 336 } 337 338 /// Get the number of stackmap records in the stackmap. getNumRecords()339 uint32_t getNumRecords() const { 340 return read<uint32_t>(&StackMapSection[NumRecordsOffset]); 341 } 342 343 /// Return an FunctionAccessor for the given function index. getFunction(unsigned FunctionIndex)344 FunctionAccessor getFunction(unsigned FunctionIndex) const { 345 return FunctionAccessor(StackMapSection.data() + 346 getFunctionOffset(FunctionIndex)); 347 } 348 349 /// Begin iterator for functions. functions_begin()350 function_iterator functions_begin() const { 351 return function_iterator(getFunction(0)); 352 } 353 354 /// End iterator for functions. functions_end()355 function_iterator functions_end() const { 356 return function_iterator( 357 FunctionAccessor(StackMapSection.data() + 358 getFunctionOffset(getNumFunctions()))); 359 } 360 361 /// Iterator range for functions. functions()362 iterator_range<function_iterator> functions() const { 363 return make_range(functions_begin(), functions_end()); 364 } 365 366 /// Return the large constant at the given index. getConstant(unsigned ConstantIndex)367 ConstantAccessor getConstant(unsigned ConstantIndex) const { 368 return ConstantAccessor(StackMapSection.data() + 369 getConstantOffset(ConstantIndex)); 370 } 371 372 /// Begin iterator for constants. constants_begin()373 constant_iterator constants_begin() const { 374 return constant_iterator(getConstant(0)); 375 } 376 377 /// End iterator for constants. constants_end()378 constant_iterator constants_end() const { 379 return constant_iterator( 380 ConstantAccessor(StackMapSection.data() + 381 getConstantOffset(getNumConstants()))); 382 } 383 384 /// Iterator range for constants. constants()385 iterator_range<constant_iterator> constants() const { 386 return make_range(constants_begin(), constants_end()); 387 } 388 389 /// Return a RecordAccessor for the given record index. getRecord(unsigned RecordIndex)390 RecordAccessor getRecord(unsigned RecordIndex) const { 391 std::size_t RecordOffset = StackMapRecordOffsets[RecordIndex]; 392 return RecordAccessor(StackMapSection.data() + RecordOffset); 393 } 394 395 /// Begin iterator for records. records_begin()396 record_iterator records_begin() const { 397 if (getNumRecords() == 0) 398 return record_iterator(RecordAccessor(nullptr)); 399 return record_iterator(getRecord(0)); 400 } 401 402 /// End iterator for records. records_end()403 record_iterator records_end() const { 404 // Records need to be handled specially, since we cache the start addresses 405 // for them: We can't just compute the 1-past-the-end address, we have to 406 // look at the last record and use the 'next' method. 407 if (getNumRecords() == 0) 408 return record_iterator(RecordAccessor(nullptr)); 409 return record_iterator(getRecord(getNumRecords() - 1).next()); 410 } 411 412 /// Iterator range for records. records()413 iterator_range<record_iterator> records() const { 414 return make_range(records_begin(), records_end()); 415 } 416 417 private: 418 template <typename T> read(const uint8_t * P)419 static T read(const uint8_t *P) { 420 return support::endian::read<T, Endianness, 1>(P); 421 } 422 423 static const unsigned HeaderOffset = 0; 424 static const unsigned NumFunctionsOffset = HeaderOffset + sizeof(uint32_t); 425 static const unsigned NumConstantsOffset = NumFunctionsOffset + sizeof(uint32_t); 426 static const unsigned NumRecordsOffset = NumConstantsOffset + sizeof(uint32_t); 427 static const unsigned FunctionListOffset = NumRecordsOffset + sizeof(uint32_t); 428 429 static const unsigned FunctionSize = 3 * sizeof(uint64_t); 430 static const unsigned ConstantSize = sizeof(uint64_t); 431 getFunctionOffset(unsigned FunctionIndex)432 std::size_t getFunctionOffset(unsigned FunctionIndex) const { 433 return FunctionListOffset + FunctionIndex * FunctionSize; 434 } 435 getConstantOffset(unsigned ConstantIndex)436 std::size_t getConstantOffset(unsigned ConstantIndex) const { 437 return ConstantsListOffset + ConstantIndex * ConstantSize; 438 } 439 440 ArrayRef<uint8_t> StackMapSection; 441 unsigned ConstantsListOffset; 442 std::vector<unsigned> StackMapRecordOffsets; 443 }; 444 445 } // end namespace llvm 446 447 #endif // LLVM_CODEGEN_STACKMAPPARSER_H 448