Compressed native PC for stack maps
Compress native PC based on instruction alignment. This reduces the
size of stack maps, boot.oat is 0.4% smaller for arm64.
Test: test-art-host, test-art-target, N6P booting
Change-Id: I2b70eecabda88b06fa80a85688fd992070d54278
diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h
new file mode 100644
index 0000000..ab04b1e
--- /dev/null
+++ b/runtime/arch/code_offset.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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_RUNTIME_ARCH_CODE_OFFSET_H_
+#define ART_RUNTIME_ARCH_CODE_OFFSET_H_
+
+#include <iosfwd>
+
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "instruction_set.h"
+
+namespace art {
+
+// CodeOffset is a holder for compressed code offsets. Since some architectures have alignment
+// requirements it is possible to compress code offsets to reduce stack map sizes.
+class CodeOffset {
+ public:
+ ALWAYS_INLINE static CodeOffset FromOffset(uint32_t offset, InstructionSet isa = kRuntimeISA) {
+ return CodeOffset(offset / GetInstructionSetInstructionAlignment(isa));
+ }
+
+ ALWAYS_INLINE static CodeOffset FromCompressedOffset(uint32_t offset) {
+ return CodeOffset(offset);
+ }
+
+ ALWAYS_INLINE uint32_t Uint32Value(InstructionSet isa = kRuntimeISA) const {
+ uint32_t decoded = value_ * GetInstructionSetInstructionAlignment(isa);
+ DCHECK_GE(decoded, value_) << "Integer overflow";
+ return decoded;
+ }
+
+ // Return compressed internal value.
+ ALWAYS_INLINE uint32_t CompressedValue() const {
+ return value_;
+ }
+
+ ALWAYS_INLINE CodeOffset() = default;
+ ALWAYS_INLINE CodeOffset(const CodeOffset&) = default;
+ ALWAYS_INLINE CodeOffset& operator=(const CodeOffset&) = default;
+ ALWAYS_INLINE CodeOffset& operator=(CodeOffset&&) = default;
+
+ private:
+ ALWAYS_INLINE explicit CodeOffset(uint32_t value) : value_(value) {}
+
+ uint32_t value_ = 0u;
+};
+
+inline bool operator==(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() == b.CompressedValue();
+}
+
+inline bool operator!=(const CodeOffset& a, const CodeOffset& b) {
+ return !(a == b);
+}
+
+inline bool operator<(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() < b.CompressedValue();
+}
+
+inline bool operator<=(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() <= b.CompressedValue();
+}
+
+inline bool operator>(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() > b.CompressedValue();
+}
+
+inline bool operator>=(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() >= b.CompressedValue();
+}
+
+inline std::ostream& operator<<(std::ostream& os, const CodeOffset& offset) {
+ return os << offset.Uint32Value();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_CODE_OFFSET_H_
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index 4a8bea4..99aea62 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -75,6 +75,14 @@
// X86 instruction alignment. This is the recommended alignment for maximum performance.
static constexpr size_t kX86Alignment = 16;
+// Different than code alignment since code alignment is only first instruction of method.
+static constexpr size_t kThumb2InstructionAlignment = 2;
+static constexpr size_t kArm64InstructionAlignment = 4;
+static constexpr size_t kX86InstructionAlignment = 1;
+static constexpr size_t kX86_64InstructionAlignment = 1;
+static constexpr size_t kMipsInstructionAlignment = 2;
+static constexpr size_t kMips64InstructionAlignment = 2;
+
const char* GetInstructionSetString(InstructionSet isa);
// Note: Returns kNone when the string cannot be parsed to a known value.
@@ -106,6 +114,17 @@
}
}
+ALWAYS_INLINE static inline constexpr size_t GetInstructionSetInstructionAlignment(
+ InstructionSet isa) {
+ return (isa == kThumb2 || isa == kArm) ? kThumb2InstructionAlignment :
+ (isa == kArm64) ? kArm64InstructionAlignment :
+ (isa == kX86) ? kX86InstructionAlignment :
+ (isa == kX86_64) ? kX86_64InstructionAlignment :
+ (isa == kMips) ? kMipsInstructionAlignment :
+ (isa == kMips64) ? kMips64InstructionAlignment :
+ 0; // Invalid case, but constexpr doesn't support asserts.
+}
+
static inline bool IsValidInstructionSet(InstructionSet isa) {
switch (isa) {
case kArm:
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 5aae93a..b251b57 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -44,6 +44,15 @@
EXPECT_STREQ("none", GetInstructionSetString(kNone));
}
+TEST(InstructionSetTest, GetInstructionSetInstructionAlignment) {
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kThumb2), kThumb2InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kArm64), kArm64InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86), kX86InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86_64), kX86_64InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips), kMipsInstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips64), kMips64InstructionAlignment);
+}
+
TEST(InstructionSetTest, TestRoundTrip) {
EXPECT_EQ(kRuntimeISA, GetInstructionSetFromString(GetInstructionSetString(kRuntimeISA)));
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 2bb8819..6deb03d 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -514,7 +514,7 @@
}
}
- native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding) +
+ native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) +
osr_method->GetEntryPoint();
VLOG(jit) << "Jumping to "
<< method_name
diff --git a/runtime/oat.h b/runtime/oat.h
index 953b445..29821a2 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', '0', '2', '\0' }; // Enabling CC
+ static constexpr uint8_t kOatVersion[] = { '1', '0', '3', '\0' }; // Native pc change
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 9c2378d..fd84426 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -80,7 +80,7 @@
: code_info.GetStackMapForDexPc(dex_pc, encoding);
if (stack_map.IsValid()) {
return reinterpret_cast<uintptr_t>(entry_point) +
- stack_map.GetNativePcOffset(encoding.stack_map_encoding);
+ stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA);
}
if (abort_on_failure) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 9ebf9a7..3c92b86 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -116,7 +116,8 @@
void CodeInfo::Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
uint16_t number_of_dex_registers,
- bool dump_stack_maps) const {
+ bool dump_stack_maps,
+ InstructionSet instruction_set) const {
CodeInfoEncoding encoding = ExtractEncoding();
size_t number_of_stack_maps = GetNumberOfStackMaps(encoding);
vios->Stream()
@@ -139,6 +140,7 @@
encoding,
code_offset,
number_of_dex_registers,
+ instruction_set,
" " + std::to_string(i));
}
}
@@ -188,14 +190,16 @@
const CodeInfoEncoding& encoding,
uint32_t code_offset,
uint16_t number_of_dex_registers,
+ InstructionSet instruction_set,
const std::string& header_suffix) const {
StackMapEncoding stack_map_encoding = encoding.stack_map_encoding;
+ const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set);
vios->Stream()
<< "StackMap" << header_suffix
<< std::hex
- << " [native_pc=0x" << code_offset + GetNativePcOffset(stack_map_encoding) << "]"
+ << " [native_pc=0x" << code_offset + pc_offset << "]"
<< " (dex_pc=0x" << GetDexPc(stack_map_encoding)
- << ", native_pc_offset=0x" << GetNativePcOffset(stack_map_encoding)
+ << ", native_pc_offset=0x" << pc_offset
<< ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding)
<< ", inline_info_offset=0x" << GetInlineDescriptorOffset(stack_map_encoding)
<< ", register_mask=0x" << GetRegisterMask(stack_map_encoding)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 13886f2..28c4b88 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_STACK_MAP_H_
#define ART_RUNTIME_STACK_MAP_H_
+#include "arch/code_offset.h"
#include "base/bit_vector.h"
#include "base/bit_utils.h"
#include "dex_file.h"
@@ -805,12 +806,16 @@
encoding.GetDexPcEncoding().Store(region_, dex_pc);
}
- ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding) const {
- return encoding.GetNativePcEncoding().Load(region_);
+ ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding,
+ InstructionSet instruction_set) const {
+ CodeOffset offset(
+ CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+ return offset.Uint32Value(instruction_set);
}
- ALWAYS_INLINE void SetNativePcOffset(const StackMapEncoding& encoding, uint32_t native_pc_offset) {
- encoding.GetNativePcEncoding().Store(region_, native_pc_offset);
+ ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding,
+ CodeOffset native_pc_offset) {
+ encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
}
ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const {
@@ -866,6 +871,7 @@
const CodeInfoEncoding& encoding,
uint32_t code_offset,
uint16_t number_of_dex_registers,
+ InstructionSet instruction_set,
const std::string& header_suffix = "") const;
// Special (invalid) offset for the DexRegisterMapOffset field meaning
@@ -1234,15 +1240,16 @@
if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) {
StackMap other = GetStackMapAt(i + 1, encoding);
if (other.GetDexPc(stack_map_encoding) == dex_pc &&
- other.GetNativePcOffset(stack_map_encoding) ==
- stack_map.GetNativePcOffset(stack_map_encoding)) {
+ other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) ==
+ stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) {
DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding),
stack_map.GetDexRegisterMapOffset(stack_map_encoding));
DCHECK(!stack_map.HasInlineInfo(stack_map_encoding));
if (i < e - 2) {
// Make sure there are not three identical stack maps following each other.
- DCHECK_NE(stack_map.GetNativePcOffset(stack_map_encoding),
- GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding));
+ DCHECK_NE(
+ stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA),
+ GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA));
}
return stack_map;
}
@@ -1258,7 +1265,8 @@
// we could do binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
StackMap stack_map = GetStackMapAt(i, encoding);
- if (stack_map.GetNativePcOffset(encoding.stack_map_encoding) == native_pc_offset) {
+ if (stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) ==
+ native_pc_offset) {
return stack_map;
}
}
@@ -1273,7 +1281,8 @@
void Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
uint16_t number_of_dex_registers,
- bool dump_stack_maps) const;
+ bool dump_stack_maps,
+ InstructionSet instruction_set) const;
// Check that the code info has valid stack map and abort if it does not.
void AssertValidStackMap(const CodeInfoEncoding& encoding) const {