Add invoke infos to stack maps
Invoke info records the invoke type and dex method index for invokes
that may reach artQuickResolutionTrampoline. Having this information
recorded allows the runtime to avoid reading the dex code and pulling
in extra pages.
Code size increase for a large app:
93886360 -> 95811480 (2.05% increase)
1/2 of the code size increase is from making less stack maps deduped.
I suspect there is less deduping because of the invoke info method
index.
Merged disabled until we measure the RAM savings.
Test: test-art-host, N6P boots
Bug: 34109702
Change-Id: I6c5e4a60675a1d7c76dee0561a12909e4ab6d5d9
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index bac16cd..8dd423f 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -58,6 +58,9 @@
namespace art {
+// If true, we record the static and direct invokes in the invoke infos.
+static constexpr bool kEnableDexLayoutOptimizations = false;
+
// Return whether a location is consistent with a type.
static bool CheckType(Primitive::Type type, Location location) {
if (location.IsFpuRegister()
@@ -801,7 +804,18 @@
outer_environment_size,
inlining_depth);
- EmitEnvironment(instruction->GetEnvironment(), slow_path);
+ HEnvironment* const environment = instruction->GetEnvironment();
+ EmitEnvironment(environment, slow_path);
+ // Record invoke info, the common case for the trampoline is super and static invokes. Only
+ // record these to reduce oat file size.
+ if (kEnableDexLayoutOptimizations) {
+ if (environment != nullptr &&
+ instruction->IsInvoke() &&
+ instruction->IsInvokeStaticOrDirect()) {
+ HInvoke* const invoke = instruction->AsInvoke();
+ stack_map_stream_.AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex());
+ }
+ }
stack_map_stream_.EndStackMapEntry();
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
@@ -818,7 +832,6 @@
EmitEnvironment(instruction->GetEnvironment(), slow_path);
stack_map_stream_.EndStackMapEntry();
if (kIsDebugBuild) {
- HEnvironment* environment = instruction->GetEnvironment();
for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
HInstruction* in_environment = environment->GetInstructionAt(i);
if (in_environment != nullptr) {
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 1bcc8e1..eeae96e 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -41,12 +41,12 @@
current_entry_.inlining_depth = inlining_depth;
current_entry_.inline_infos_start_index = inline_infos_.size();
current_entry_.stack_mask_index = 0;
+ current_entry_.dex_method_index = DexFile::kDexNoIndex;
current_entry_.dex_register_entry.num_dex_registers = num_dex_registers;
current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size();
current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0)
? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream)
: nullptr;
-
if (sp_mask != nullptr) {
stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
}
@@ -99,6 +99,11 @@
current_dex_register_++;
}
+void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
+ current_entry_.invoke_type = invoke_type;
+ current_entry_.dex_method_index = dex_method_index;
+}
+
void StackMapStream::BeginInlineInfoEntry(ArtMethod* method,
uint32_t dex_pc,
uint32_t num_dex_registers,
@@ -166,6 +171,7 @@
encoding.inline_info.num_entries,
encoding.register_mask.num_entries,
encoding.stack_mask.num_entries);
+ ComputeInvokeInfoEncoding(&encoding);
DCHECK_EQ(code_info_encoding_.size(), 0u);
encoding.Compress(&code_info_encoding_);
encoding.ComputeTableOffsets();
@@ -212,6 +218,24 @@
return size;
}
+void StackMapStream::ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding) {
+ DCHECK(encoding != nullptr);
+ uint32_t native_pc_max = 0;
+ uint16_t method_index_max = 0;
+ size_t invoke_infos_count = 0;
+ size_t invoke_type_max = 0;
+ for (const StackMapEntry& entry : stack_maps_) {
+ if (entry.dex_method_index != DexFile::kDexNoIndex) {
+ native_pc_max = std::max(native_pc_max, entry.native_pc_code_offset.CompressedValue());
+ method_index_max = std::max(method_index_max, static_cast<uint16_t>(entry.dex_method_index));
+ invoke_type_max = std::max(invoke_type_max, static_cast<size_t>(entry.invoke_type));
+ ++invoke_infos_count;
+ }
+ }
+ encoding->invoke_info.num_entries = invoke_infos_count;
+ encoding->invoke_info.encoding.SetFromSizes(native_pc_max, invoke_type_max, method_index_max);
+}
+
void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
size_t dex_register_maps_bytes) {
uint32_t method_index_max = 0;
@@ -304,6 +328,7 @@
ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream);
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_index = 0;
+ size_t invoke_info_idx = 0;
for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) {
StackMap stack_map = code_info.GetStackMapAt(i, encoding);
StackMapEntry entry = stack_maps_[i];
@@ -318,6 +343,14 @@
dex_register_locations_region);
stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset);
+ if (entry.dex_method_index != DexFile::kDexNoIndex) {
+ InvokeInfo invoke_info(code_info.GetInvokeInfo(encoding, invoke_info_idx));
+ invoke_info.SetNativePcCodeOffset(encoding.invoke_info.encoding, entry.native_pc_code_offset);
+ invoke_info.SetInvokeType(encoding.invoke_info.encoding, entry.invoke_type);
+ invoke_info.SetMethodIndex(encoding.invoke_info.encoding, entry.dex_method_index);
+ ++invoke_info_idx;
+ }
+
// Set the inlining info.
if (entry.inlining_depth != 0) {
InlineInfo inline_info = code_info.GetInlineInfo(next_inline_info_index, encoding);
@@ -528,6 +561,7 @@
CodeInfo code_info(region);
CodeInfoEncoding encoding = code_info.ExtractEncoding();
DCHECK_EQ(code_info.GetNumberOfStackMaps(encoding), stack_maps_.size());
+ size_t invoke_info_index = 0;
for (size_t s = 0; s < stack_maps_.size(); ++s) {
const StackMap stack_map = code_info.GetStackMapAt(s, encoding);
const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding;
@@ -552,7 +586,14 @@
DCHECK_EQ(stack_mask.LoadBit(b), 0u);
}
}
-
+ if (entry.dex_method_index != DexFile::kDexNoIndex) {
+ InvokeInfo invoke_info = code_info.GetInvokeInfo(encoding, invoke_info_index);
+ DCHECK_EQ(invoke_info.GetNativePcOffset(encoding.invoke_info.encoding, instruction_set_),
+ entry.native_pc_code_offset.Uint32Value(instruction_set_));
+ DCHECK_EQ(invoke_info.GetInvokeType(encoding.invoke_info.encoding), entry.invoke_type);
+ DCHECK_EQ(invoke_info.GetMethodIndex(encoding.invoke_info.encoding), entry.dex_method_index);
+ invoke_info_index++;
+ }
CheckDexRegisterMap(code_info,
code_info.GetDexRegisterMapOf(
stack_map, encoding, entry.dex_register_entry.num_dex_registers),
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index bba3d51..4225a87 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -118,6 +118,8 @@
uint32_t register_mask_index;
DexRegisterMapEntry dex_register_entry;
size_t dex_register_map_index;
+ InvokeType invoke_type;
+ uint32_t dex_method_index;
};
struct InlineInfoEntry {
@@ -138,6 +140,8 @@
void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value);
+ void AddInvoke(InvokeType type, uint32_t dex_method_index);
+
void BeginInlineInfoEntry(ArtMethod* method,
uint32_t dex_pc,
uint32_t num_dex_registers,
@@ -184,6 +188,14 @@
bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const;
// Fill in the corresponding entries of a register map.
+ void ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding);
+
+ // Returns the index of an entry with the same dex register map as the current_entry,
+ // or kNoSameDexMapFound if no such entry exists.
+ size_t FindEntryWithTheSameDexMap();
+ bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const;
+
+ // Fill in the corresponding entries of a register map.
void FillInDexRegisterMap(DexRegisterMap dex_register_map,
uint32_t num_dex_registers,
const BitVector& live_dex_registers_mask,
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 0416951..330f7f2 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -934,7 +934,6 @@
EXPECT_EQ(offset_mips64.Uint32Value(kMips64), kMips64InstructionAlignment);
}
-
TEST(StackMapTest, TestDeduplicateStackMask) {
ArenaPool pool;
ArenaAllocator arena(&pool);
@@ -963,4 +962,48 @@
stack_map2.GetStackMaskIndex(encoding.stack_map.encoding));
}
+TEST(StackMapTest, TestInvokeInfo) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ StackMapStream stream(&arena, kRuntimeISA);
+
+ ArenaBitVector sp_mask(&arena, 0, true);
+ sp_mask.SetBit(1);
+ stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0);
+ stream.AddInvoke(kSuper, 1);
+ stream.EndStackMapEntry();
+ stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0);
+ stream.AddInvoke(kStatic, 3);
+ stream.EndStackMapEntry();
+ stream.BeginStackMapEntry(0, 16, 0x3, &sp_mask, 0, 0);
+ stream.AddInvoke(kDirect, 65535);
+ stream.EndStackMapEntry();
+
+ const size_t size = stream.PrepareForFillIn();
+ MemoryRegion region(arena.Alloc(size, kArenaAllocMisc), size);
+ stream.FillIn(region);
+
+ CodeInfo code_info(region);
+ CodeInfoEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(3u, code_info.GetNumberOfStackMaps(encoding));
+
+ InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4, encoding));
+ InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8, encoding));
+ InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16, encoding));
+ InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12, encoding));
+ EXPECT_FALSE(invoke_invalid.IsValid()); // No entry for that index.
+ EXPECT_TRUE(invoke1.IsValid());
+ EXPECT_TRUE(invoke2.IsValid());
+ EXPECT_TRUE(invoke3.IsValid());
+ EXPECT_EQ(invoke1.GetInvokeType(encoding.invoke_info.encoding), kSuper);
+ EXPECT_EQ(invoke1.GetMethodIndex(encoding.invoke_info.encoding), 1u);
+ EXPECT_EQ(invoke1.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 4u);
+ EXPECT_EQ(invoke2.GetInvokeType(encoding.invoke_info.encoding), kStatic);
+ EXPECT_EQ(invoke2.GetMethodIndex(encoding.invoke_info.encoding), 3u);
+ EXPECT_EQ(invoke2.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 8u);
+ EXPECT_EQ(invoke3.GetInvokeType(encoding.invoke_info.encoding), kDirect);
+ EXPECT_EQ(invoke3.GetMethodIndex(encoding.invoke_info.encoding), 65535u);
+ EXPECT_EQ(invoke3.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 16u);
+}
+
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9a3b28b..4413286 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -587,6 +587,7 @@
kByteKindCodeInfoLocationCatalog,
kByteKindCodeInfoDexRegisterMap,
kByteKindCodeInfoEncoding,
+ kByteKindCodeInfoInvokeInfo,
kByteKindCodeInfoStackMasks,
kByteKindCodeInfoRegisterMasks,
kByteKindStackMapNativePc,
@@ -637,6 +638,7 @@
Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum);
Dump(os, "CodeInfoStackMasks ", bits[kByteKindCodeInfoStackMasks], sum);
Dump(os, "CodeInfoRegisterMasks ", bits[kByteKindCodeInfoRegisterMasks], sum);
+ Dump(os, "CodeInfoInvokeInfo ", bits[kByteKindCodeInfoInvokeInfo], sum);
// Stack map section.
const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst,
bits + kByteKindStackMapLast + 1,
@@ -1592,10 +1594,8 @@
CodeInfoEncoding encoding(helper.GetEncoding());
StackMapEncoding stack_map_encoding(encoding.stack_map.encoding);
const size_t num_stack_maps = encoding.stack_map.num_entries;
- std::vector<uint8_t> size_vector;
- encoding.Compress(&size_vector);
if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
- size_vector.size() * kBitsPerByte,
+ encoding.HeaderSize() * kBitsPerByte,
oat_method.GetVmapTable())) {
// Stack maps
stats_.AddBits(
@@ -1627,6 +1627,13 @@
Stats::kByteKindCodeInfoRegisterMasks,
encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries);
+ // Invoke infos
+ if (encoding.invoke_info.num_entries > 0u) {
+ stats_.AddBits(
+ Stats::kByteKindCodeInfoInvokeInfo,
+ encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries);
+ }
+
// Location catalog
const size_t location_catalog_bytes =
helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index c2bca53..4c3990a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -358,6 +358,29 @@
}
}
+ static bool GetInvokeType(ArtMethod** sp, InvokeType* invoke_type, uint32_t* dex_method_index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK((*sp)->IsCalleeSaveMethod());
+ const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, Runtime::kSaveRefsAndArgs);
+ ArtMethod** caller_sp = reinterpret_cast<ArtMethod**>(
+ reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
+ uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp);
+ const OatQuickMethodHeader* current_code = (*caller_sp)->GetOatQuickMethodHeader(outer_pc);
+ if (!current_code->IsOptimized()) {
+ return false;
+ }
+ uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc);
+ CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+ CodeInfoEncoding encoding = code_info.ExtractEncoding();
+ InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset, encoding));
+ if (invoke.IsValid()) {
+ *invoke_type = static_cast<InvokeType>(invoke.GetInvokeType(encoding.invoke_info.encoding));
+ *dex_method_index = invoke.GetMethodIndex(encoding.invoke_info.encoding);
+ return true;
+ }
+ return false;
+ }
+
// For the given quick ref and args quick frame, return the caller's PC.
static uintptr_t GetCallingPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK((*sp)->IsCalleeSaveMethod());
@@ -977,60 +1000,87 @@
ArtMethod* caller = nullptr;
if (!called_method_known_on_entry) {
caller = QuickArgumentVisitor::GetCallingMethod(sp);
- uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
- const DexFile::CodeItem* code;
called_method.dex_file = caller->GetDexFile();
- code = caller->GetCodeItem();
- CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
- Instruction::Code instr_code = instr->Opcode();
- bool is_range;
- switch (instr_code) {
- case Instruction::INVOKE_DIRECT:
- invoke_type = kDirect;
- is_range = false;
- break;
- case Instruction::INVOKE_DIRECT_RANGE:
- invoke_type = kDirect;
- is_range = true;
- break;
- case Instruction::INVOKE_STATIC:
- invoke_type = kStatic;
- is_range = false;
- break;
- case Instruction::INVOKE_STATIC_RANGE:
- invoke_type = kStatic;
- is_range = true;
- break;
- case Instruction::INVOKE_SUPER:
- invoke_type = kSuper;
- is_range = false;
- break;
- case Instruction::INVOKE_SUPER_RANGE:
- invoke_type = kSuper;
- is_range = true;
- break;
- case Instruction::INVOKE_VIRTUAL:
- invoke_type = kVirtual;
- is_range = false;
- break;
- case Instruction::INVOKE_VIRTUAL_RANGE:
- invoke_type = kVirtual;
- is_range = true;
- break;
- case Instruction::INVOKE_INTERFACE:
- invoke_type = kInterface;
- is_range = false;
- break;
- case Instruction::INVOKE_INTERFACE_RANGE:
- invoke_type = kInterface;
- is_range = true;
- break;
- default:
- LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
- UNREACHABLE();
+
+ InvokeType stack_map_invoke_type;
+ uint32_t stack_map_dex_method_idx;
+ const bool found_stack_map = QuickArgumentVisitor::GetInvokeType(sp,
+ &stack_map_invoke_type,
+ &stack_map_dex_method_idx);
+ // For debug builds, we make sure both of the paths are consistent by also looking at the dex
+ // code.
+ if (!found_stack_map || kIsDebugBuild) {
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+ const DexFile::CodeItem* code;
+ code = caller->GetCodeItem();
+ CHECK_LT(dex_pc, code->insns_size_in_code_units_);
+ const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+ Instruction::Code instr_code = instr->Opcode();
+ bool is_range;
+ switch (instr_code) {
+ case Instruction::INVOKE_DIRECT:
+ invoke_type = kDirect;
+ is_range = false;
+ break;
+ case Instruction::INVOKE_DIRECT_RANGE:
+ invoke_type = kDirect;
+ is_range = true;
+ break;
+ case Instruction::INVOKE_STATIC:
+ invoke_type = kStatic;
+ is_range = false;
+ break;
+ case Instruction::INVOKE_STATIC_RANGE:
+ invoke_type = kStatic;
+ is_range = true;
+ break;
+ case Instruction::INVOKE_SUPER:
+ invoke_type = kSuper;
+ is_range = false;
+ break;
+ case Instruction::INVOKE_SUPER_RANGE:
+ invoke_type = kSuper;
+ is_range = true;
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ invoke_type = kVirtual;
+ is_range = false;
+ break;
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ invoke_type = kVirtual;
+ is_range = true;
+ break;
+ case Instruction::INVOKE_INTERFACE:
+ invoke_type = kInterface;
+ is_range = false;
+ break;
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ invoke_type = kInterface;
+ is_range = true;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
+ UNREACHABLE();
+ }
+ called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
+ // Check that the invoke matches what we expected, note that this path only happens for debug
+ // builds.
+ if (found_stack_map) {
+ DCHECK_EQ(stack_map_invoke_type, invoke_type);
+ if (invoke_type != kSuper) {
+ // Super may be sharpened.
+ DCHECK_EQ(stack_map_dex_method_idx, called_method.dex_method_index)
+ << called_method.dex_file->PrettyMethod(stack_map_dex_method_idx) << " "
+ << called_method.dex_file->PrettyMethod(called_method.dex_method_index);
+ }
+ } else {
+ VLOG(oat) << "Accessed dex file for invoke " << invoke_type << " "
+ << called_method.dex_method_index;
+ }
+ } else {
+ invoke_type = stack_map_invoke_type;
+ called_method.dex_method_index = stack_map_dex_method_idx;
}
- called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
} else {
invoke_type = kStatic;
called_method.dex_file = called->GetDexFile();
diff --git a/runtime/oat.h b/runtime/oat.h
index 0f6657b..656b868 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '1', '2', '\0' }; // Manual bump (Revert^3 hash-based DexCache types; stack maps).
+ static constexpr uint8_t kOatVersion[] = { '1', '1', '3', '\0' }; // Invoke info change.
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index f7a6402..67f0b57 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1145,6 +1145,94 @@
}
};
+// Format is [native pc, invoke type, method index].
+class InvokeInfoEncoding {
+ public:
+ void SetFromSizes(size_t native_pc_max,
+ size_t invoke_type_max,
+ size_t method_index_max) {
+ total_bit_size_ = 0;
+ DCHECK_EQ(kNativePcBitOffset, total_bit_size_);
+ total_bit_size_ += MinimumBitsToStore(native_pc_max);
+ invoke_type_bit_offset_ = total_bit_size_;
+ total_bit_size_ += MinimumBitsToStore(invoke_type_max);
+ method_index_bit_offset_ = total_bit_size_;
+ total_bit_size_ += MinimumBitsToStore(method_index_max);
+ }
+
+ ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
+ return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_);
+ }
+
+ ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
+ return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_);
+ }
+
+ ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const {
+ return FieldEncoding(method_index_bit_offset_, total_bit_size_);
+ }
+
+ ALWAYS_INLINE size_t BitSize() const {
+ return total_bit_size_;
+ }
+
+ template<typename Vector>
+ void Encode(Vector* dest) const {
+ static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment");
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
+ dest->insert(dest->end(), ptr, ptr + sizeof(*this));
+ }
+
+ void Decode(const uint8_t** ptr) {
+ *this = *reinterpret_cast<const InvokeInfoEncoding*>(*ptr);
+ *ptr += sizeof(*this);
+ }
+
+ private:
+ static constexpr uint8_t kNativePcBitOffset = 0;
+ uint8_t invoke_type_bit_offset_;
+ uint8_t method_index_bit_offset_;
+ uint8_t total_bit_size_;
+};
+
+class InvokeInfo {
+ public:
+ explicit InvokeInfo(BitMemoryRegion region) : region_(region) {}
+
+ ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding,
+ InstructionSet instruction_set) const {
+ CodeOffset offset(
+ CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+ return offset.Uint32Value(instruction_set);
+ }
+
+ ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding,
+ CodeOffset native_pc_offset) {
+ encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
+ }
+
+ ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const {
+ return encoding.GetInvokeTypeEncoding().Load(region_);
+ }
+
+ ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) {
+ encoding.GetInvokeTypeEncoding().Store(region_, invoke_type);
+ }
+
+ ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding) const {
+ return encoding.GetMethodIndexEncoding().Load(region_);
+ }
+
+ ALWAYS_INLINE void SetMethodIndex(const InvokeInfoEncoding& encoding, uint32_t method_index) {
+ encoding.GetMethodIndexEncoding().Store(region_, method_index);
+ }
+
+ bool IsValid() const { return region_.pointer() != nullptr; }
+
+ private:
+ BitMemoryRegion region_;
+};
+
// Most of the fields are encoded as ULEB128 to save space.
struct CodeInfoEncoding {
static constexpr uint32_t kInvalidSize = static_cast<size_t>(-1);
@@ -1154,6 +1242,7 @@
BitEncodingTable<StackMapEncoding> stack_map;
BitEncodingTable<BitRegionEncoding> register_mask;
BitEncodingTable<BitRegionEncoding> stack_mask;
+ BitEncodingTable<InvokeInfoEncoding> invoke_info;
BitEncodingTable<InlineInfoEncoding> inline_info;
CodeInfoEncoding() {}
@@ -1165,6 +1254,7 @@
stack_map.Decode(&ptr);
register_mask.Decode(&ptr);
stack_mask.Decode(&ptr);
+ invoke_info.Decode(&ptr);
if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
inline_info.Decode(&ptr);
} else {
@@ -1183,6 +1273,7 @@
stack_map.Encode(dest);
register_mask.Encode(dest);
stack_mask.Encode(dest);
+ invoke_info.Encode(dest);
if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
inline_info.Encode(dest);
}
@@ -1199,6 +1290,7 @@
stack_map.UpdateBitOffset(&bit_offset);
register_mask.UpdateBitOffset(&bit_offset);
stack_mask.UpdateBitOffset(&bit_offset);
+ invoke_info.UpdateBitOffset(&bit_offset);
inline_info.UpdateBitOffset(&bit_offset);
cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize();
}
@@ -1303,6 +1395,10 @@
return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding);
}
+ InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const {
+ return InvokeInfo(encoding.invoke_info.BitRegion(region_, index));
+ }
+
DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
const CodeInfoEncoding& encoding,
size_t number_of_dex_registers) const {
@@ -1426,6 +1522,17 @@
return StackMap();
}
+ InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset,
+ const CodeInfoEncoding& encoding) {
+ for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) {
+ InvokeInfo item = GetInvokeInfo(encoding, index);
+ if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) {
+ return item;
+ }
+ }
+ return InvokeInfo(BitMemoryRegion());
+ }
+
// Dump this CodeInfo object on `os`. `code_offset` is the (absolute)
// native PC of the compiled method and `number_of_dex_registers` the
// number of Dex virtual registers used in this method. If