| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "stack_map_parser.h" |
| |
| namespace stackmap { |
| |
| FrameRoots StackmapV3Parser::ParseFrame() { |
| std::vector<DWARF> reg_roots; |
| std::vector<RBPOffset> stack_roots; |
| |
| auto* loc = |
| ptr_offset<const StkMapLocation*>(cur_frame_, sizeof(StkMapRecordHeader)); |
| |
| // The first few locations are reserved constants and not of interest to us. |
| // We skip over them but assert that they are indeed constants (else |
| // something has gone very wrong!). |
| for (int i = 0; i < kSkipLocs; i++) { |
| assert(loc->kind == kConstant); |
| loc++; |
| } |
| |
| // Deopt locations are not of interest to us either, but the first one |
| // describes how many will follow, so we need it to jump over the rest in |
| // order to get to the recorded gc root locations. |
| int num_deopts = loc->offset; |
| loc += num_deopts + 1; |
| |
| int gc_locs = (cur_frame_->num_locations - (num_deopts + 1) - kSkipLocs); |
| |
| // Locations come in pairs of a base pointer followed by a derived pointer. |
| // At the moment we assume derived pointers are the same as base pointers so |
| // we skip over them. |
| for (uint16_t i = 0; i < gc_locs; i += 2) { |
| switch (loc->kind) { |
| case kRegister: |
| reg_roots.push_back(loc->reg_num); |
| break; |
| case kIndirect: |
| stack_roots.push_back(loc->offset); |
| break; |
| default: |
| // Ignore |
| break; |
| } |
| loc += 2; |
| } |
| |
| // The liveouts part of the stack map record is not of interest to us. |
| // However, it is dynamically sized, so we need to work out many records |
| // exist so that we can effectively jump over them. |
| int incr = sizeof(StkMapHeader) + |
| (cur_frame_->num_locations * sizeof(StkMapLocation)); |
| auto* liveouts = align_8(ptr_offset<const LiveOutsHeader*>(cur_frame_, incr)); |
| incr = sizeof(LiveOutsHeader) + (liveouts->num_liveouts * sizeof(LiveOut)); |
| |
| // LLVM V3 stackmap format requires padding here if we need to align to an 8 |
| // byte boundary. |
| cur_frame_ = align_8(ptr_offset<const StkMapRecordHeader*>(liveouts, incr)); |
| return FrameRoots(reg_roots, stack_roots); |
| } |
| |
| SafepointTable StackmapV3Parser::Parse() { |
| auto* header = reinterpret_cast<const StkMapHeader*>(cursor_); |
| |
| assert(header->version == kStackmapVersion && |
| "Stackmap Parser is incorrect version"); |
| |
| // Work out the the offset needed to get to the first stack map frame record |
| // entry (i.e. call site). This needs to jump over the dynamically sized |
| // function and constant table. |
| uint32_t size_consts = header->num_constants * kSizeConstantEntry; |
| uint32_t size_fns = header->num_functions * sizeof(StkSizeRecord); |
| uint32_t rec_offset = sizeof(StkMapHeader) + size_consts + size_fns; |
| cur_frame_ = ptr_offset<const StkMapRecordHeader*>(cursor_, rec_offset); |
| |
| // For each function in the stack map, we iterate over the stack map record |
| // list looking for its respective callsite, adding its entry to the table. |
| auto* fn = ptr_offset<const StkSizeRecord*>(cursor_, sizeof(StkMapHeader)); |
| std::map<ReturnAddress, FrameRoots> roots; |
| for (uint32_t i = 0; i < header->num_functions; i++) { |
| for (uint32_t j = 0; j < fn->record_count; j++) { |
| ReturnAddress key = fn->address + cur_frame_->return_addr; |
| auto frame_roots = ParseFrame(); |
| if (!frame_roots.empty()) |
| roots.insert({key, frame_roots}); |
| } |
| fn++; |
| } |
| |
| return SafepointTable(roots); |
| } |
| } // namespace stackmap |
| |
| SafepointTable GenSafepointTable() { |
| auto parser = stackmap::StackmapV3Parser(); |
| return parser.Parse(); |
| } |