blob: a73c8d77f3fd7d0bef70b85882ce30054d31ff44 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
#define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
#include "base/value_object.h"
#include "memory_region.h"
#include "nodes.h"
#include "stack_map.h"
#include "utils/growable_array.h"
namespace art {
// Helper to build art::StackMapStream::LocationCatalogEntriesIndices.
class LocationCatalogEntriesIndicesEmptyFn {
public:
void MakeEmpty(std::pair<DexRegisterLocation, size_t>& item) const {
item.first = DexRegisterLocation::None();
}
bool IsEmpty(const std::pair<DexRegisterLocation, size_t>& item) const {
return item.first == DexRegisterLocation::None();
}
};
// Hash function for art::StackMapStream::LocationCatalogEntriesIndices.
// This hash function does not create collisions.
class DexRegisterLocationHashFn {
public:
size_t operator()(DexRegisterLocation key) const {
// Concatenate `key`s fields to create a 64-bit value to be hashed.
int64_t kind_and_value =
(static_cast<int64_t>(key.kind_) << 32) | static_cast<int64_t>(key.value_);
return inner_hash_fn_(kind_and_value);
}
private:
std::hash<int64_t> inner_hash_fn_;
};
/**
* Collects and builds stack maps for a method. All the stack maps
* for a method are placed in a CodeInfo object.
*/
class StackMapStream : public ValueObject {
public:
explicit StackMapStream(ArenaAllocator* allocator)
: allocator_(allocator),
stack_maps_(allocator, 10),
location_catalog_entries_(allocator, 4),
dex_register_locations_(allocator, 10 * 4),
inline_infos_(allocator, 2),
stack_mask_max_(-1),
dex_pc_max_(0),
native_pc_offset_max_(0),
register_mask_max_(0),
number_of_stack_maps_with_inline_info_(0),
dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {}
// Compute bytes needed to encode a mask with the given maximum element.
static uint32_t StackMaskEncodingSize(int max_element) {
int number_of_bits = max_element + 1; // Need room for max element too.
return RoundUp(number_of_bits, kBitsPerByte) / kBitsPerByte;
}
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
uint32_t dex_pc;
uint32_t native_pc_offset;
uint32_t register_mask;
BitVector* sp_mask;
uint32_t num_dex_registers;
uint8_t inlining_depth;
size_t dex_register_locations_start_index;
size_t inline_infos_start_index;
BitVector* live_dex_registers_mask;
uint32_t dex_register_map_hash;
};
struct InlineInfoEntry {
uint32_t method_index;
};
void AddStackMapEntry(uint32_t dex_pc,
uint32_t native_pc_offset,
uint32_t register_mask,
BitVector* sp_mask,
uint32_t num_dex_registers,
uint8_t inlining_depth) {
StackMapEntry entry;
entry.dex_pc = dex_pc;
entry.native_pc_offset = native_pc_offset;
entry.register_mask = register_mask;
entry.sp_mask = sp_mask;
entry.num_dex_registers = num_dex_registers;
entry.inlining_depth = inlining_depth;
entry.dex_register_locations_start_index = dex_register_locations_.Size();
entry.inline_infos_start_index = inline_infos_.Size();
entry.dex_register_map_hash = 0;
if (num_dex_registers != 0) {
entry.live_dex_registers_mask =
new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true);
} else {
entry.live_dex_registers_mask = nullptr;
}
stack_maps_.Add(entry);
if (sp_mask != nullptr) {
stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
}
if (inlining_depth > 0) {
number_of_stack_maps_with_inline_info_++;
}
dex_pc_max_ = std::max(dex_pc_max_, dex_pc);
native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset);
register_mask_max_ = std::max(register_mask_max_, register_mask);
}
void AddInlineInfoEntry(uint32_t method_index) {
InlineInfoEntry entry;
entry.method_index = method_index;
inline_infos_.Add(entry);
}
size_t ComputeNeededSize() {
size_t size = CodeInfo::kFixedSize
+ ComputeDexRegisterLocationCatalogSize()
+ ComputeStackMapsSize()
+ ComputeDexRegisterMapsSize()
+ ComputeInlineInfoSize();
// Note: use RoundUp to word-size here if you want CodeInfo objects to be word aligned.
return size;
}
size_t ComputeStackMaskSize() const {
return StackMaskEncodingSize(stack_mask_max_);
}
size_t ComputeStackMapsSize() {
return stack_maps_.Size() * StackMap::ComputeStackMapSize(
ComputeStackMaskSize(),
ComputeInlineInfoSize(),
ComputeDexRegisterMapsSize(),
dex_pc_max_,
native_pc_offset_max_,
register_mask_max_);
}
// Compute the size of the Dex register location catalog of `entry`.
size_t ComputeDexRegisterLocationCatalogSize() const {
size_t size = DexRegisterLocationCatalog::kFixedSize;
for (size_t location_catalog_entry_index = 0;
location_catalog_entry_index < location_catalog_entries_.Size();
++location_catalog_entry_index) {
DexRegisterLocation dex_register_location =
location_catalog_entries_.Get(location_catalog_entry_index);
size += DexRegisterLocationCatalog::EntrySize(dex_register_location);
}
return size;
}
size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const {
// Size of the map in bytes.
size_t size = DexRegisterMap::kFixedSize;
// Add the live bit mask for the Dex register liveness.
size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers);
// Compute the size of the set of live Dex register entries.
size_t number_of_live_dex_registers = 0;
for (size_t dex_register_number = 0;
dex_register_number < entry.num_dex_registers;
++dex_register_number) {
if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
++number_of_live_dex_registers;
}
}
size_t map_entries_size_in_bits =
DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size())
* number_of_live_dex_registers;
size_t map_entries_size_in_bytes =
RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte;
size += map_entries_size_in_bytes;
return size;
}
// Compute the size of all the Dex register maps.
size_t ComputeDexRegisterMapsSize() {
size_t size = 0;
for (size_t i = 0; i < stack_maps_.Size(); ++i) {
if (FindEntryWithTheSameDexMap(i) == kNoSameDexMapFound) {
// Entries with the same dex map will have the same offset.
size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
}
}
return size;
}
// Compute the size of all the inline information pieces.
size_t ComputeInlineInfoSize() const {
return inline_infos_.Size() * InlineInfo::SingleEntrySize()
// For encoding the depth.
+ (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
}
size_t ComputeDexRegisterLocationCatalogStart() const {
return CodeInfo::kFixedSize;
}
size_t ComputeStackMapsStart() const {
return ComputeDexRegisterLocationCatalogStart() + ComputeDexRegisterLocationCatalogSize();
}
size_t ComputeDexRegisterMapsStart() {
return ComputeStackMapsStart() + ComputeStackMapsSize();
}
size_t ComputeInlineInfoStart() {
return ComputeDexRegisterMapsStart() + ComputeDexRegisterMapsSize();
}
void FillIn(MemoryRegion region) {
CodeInfo code_info(region);
DCHECK_EQ(region.size(), ComputeNeededSize());
code_info.SetOverallSize(region.size());
size_t stack_mask_size = ComputeStackMaskSize();
size_t dex_register_map_size = ComputeDexRegisterMapsSize();
size_t inline_info_size = ComputeInlineInfoSize();
MemoryRegion dex_register_locations_region = region.Subregion(
ComputeDexRegisterMapsStart(),
dex_register_map_size);
MemoryRegion inline_infos_region = region.Subregion(
ComputeInlineInfoStart(),
inline_info_size);
code_info.SetEncoding(inline_info_size,
dex_register_map_size,
dex_pc_max_,
native_pc_offset_max_,
register_mask_max_);
code_info.SetNumberOfStackMaps(stack_maps_.Size());
code_info.SetStackMaskSize(stack_mask_size);
DCHECK_EQ(code_info.GetStackMapsSize(), ComputeStackMapsSize());
// Set the Dex register location catalog.
code_info.SetNumberOfDexRegisterLocationCatalogEntries(
location_catalog_entries_.Size());
MemoryRegion dex_register_location_catalog_region = region.Subregion(
ComputeDexRegisterLocationCatalogStart(),
ComputeDexRegisterLocationCatalogSize());
DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region);
// Offset in `dex_register_location_catalog` where to store the next
// register location.
size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize;
for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) {
DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i);
dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location);
location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location);
}
// Ensure we reached the end of the Dex registers location_catalog.
DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size());
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_offset = 0;
for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
StackMap stack_map = code_info.GetStackMapAt(i);
StackMapEntry entry = stack_maps_.Get(i);
stack_map.SetDexPc(code_info, entry.dex_pc);
stack_map.SetNativePcOffset(code_info, entry.native_pc_offset);
stack_map.SetRegisterMask(code_info, entry.register_mask);
if (entry.sp_mask != nullptr) {
stack_map.SetStackMask(code_info, *entry.sp_mask);
}
if (entry.num_dex_registers == 0) {
// No dex map available.
stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
} else {
// Search for an entry with the same dex map.
size_t entry_with_same_map = FindEntryWithTheSameDexMap(i);
if (entry_with_same_map != kNoSameDexMapFound) {
// If we have a hit reuse the offset.
stack_map.SetDexRegisterMapOffset(code_info,
code_info.GetStackMapAt(entry_with_same_map).GetDexRegisterMapOffset(code_info));
} else {
// New dex registers maps should be added to the stack map.
MemoryRegion register_region =
dex_register_locations_region.Subregion(
next_dex_register_map_offset,
ComputeDexRegisterMapSize(entry));
next_dex_register_map_offset += register_region.size();
DexRegisterMap dex_register_map(register_region);
stack_map.SetDexRegisterMapOffset(
code_info, register_region.start() - dex_register_locations_region.start());
// Set the live bit mask.
dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask);
// Set the dex register location mapping data.
for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
dex_register_number < entry.num_dex_registers;
++dex_register_number) {
if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
size_t location_catalog_entry_index =
dex_register_locations_.Get(entry.dex_register_locations_start_index
+ index_in_dex_register_locations);
dex_register_map.SetLocationCatalogEntryIndex(
index_in_dex_register_locations,
location_catalog_entry_index,
entry.num_dex_registers,
location_catalog_entries_.Size());
++index_in_dex_register_locations;
}
}
}
}
// Set the inlining info.
if (entry.inlining_depth != 0) {
MemoryRegion inline_region = inline_infos_region.Subregion(
next_inline_info_offset,
InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize());
next_inline_info_offset += inline_region.size();
InlineInfo inline_info(inline_region);
// Currently relative to the dex register map.
stack_map.SetInlineDescriptorOffset(
code_info, inline_region.start() - dex_register_locations_region.start());
inline_info.SetDepth(entry.inlining_depth);
for (size_t j = 0; j < entry.inlining_depth; ++j) {
InlineInfoEntry inline_entry = inline_infos_.Get(j + entry.inline_infos_start_index);
inline_info.SetMethodReferenceIndexAtDepth(j, inline_entry.method_index);
}
} else {
if (inline_info_size != 0) {
stack_map.SetInlineDescriptorOffset(code_info, StackMap::kNoInlineInfo);
}
}
}
}
void AddDexRegisterEntry(uint16_t dex_register, DexRegisterLocation::Kind kind, int32_t value) {
StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
DCHECK_LT(dex_register, entry.num_dex_registers);
if (kind != DexRegisterLocation::Kind::kNone) {
// Ensure we only use non-compressed location kind at this stage.
DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
<< DexRegisterLocation::PrettyDescriptor(kind);
DexRegisterLocation location(kind, value);
// Look for Dex register `location` in the location catalog (using the
// companion hash map of locations to indices). Use its index if it
// is already in the location catalog. If not, insert it (in the
// location catalog and the hash map) and use the newly created index.
auto it = location_catalog_entries_indices_.Find(location);
if (it != location_catalog_entries_indices_.end()) {
// Retrieve the index from the hash map.
dex_register_locations_.Add(it->second);
} else {
// Create a new entry in the location catalog and the hash map.
size_t index = location_catalog_entries_.Size();
location_catalog_entries_.Add(location);
dex_register_locations_.Add(index);
location_catalog_entries_indices_.Insert(std::make_pair(location, index));
}
entry.live_dex_registers_mask->SetBit(dex_register);
entry.dex_register_map_hash += (1 << dex_register);
entry.dex_register_map_hash += static_cast<uint32_t>(value);
entry.dex_register_map_hash += static_cast<uint32_t>(kind);
stack_maps_.Put(stack_maps_.Size() - 1, entry);
}
}
private:
// Returns the index of an entry with the same dex register map
// or kNoSameDexMapFound if no such entry exists.
size_t FindEntryWithTheSameDexMap(size_t entry_index) {
StackMapEntry entry = stack_maps_.Get(entry_index);
auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.dex_register_map_hash);
if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
// We don't have a perfect hash functions so we need a list to collect all stack maps
// which might have the same dex register map.
GrowableArray<uint32_t> stack_map_indices(allocator_, 1);
stack_map_indices.Add(entry_index);
dex_map_hash_to_stack_map_indices_.Put(entry.dex_register_map_hash, stack_map_indices);
return kNoSameDexMapFound;
}
// TODO: We don't need to add ourselves to the map if we can guarantee that
// FindEntryWithTheSameDexMap is called just once per stack map entry.
// A good way to do this is to cache the offset in the stack map entry. This
// is easier to do if we add markers when the stack map constructions begins
// and when it ends.
// We might have collisions, so we need to check whether or not we should
// add the entry to the map. `needs_to_be_added` keeps track of this.
bool needs_to_be_added = true;
size_t result = kNoSameDexMapFound;
for (size_t i = 0; i < entries_it->second.Size(); i++) {
size_t test_entry_index = entries_it->second.Get(i);
if (test_entry_index == entry_index) {
needs_to_be_added = false;
} else if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), entry)) {
result = test_entry_index;
needs_to_be_added = false;
break;
}
}
if (needs_to_be_added) {
entries_it->second.Add(entry_index);
}
return result;
}
bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
return true;
}
if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
return false;
}
if (a.num_dex_registers != b.num_dex_registers) {
return false;
}
int index_in_dex_register_locations = 0;
for (uint32_t i = 0; i < a.num_dex_registers; i++) {
if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) {
return false;
}
if (a.live_dex_registers_mask->IsBitSet(i)) {
size_t a_loc = dex_register_locations_.Get(
a.dex_register_locations_start_index + index_in_dex_register_locations);
size_t b_loc = dex_register_locations_.Get(
b.dex_register_locations_start_index + index_in_dex_register_locations);
if (a_loc != b_loc) {
return false;
}
++index_in_dex_register_locations;
}
}
return true;
}
ArenaAllocator* allocator_;
GrowableArray<StackMapEntry> stack_maps_;
// A catalog of unique [location_kind, register_value] pairs (per method).
GrowableArray<DexRegisterLocation> location_catalog_entries_;
// Map from Dex register location catalog entries to their indices in the
// location catalog.
typedef HashMap<DexRegisterLocation, size_t, LocationCatalogEntriesIndicesEmptyFn,
DexRegisterLocationHashFn> LocationCatalogEntriesIndices;
LocationCatalogEntriesIndices location_catalog_entries_indices_;
// A set of concatenated maps of Dex register locations indices to
// `location_catalog_entries_`.
GrowableArray<size_t> dex_register_locations_;
GrowableArray<InlineInfoEntry> inline_infos_;
int stack_mask_max_;
uint32_t dex_pc_max_;
uint32_t native_pc_offset_max_;
uint32_t register_mask_max_;
size_t number_of_stack_maps_with_inline_info_;
ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
static constexpr uint32_t kNoSameDexMapFound = -1;
DISALLOW_COPY_AND_ASSIGN(StackMapStream);
};
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_