Merge "Clear inline caches at each full GC."
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 239bc59..6075cd6 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -187,7 +187,9 @@
}
}
-void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) {
+void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind,
+ InstructionSet isa,
+ size_t number_of_threads) {
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
method_inliner_map_.get(),
@@ -198,7 +200,7 @@
GetImageClasses(),
GetCompiledClasses(),
GetCompiledMethods(),
- /* thread_count */ 2,
+ number_of_threads,
/* dump_stats */ true,
/* dump_passes */ true,
timer_.get(),
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 7e0fbab..9552143 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -93,7 +93,7 @@
const char* method_name, const char* signature)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa);
+ void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U);
void ReserveImageSpace();
diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h
index 58502a3..b4a4d63 100644
--- a/compiler/debug/dwarf/debug_line_opcode_writer.h
+++ b/compiler/debug/dwarf/debug_line_opcode_writer.h
@@ -36,7 +36,7 @@
public:
static constexpr int kOpcodeBase = 13;
- static constexpr bool kDefaultIsStmt = true;
+ static constexpr bool kDefaultIsStmt = false;
static constexpr int kLineBase = -5;
static constexpr int kLineRange = 14;
@@ -81,8 +81,11 @@
this->PushUleb128(column);
}
- void NegateStmt() {
- this->PushUint8(DW_LNS_negate_stmt);
+ void SetIsStmt(bool is_stmt) {
+ if (is_stmt_ != is_stmt) {
+ this->PushUint8(DW_LNS_negate_stmt);
+ is_stmt_ = is_stmt;
+ }
}
void SetBasicBlock() {
@@ -112,6 +115,7 @@
current_address_ = 0;
current_file_ = 1;
current_line_ = 1;
+ is_stmt_ = kDefaultIsStmt;
}
// Uncoditionally set address using the long encoding.
@@ -227,7 +231,8 @@
code_factor_bits_(codeFactorBits),
current_address_(0),
current_file_(1),
- current_line_(1) {
+ current_line_(1),
+ is_stmt_(kDefaultIsStmt) {
}
private:
@@ -244,6 +249,7 @@
uint64_t current_address_;
int current_file_;
int current_line_;
+ bool is_stmt_;
std::vector<uintptr_t> patch_locations_;
DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index e455d0d..2ba3af5 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -217,7 +217,9 @@
DW_CHECK_NEXT("Advance Line by 2 to 3");
opcodes.SetColumn(4);
DW_CHECK_NEXT("Set column to 4");
- opcodes.NegateStmt();
+ opcodes.SetIsStmt(true);
+ DW_CHECK_NEXT("Set is_stmt to 1");
+ opcodes.SetIsStmt(false);
DW_CHECK_NEXT("Set is_stmt to 0");
opcodes.SetBasicBlock();
DW_CHECK_NEXT("Set basic block");
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index 41bfe79..e2f0a65 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -62,7 +62,7 @@
InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
ScratchFile file;
FileOutputStream output_stream(file.GetFile());
- ElfBuilder<ElfTypes> builder(isa, &output_stream);
+ ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
builder.Start();
if (!debug_info_data_.empty()) {
builder.WriteSection(".debug_info", &debug_info_data_);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index d3859ca..11be4e9 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -184,6 +184,10 @@
// Generate mapping opcodes from PC to Java lines.
if (file_index != 0) {
+ // If the method was not compiled as native-debuggable, we still generate all available
+ // lines, but we try to prevent the debugger from stepping and setting breakpoints since
+ // the information is too inaccurate for that (breakpoints would be set after the calls).
+ const bool default_is_stmt = mi->is_native_debuggable;
bool first = true;
for (SrcMapElem pc2dex : pc2dex_map) {
uint32_t pc = pc2dex.from_;
@@ -205,13 +209,14 @@
// Assume that any preceding code is prologue.
int first_line = dex2line_map.front().line_;
// Prologue is not a sensible place for a breakpoint.
- opcodes.NegateStmt();
+ opcodes.SetIsStmt(false);
opcodes.AddRow(method_address, first_line);
- opcodes.NegateStmt();
opcodes.SetPrologueEnd();
}
+ opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
} else if (line != opcodes.CurrentLine()) {
+ opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
}
}
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 01bd679..79069d1 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -91,24 +91,34 @@
std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
+ const InstructionSetFeatures* features,
size_t rodata_size,
size_t text_size,
const ArrayRef<const MethodDebugInfo>& method_infos) {
if (Is64BitInstructionSet(isa)) {
- return MakeMiniDebugInfoInternal<ElfTypes64>(isa, rodata_size, text_size, method_infos);
+ return MakeMiniDebugInfoInternal<ElfTypes64>(isa,
+ features,
+ rodata_size,
+ text_size,
+ method_infos);
} else {
- return MakeMiniDebugInfoInternal<ElfTypes32>(isa, rodata_size, text_size, method_infos);
+ return MakeMiniDebugInfoInternal<ElfTypes32>(isa,
+ features,
+ rodata_size,
+ text_size,
+ method_infos);
}
}
template <typename ElfTypes>
static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
+ const InstructionSet isa,
+ const InstructionSetFeatures* features,
const MethodDebugInfo& method_info) {
- const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
std::vector<uint8_t> buffer;
buffer.reserve(KB);
VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
WriteDebugInfo(builder.get(),
@@ -124,23 +134,26 @@
return ArrayRef<const uint8_t>(result, buffer.size());
}
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) {
- const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa,
+ const InstructionSetFeatures* features,
+ const MethodDebugInfo& method_info) {
if (Is64BitInstructionSet(isa)) {
- return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info);
+ return WriteDebugElfFileForMethodInternal<ElfTypes64>(isa, features, method_info);
} else {
- return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info);
+ return WriteDebugElfFileForMethodInternal<ElfTypes32>(isa, features, method_info);
}
}
template <typename ElfTypes>
static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal(
- const InstructionSet isa, const ArrayRef<mirror::Class*>& types)
+ const InstructionSet isa,
+ const InstructionSetFeatures* features,
+ const ArrayRef<mirror::Class*>& types)
SHARED_REQUIRES(Locks::mutator_lock_) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
@@ -159,11 +172,12 @@
}
ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+ const InstructionSetFeatures* features,
const ArrayRef<mirror::Class*>& types) {
if (Is64BitInstructionSet(isa)) {
- return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types);
+ return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, features, types);
} else {
- return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types);
+ return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, features, types);
}
}
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 103b501..d364521 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -37,13 +37,17 @@
bool write_oat_patches);
std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa,
+ const InstructionSetFeatures* features,
size_t rodata_section_size,
size_t text_section_size,
const ArrayRef<const MethodDebugInfo>& method_infos);
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info);
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa,
+ const InstructionSetFeatures* features,
+ const MethodDebugInfo& method_info);
ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+ const InstructionSetFeatures* features,
const ArrayRef<mirror::Class*>& types)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
index 5c7d1c7..fb63d62 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -79,13 +79,14 @@
template <typename ElfTypes>
static std::vector<uint8_t> MakeMiniDebugInfoInternal(
InstructionSet isa,
+ const InstructionSetFeatures* features,
size_t rodata_section_size,
size_t text_section_size,
const ArrayRef<const MethodDebugInfo>& method_infos) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
VectorOutputStream out("Mini-debug-info ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
builder->Start();
// Mirror .rodata and .text as NOBITS sections.
// It is needed to detected relocations after compression.
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 6b3dd8c..bb09f7e 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -30,6 +30,7 @@
uint32_t access_flags;
const DexFile::CodeItem* code_item;
bool deduped;
+ bool is_native_debuggable;
uintptr_t low_pc;
uintptr_t high_pc;
CompiledMethod* compiled_method;
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index ad4ddad..8f5d3ae 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -101,6 +101,14 @@
false, // kIntrinsicCas
false, // kIntrinsicUnsafeGet
false, // kIntrinsicUnsafePut
+ false, // kIntrinsicUnsafeGetAndAddInt,
+ false, // kIntrinsicUnsafeGetAndAddLong,
+ false, // kIntrinsicUnsafeGetAndSetInt,
+ false, // kIntrinsicUnsafeGetAndSetLong,
+ false, // kIntrinsicUnsafeGetAndSetObject,
+ false, // kIntrinsicUnsafeLoadFence,
+ false, // kIntrinsicUnsafeStoreFence,
+ false, // kIntrinsicUnsafeFullFence,
true, // kIntrinsicSystemArrayCopyCharArray
true, // kIntrinsicSystemArrayCopy
};
@@ -177,6 +185,14 @@
static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static");
static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
"SystemArrayCopyCharArray must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy],
@@ -318,6 +334,14 @@
"putObject", // kNameCachePutObject
"putObjectVolatile", // kNameCachePutObjectVolatile
"putOrderedObject", // kNameCachePutOrderedObject
+ "getAndAddInt", // kNameCacheGetAndAddInt,
+ "getAndAddLong", // kNameCacheGetAndAddLong,
+ "getAndSetInt", // kNameCacheGetAndSetInt,
+ "getAndSetLong", // kNameCacheGetAndSetLong,
+ "getAndSetObject", // kNameCacheGetAndSetObject,
+ "loadFence", // kNameCacheLoadFence,
+ "storeFence", // kNameCacheStoreFence,
+ "fullFence", // kNameCacheFullFence,
"arraycopy", // kNameCacheArrayCopy
"bitCount", // kNameCacheBitCount
"compare", // kNameCacheCompare
@@ -404,10 +428,14 @@
kClassCacheJavaLangObject, kClassCacheJavaLangObject } },
// kProtoCacheObjectJ_I
{ kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
+ // kProtoCacheObjectJI_I
+ { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
// kProtoCacheObjectJI_V
{ kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
// kProtoCacheObjectJ_J
{ kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
+ // kProtoCacheObjectJJ_J
+ { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
// kProtoCacheObjectJJ_V
{ kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
// kProtoCacheObjectJ_Object
@@ -415,6 +443,9 @@
// kProtoCacheObjectJObject_V
{ kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
kClassCacheJavaLangObject } },
+ // kProtoCacheObjectJObject_Object
+ { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong,
+ kClassCacheJavaLangObject } },
// kProtoCacheCharArrayICharArrayII_V
{ kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} },
@@ -609,6 +640,16 @@
UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
#undef UNSAFE_GET_PUT
+ // 1.8
+ INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0),
+ INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0),
+ INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0),
+ INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0),
+ INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0),
+ INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0),
+ INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0),
+ INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0),
+
INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
0),
INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy,
@@ -815,6 +856,14 @@
case kIntrinsicRotateRight:
case kIntrinsicRotateLeft:
case kIntrinsicSignum:
+ case kIntrinsicUnsafeGetAndAddInt:
+ case kIntrinsicUnsafeGetAndAddLong:
+ case kIntrinsicUnsafeGetAndSetInt:
+ case kIntrinsicUnsafeGetAndSetLong:
+ case kIntrinsicUnsafeGetAndSetObject:
+ case kIntrinsicUnsafeLoadFence:
+ case kIntrinsicUnsafeStoreFence:
+ case kIntrinsicUnsafeFullFence:
case kIntrinsicSystemArrayCopy:
return false; // not implemented in quick.
default:
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index b465db2..34b56cd 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -227,6 +227,14 @@
kNameCachePutObject,
kNameCachePutObjectVolatile,
kNameCachePutOrderedObject,
+ kNameCacheGetAndAddInt,
+ kNameCacheGetAndAddLong,
+ kNameCacheGetAndSetInt,
+ kNameCacheGetAndSetLong,
+ kNameCacheGetAndSetObject,
+ kNameCacheLoadFence,
+ kNameCacheStoreFence,
+ kNameCacheFullFence,
kNameCacheArrayCopy,
kNameCacheBitCount,
kNameCacheCompare,
@@ -282,11 +290,14 @@
kProtoCacheObjectJJJ_Z,
kProtoCacheObjectJObjectObject_Z,
kProtoCacheObjectJ_I,
+ kProtoCacheObjectJI_I,
kProtoCacheObjectJI_V,
kProtoCacheObjectJ_J,
+ kProtoCacheObjectJJ_J,
kProtoCacheObjectJJ_V,
kProtoCacheObjectJ_Object,
kProtoCacheObjectJObject_V,
+ kProtoCacheObjectJObject_Object,
kProtoCacheCharArrayICharArrayII_V,
kProtoCacheObjectIObjectII_V,
kProtoCacheIICharArrayI_V,
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a220959..4db82a6 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -159,10 +159,16 @@
size_t GetInlineDepthLimit() const {
return inline_depth_limit_;
}
+ void SetInlineDepthLimit(size_t limit) {
+ inline_depth_limit_ = limit;
+ }
size_t GetInlineMaxCodeUnits() const {
return inline_max_code_units_;
}
+ void SetInlineMaxCodeUnits(size_t units) {
+ inline_max_code_units_ = units;
+ }
double GetTopKProfileThreshold() const {
return top_k_profile_threshold_;
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index f7da609..943e2a8 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -20,6 +20,7 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "arch/mips/instruction_set_features_mips.h"
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/unix_file/fd_file.h"
@@ -392,8 +393,9 @@
}
};
- ElfBuilder(InstructionSet isa, OutputStream* output)
+ ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output)
: isa_(isa),
+ features_(features),
stream_(output),
rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0),
@@ -818,6 +820,7 @@
}
InstructionSet isa_;
+ const InstructionSetFeatures* features_;
ErrorDelayingOutputStream stream_;
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 19346ec..e35662d 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -51,10 +51,12 @@
class DebugInfoTask : public Task {
public:
DebugInfoTask(InstructionSet isa,
+ const InstructionSetFeatures* features,
size_t rodata_section_size,
size_t text_section_size,
const ArrayRef<const debug::MethodDebugInfo>& method_infos)
: isa_(isa),
+ instruction_set_features_(features),
rodata_section_size_(rodata_section_size),
text_section_size_(text_section_size),
method_infos_(method_infos) {
@@ -62,6 +64,7 @@
void Run(Thread*) {
result_ = debug::MakeMiniDebugInfo(isa_,
+ instruction_set_features_,
rodata_section_size_,
text_section_size_,
method_infos_);
@@ -73,6 +76,7 @@
private:
InstructionSet isa_;
+ const InstructionSetFeatures* instruction_set_features_;
size_t rodata_section_size_;
size_t text_section_size_;
const ArrayRef<const debug::MethodDebugInfo>& method_infos_;
@@ -83,6 +87,7 @@
class ElfWriterQuick FINAL : public ElfWriter {
public:
ElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
const CompilerOptions* compiler_options,
File* elf_file);
~ElfWriterQuick();
@@ -107,6 +112,7 @@
std::vector<uint8_t>* buffer);
private:
+ const InstructionSetFeatures* instruction_set_features_;
const CompilerOptions* const compiler_options_;
File* const elf_file_;
size_t rodata_size_;
@@ -121,27 +127,36 @@
};
std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
const CompilerOptions* compiler_options,
File* elf_file) {
if (Is64BitInstructionSet(instruction_set)) {
- return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set, compiler_options, elf_file);
+ return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set,
+ features,
+ compiler_options,
+ elf_file);
} else {
- return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set, compiler_options, elf_file);
+ return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set,
+ features,
+ compiler_options,
+ elf_file);
}
}
template <typename ElfTypes>
ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
const CompilerOptions* compiler_options,
File* elf_file)
: ElfWriter(),
+ instruction_set_features_(features),
compiler_options_(compiler_options),
elf_file_(elf_file),
rodata_size_(0u),
text_size_(0u),
bss_size_(0u),
output_stream_(MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))),
- builder_(new ElfBuilder<ElfTypes>(instruction_set, output_stream_.get())) {}
+ builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {}
template <typename ElfTypes>
ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {}
@@ -205,7 +220,11 @@
// Prepare the mini-debug-info in background while we do other I/O.
Thread* self = Thread::Current();
debug_info_task_ = std::unique_ptr<DebugInfoTask>(
- new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos));
+ new DebugInfoTask(builder_->GetIsa(),
+ instruction_set_features_,
+ rodata_size_,
+ text_size_,
+ method_infos));
debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
new ThreadPool("Mini-debug-info writer", 1));
debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 347d372..3d5dd39 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -26,8 +26,10 @@
namespace art {
class CompilerOptions;
+class InstructionSetFeatures;
std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
const CompilerOptions* compiler_options,
File* elf_file);
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 5763cec..91579e9 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -24,6 +24,7 @@
#include "class_linker-inl.h"
#include "common_compiler_test.h"
#include "debug/method_debug_info.h"
+#include "driver/compiler_options.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
@@ -48,8 +49,12 @@
};
void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
- // TODO: Test does not currently work with optimizing.
- CreateCompilerDriver(Compiler::kQuick, kRuntimeISA);
+ CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+
+ // Set inline filter values.
+ compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
+ compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// Enable write for dex2dex.
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
@@ -99,6 +104,7 @@
const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath();
std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
&compiler_driver_->GetCompilerOptions(),
oat_file.GetFile());
elf_writer->Start();
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 871435b..b1b971f 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -266,17 +266,9 @@
<< PrettyDuration(NanoTime() - compress_start_time);
}
- // Write header first, as uncompressed.
- image_header->data_size_ = data_size;
- if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) {
- PLOG(ERROR) << "Failed to write image file header " << image_filename;
- image_file->Erase();
- return false;
- }
-
// Write out the image + fields + methods.
const bool is_compressed = compressed_data != nullptr;
- if (!image_file->WriteFully(image_data_to_write, data_size)) {
+ if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) {
PLOG(ERROR) << "Failed to write image file data " << image_filename;
image_file->Erase();
return false;
@@ -291,13 +283,33 @@
if (!is_compressed) {
CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
}
- if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
- bitmap_section.Size(),
- bitmap_position_in_file)) {
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
+ bitmap_section.Size(),
+ bitmap_position_in_file)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
image_file->Erase();
return false;
}
+
+ int err = image_file->Flush();
+ if (err < 0) {
+ PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write header last in case the compiler gets killed in the middle of image writing.
+ // We do not want to have a corrupted image with a valid header.
+ // The header is uncompressed since it contains whether the image is compressed or not.
+ image_header->data_size_ = data_size;
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()),
+ sizeof(ImageHeader),
+ 0)) {
+ PLOG(ERROR) << "Failed to write image file header " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
static_cast<size_t>(image_file->GetLength()));
if (image_file->FlushCloseOrErase() != 0) {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index dba9dd7..f204b28 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -443,7 +443,7 @@
static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
- uintptr_t NativeOffsetInImage(void* obj);
+ uintptr_t NativeOffsetInImage(void* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index eeb2576..cda2e27 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -69,7 +69,8 @@
DCHECK(jit_compiler != nullptr);
if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) {
const ArrayRef<mirror::Class*> types_array(types, count);
- ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array);
+ ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
+ kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
CreateJITCodeEntry(std::unique_ptr<const uint8_t[]>(elf_file.data()), elf_file.size());
}
}
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index bc134fe..533dccf 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -42,6 +42,9 @@
CompilerOptions* GetCompilerOptions() const {
return compiler_options_.get();
}
+ CompilerDriver* GetCompilerDriver() const {
+ return compiler_driver_.get();
+ }
private:
std::unique_ptr<CompilerOptions> compiler_options_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 14fd105..d22044a 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -176,6 +176,7 @@
bool verify) {
std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
&compiler_driver_->GetCompilerOptions(),
file);
elf_writer->Start();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index c60b02a..50f5aba 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -806,7 +806,8 @@
}
}
- if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) {
+ const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions();
+ if (compiler_options.GenerateAnyDebugInfo()) {
// Record debug information for this function if we are doing that.
const uint32_t quick_code_start = quick_code_offset -
writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -817,6 +818,7 @@
it.GetMethodAccessFlags(),
it.GetMethodCodeItem(),
deduped,
+ compiler_options.GetNativeDebuggable(),
quick_code_start,
quick_code_start + code_size,
compiled_method});
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index f8a9a94..b95ece5 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -94,6 +94,7 @@
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
+ void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1594,6 +1595,12 @@
invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
}
+void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) {
+ uint32_t dex_pc = invoke->GetDexPc();
+ HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier);
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
switch (instruction->GetIntrinsic()) {
case Intrinsics::kStringEquals:
@@ -1626,6 +1633,15 @@
case Intrinsics::kDoubleDoubleToLongBits:
SimplifyFP2Int(instruction);
break;
+ case Intrinsics::kUnsafeLoadFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny);
+ break;
+ case Intrinsics::kUnsafeStoreFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore);
+ break;
+ case Intrinsics::kUnsafeFullFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny);
+ break;
default:
break;
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 3ed0278..5d4c4e2 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -472,6 +472,24 @@
break;
}
+ // 1.8.
+ case kIntrinsicUnsafeGetAndAddInt:
+ return Intrinsics::kUnsafeGetAndAddInt;
+ case kIntrinsicUnsafeGetAndAddLong:
+ return Intrinsics::kUnsafeGetAndAddLong;
+ case kIntrinsicUnsafeGetAndSetInt:
+ return Intrinsics::kUnsafeGetAndSetInt;
+ case kIntrinsicUnsafeGetAndSetLong:
+ return Intrinsics::kUnsafeGetAndSetLong;
+ case kIntrinsicUnsafeGetAndSetObject:
+ return Intrinsics::kUnsafeGetAndSetObject;
+ case kIntrinsicUnsafeLoadFence:
+ return Intrinsics::kUnsafeLoadFence;
+ case kIntrinsicUnsafeStoreFence:
+ return Intrinsics::kUnsafeStoreFence;
+ case kIntrinsicUnsafeFullFence:
+ return Intrinsics::kUnsafeFullFence;
+
// Virtual cases.
case kIntrinsicReferenceGetReferent:
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 0cec5cc..3da8285 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -231,7 +231,10 @@
UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
UNREACHABLE_INTRINSIC(Arch, LongCompare) \
UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
-UNREACHABLE_INTRINSIC(Arch, LongSignum)
+UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence)
} // namespace art
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 69c9708..c7d9a0d 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -2002,6 +2002,13 @@
UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
+
UNREACHABLE_INTRINSICS(ARM)
#undef __
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 934b427..8a44441 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1953,6 +1953,13 @@
UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
+
UNREACHABLE_INTRINSICS(ARM64)
#undef __
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index b8933e1..dd9294d 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -128,6 +128,14 @@
V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
+ V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow)
#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 710df0a..e159701 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1832,9 +1832,14 @@
UNIMPLEMENTED_INTRINSIC(MIPS, MathTan)
UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh)
-UNREACHABLE_INTRINSICS(MIPS)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
-#undef UNIMPLEMENTED_INTRINSIC
+UNREACHABLE_INTRINSICS(MIPS)
#undef __
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 617844b..0dc602f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1729,6 +1729,13 @@
UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject)
+
UNREACHABLE_INTRINSICS(MIPS64)
#undef __
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 9a2dc41..4c802c0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2637,6 +2637,13 @@
UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject)
+
UNREACHABLE_INTRINSICS(X86)
#undef __
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 75204b4..41095cd 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2715,6 +2715,13 @@
UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
+// 1.8.
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong)
+UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject)
+
UNREACHABLE_INTRINSICS(X86_64)
#undef __
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b684cc6..ecb690f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
+#include "dex_instruction-inl.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c1b4d24..42f22af 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -913,7 +913,8 @@
return false;
}
- if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) {
+ const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
+ if (compiler_options.GetGenerateDebugInfo()) {
const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
CompiledMethod compiled_method(
@@ -936,11 +937,15 @@
access_flags,
code_item,
false, // deduped.
+ compiler_options.GetNativeDebuggable(),
code_address,
code_address + code_allocator.GetSize(),
&compiled_method
};
- ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(method_debug_info);
+ ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(
+ GetCompilerDriver()->GetInstructionSet(),
+ GetCompilerDriver()->GetInstructionSetFeatures(),
+ method_debug_info);
CreateJITCodeEntryForAddress(code_address,
std::unique_ptr<const uint8_t[]>(elf_file.data()),
elf_file.size());
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ea19059..44e7fc9 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1034,9 +1034,11 @@
key_value_store_->Put(
OatHeader::kDebuggableKey,
compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
- key_value_store_->Put(
- OatHeader::kExtractOnlyKey,
- compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ if (compiler_options_->IsExtractOnly()) {
+ key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue);
+ } else if (UseProfileGuidedCompilation()) {
+ key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue);
+ }
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1891,13 +1893,6 @@
return success;
}
- bool ShouldCompileBasedOnProfiles() const {
- DCHECK(UseProfileGuidedCompilation());
- // If we are given a profile, compile only if we have some data in it.
- return (profile_compilation_info_ != nullptr) &&
- (profile_compilation_info_->GetNumberOfMethods() != 0);
- }
-
private:
template <typename T>
static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
@@ -2105,8 +2100,10 @@
elf_writers_.reserve(oat_files_.size());
oat_writers_.reserve(oat_files_.size());
for (const std::unique_ptr<File>& oat_file : oat_files_) {
- elf_writers_.emplace_back(
- CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get()));
+ elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_,
+ instruction_set_features_.get(),
+ compiler_options_.get(),
+ oat_file.get()));
elf_writers_.back()->Start();
oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_));
}
@@ -2593,16 +2590,11 @@
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
dex2oat->ParseArgs(argc, argv);
- // Process profile information and assess if we need to do a profile guided compilation.
+ // If needed, process profile information for profile guided compilation.
// This operation involves I/O.
if (dex2oat->UseProfileGuidedCompilation()) {
- if (dex2oat->LoadProfile()) {
- if (!dex2oat->ShouldCompileBasedOnProfiles()) {
- LOG(INFO) << "Skipped compilation because of insignificant profile delta";
- return EXIT_SUCCESS;
- }
- } else {
- LOG(WARNING) << "Failed to process profile files";
+ if (!dex2oat->LoadProfile()) {
+ LOG(ERROR) << "Failed to process profile file";
return EXIT_FAILURE;
}
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index c187536..6c6c807 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -98,6 +98,7 @@
return ret;
}
+template <typename ElfTypes>
class OatSymbolizer FINAL {
public:
OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
@@ -115,11 +116,13 @@
bool Symbolize() {
const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet();
+ const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap(
+ isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap());
File* elf_file = OS::CreateEmptyFile(output_name_.c_str());
std::unique_ptr<BufferedOutputStream> output_stream(
MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file)));
- builder_.reset(new ElfBuilder<ElfTypes32>(isa, output_stream.get()));
+ builder_.reset(new ElfBuilder<ElfTypes>(isa, features, output_stream.get()));
builder_->Start();
@@ -149,14 +152,14 @@
elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize());
builder_->WriteDynamicSection();
- Walk(&art::OatSymbolizer::RegisterForDedup);
+ Walk(&art::OatSymbolizer<ElfTypes>::RegisterForDedup);
NormalizeState();
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
AddTrampolineSymbols();
- Walk(&art::OatSymbolizer::AddSymbol);
+ Walk(&art::OatSymbolizer<ElfTypes>::AddSymbol);
strtab->End();
symtab->Start();
@@ -344,7 +347,7 @@
}
const OatFile* oat_file_;
- std::unique_ptr<ElfBuilder<ElfTypes32> > builder_;
+ std::unique_ptr<ElfBuilder<ElfTypes> > builder_;
std::unordered_map<uint32_t, uint32_t> state_;
const std::string output_name_;
};
@@ -2542,8 +2545,17 @@
return EXIT_FAILURE;
}
- OatSymbolizer oat_symbolizer(oat_file, output_name);
- if (!oat_symbolizer.Symbolize()) {
+ bool result;
+ // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF
+ // files for 64-bit code in the past.
+ if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) {
+ OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file, output_name);
+ result = oat_symbolizer.Symbolize();
+ } else {
+ OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file, output_name);
+ result = oat_symbolizer.Symbolize();
+ }
+ if (!result) {
fprintf(stderr, "Failed to symbolize\n");
return EXIT_FAILURE;
}
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 12d6d8f..ebe89bb 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -463,6 +463,12 @@
interface_method->VisitRoots(visitor, pointer_size);
}
visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
+ if (!IsNative()) {
+ ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
+ if (profiling_info != nullptr) {
+ profiling_info->VisitRoots(visitor);
+ }
+ }
}
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index a60f31e..f97ad51 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -377,7 +377,7 @@
Runtime* runtime = Runtime::Current();
const void* existing_entry_point = GetEntryPointFromQuickCompiledCode();
- DCHECK(existing_entry_point != nullptr);
+ CHECK(existing_entry_point != nullptr) << PrettyMethod(this) << "@" << this;
ClassLinker* class_linker = runtime->GetClassLinker();
if (class_linker->IsQuickGenericJniStub(existing_entry_point)) {
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 4672948..e4097dd 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -234,21 +234,34 @@
return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
}
-bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+template <bool kUseOffset>
+bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
DCHECK(!read_only_mode_);
- const char* ptr = static_cast<const char*>(buffer);
moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
+ DCHECK(kUseOffset || offset == 0u);
+ const char* ptr = static_cast<const char*>(buffer);
while (byte_count > 0) {
- ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
+ ssize_t bytes_written = kUseOffset
+ ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
+ : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
if (bytes_written == -1) {
return false;
}
byte_count -= bytes_written; // Reduce the number of remaining bytes.
ptr += bytes_written; // Move the buffer forward.
+ offset += static_cast<size_t>(bytes_written);
}
return true;
}
+bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
+ return WriteFullyGeneric<true>(buffer, byte_count, offset);
+}
+
+bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+ return WriteFullyGeneric<false>(buffer, byte_count, 0u);
+}
+
bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
DCHECK(!read_only_mode_);
off_t off = static_cast<off_t>(offset);
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8040afe..16cd44f 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -79,6 +79,7 @@
bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
+ bool PwriteFully(const void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
// Copy data from another file.
bool Copy(FdFile* input_file, int64_t offset, int64_t size);
@@ -119,6 +120,9 @@
GuardState guard_state_;
private:
+ template <bool kUseOffset>
+ bool WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset);
+
int fd_;
std::string file_path_;
bool auto_close_;
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index ecf607c..9bc87e5 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -110,6 +110,34 @@
ASSERT_EQ(file.Close(), 0);
}
+TEST_F(FdFileTest, ReadWriteFullyWithOffset) {
+ // New scratch file, zero-length.
+ art::ScratchFile tmp;
+ FdFile file;
+ ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+
+ const char* test_string = "This is a test string";
+ size_t length = strlen(test_string) + 1;
+ const size_t offset = 12;
+ std::unique_ptr<char[]> offset_read_string(new char[length]);
+ std::unique_ptr<char[]> read_string(new char[length]);
+
+ // Write scratch data to file that we can read back into.
+ EXPECT_TRUE(file.PwriteFully(test_string, length, offset));
+ ASSERT_EQ(file.Flush(), 0);
+
+ // Test reading both the offsets.
+ EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset));
+ EXPECT_STREQ(test_string, &offset_read_string[0]);
+
+ EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u));
+ EXPECT_NE(memcmp(&read_string[0], test_string, length), 0);
+
+ ASSERT_EQ(file.Close(), 0);
+}
+
TEST_F(FdFileTest, Copy) {
art::ScratchFile src_tmp;
FdFile src;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a13a2e3..d51a1f7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1207,217 +1207,222 @@
Thread* const self = Thread::Current();
gc::Heap* const heap = Runtime::Current()->GetHeap();
const ImageHeader& header = space->GetImageHeader();
- // Add image classes into the class table for the class loader, and fixup the dex caches and
- // class loader fields.
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
- // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
- // rely on clobering the dex cache arrays in the image to forward to bss.
- size_t num_dex_caches_with_bss_arrays = 0;
- const size_t num_dex_caches = dex_caches->GetLength();
- for (size_t i = 0; i < num_dex_caches; i++) {
- mirror::DexCache* const dex_cache = dex_caches->Get(i);
- const DexFile* const dex_file = dex_cache->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
- if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
- ++num_dex_caches_with_bss_arrays;
- }
- }
- *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
- if (*out_forward_dex_cache_array) {
- if (num_dex_caches_with_bss_arrays != num_dex_caches) {
- // Reject application image since we cannot forward only some of the dex cache arrays.
- // TODO: We could get around this by having a dedicated forwarding slot. It should be an
- // uncommon case.
- *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
- num_dex_caches_with_bss_arrays,
- num_dex_caches);
- return false;
- }
- }
- // Only add the classes to the class loader after the points where we can return false.
- for (size_t i = 0; i < num_dex_caches; i++) {
- mirror::DexCache* const dex_cache = dex_caches->Get(i);
- const DexFile* const dex_file = dex_cache->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
- if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
- // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
- // copy over the arrays.
- DCHECK(dex_file != nullptr);
- const size_t num_strings = dex_file->NumStringIds();
- const size_t num_types = dex_file->NumTypeIds();
- const size_t num_methods = dex_file->NumMethodIds();
- const size_t num_fields = dex_file->NumFieldIds();
- CHECK_EQ(num_strings, dex_cache->NumStrings());
- CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
- CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
- CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
- DexCacheArraysLayout layout(image_pointer_size_, dex_file);
- uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
- // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n.
- if (num_strings != 0u) {
- GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
- GcRoot<mirror::String>* const strings =
- reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
- DCHECK(strings[j].IsNull());
- }
- std::copy_n(image_resolved_strings, num_strings, strings);
- dex_cache->SetStrings(strings);
- }
- if (num_types != 0u) {
- GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
- GcRoot<mirror::Class>* const types =
- reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
- DCHECK(types[j].IsNull());
- }
- std::copy_n(image_resolved_types, num_types, types);
- // Store a pointer to the new location for fast ArtMethod patching without requiring map.
- // This leaves random garbage at the start of the dex cache array, but nobody should ever
- // read from it again.
- *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types;
- dex_cache->SetResolvedTypes(types);
- }
- if (num_methods != 0u) {
- ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
- raw_arrays + layout.MethodsOffset());
- ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
- for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
- DCHECK(methods[j] == nullptr);
- }
- std::copy_n(image_resolved_methods, num_methods, methods);
- // Store a pointer to the new location for fast ArtMethod patching without requiring map.
- *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods;
- dex_cache->SetResolvedMethods(methods);
- }
- if (num_fields != 0u) {
- ArtField** const fields = reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
- DCHECK(fields[j] == nullptr);
- }
- std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
- dex_cache->SetResolvedFields(fields);
+ {
+ // Add image classes into the class table for the class loader, and fixup the dex caches and
+ // class loader fields.
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
+ // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
+ // rely on clobering the dex cache arrays in the image to forward to bss.
+ size_t num_dex_caches_with_bss_arrays = 0;
+ const size_t num_dex_caches = dex_caches->GetLength();
+ for (size_t i = 0; i < num_dex_caches; i++) {
+ mirror::DexCache* const dex_cache = dex_caches->Get(i);
+ const DexFile* const dex_file = dex_cache->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+ if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
+ ++num_dex_caches_with_bss_arrays;
}
}
- {
- WriterMutexLock mu2(self, dex_lock_);
- // Make sure to do this after we update the arrays since we store the resolved types array
- // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
- // BSS.
- mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self,
- *dex_file,
- /*allow_failure*/true);
- CHECK(existing_dex_cache == nullptr);
- StackHandleScope<1> hs3(self);
- RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache));
+ *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
+ if (*out_forward_dex_cache_array) {
+ if (num_dex_caches_with_bss_arrays != num_dex_caches) {
+ // Reject application image since we cannot forward only some of the dex cache arrays.
+ // TODO: We could get around this by having a dedicated forwarding slot. It should be an
+ // uncommon case.
+ *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
+ num_dex_caches_with_bss_arrays,
+ num_dex_caches);
+ return false;
+ }
}
- GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
- const size_t num_types = dex_cache->NumResolvedTypes();
- if (new_class_set == nullptr) {
- for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
- // The image space is not yet added to the heap, avoid read barriers.
- mirror::Class* klass = types[j].Read();
- if (klass != nullptr) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
- // Update the class loader from the one in the image class loader to the one that loaded
- // the app image.
- klass->SetClassLoader(class_loader.Get());
- // The resolved type could be from another dex cache, go through the dex cache just in
- // case. May be null for array classes.
- if (klass->GetDexCacheStrings() != nullptr) {
- DCHECK(!klass->IsArrayClass());
- klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
+ // Only add the classes to the class loader after the points where we can return false.
+ for (size_t i = 0; i < num_dex_caches; i++) {
+ mirror::DexCache* const dex_cache = dex_caches->Get(i);
+ const DexFile* const dex_file = dex_cache->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+ if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
+ // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
+ // copy over the arrays.
+ DCHECK(dex_file != nullptr);
+ const size_t num_strings = dex_file->NumStringIds();
+ const size_t num_types = dex_file->NumTypeIds();
+ const size_t num_methods = dex_file->NumMethodIds();
+ const size_t num_fields = dex_file->NumFieldIds();
+ CHECK_EQ(num_strings, dex_cache->NumStrings());
+ CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
+ CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
+ CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
+ DexCacheArraysLayout layout(image_pointer_size_, dex_file);
+ uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
+ // The space is not yet visible to the GC, we can avoid the read barriers and use
+ // std::copy_n.
+ if (num_strings != 0u) {
+ GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
+ GcRoot<mirror::String>* const strings =
+ reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
+ for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
+ DCHECK(strings[j].IsNull());
}
- // If there are multiple dex caches, there may be the same class multiple times
- // in different dex caches. Check for this since inserting will add duplicates
- // otherwise.
- if (num_dex_caches > 1) {
- mirror::Class* existing = table->LookupByDescriptor(klass);
- if (existing != nullptr) {
- DCHECK_EQ(existing, klass) << PrettyClass(klass);
+ std::copy_n(image_resolved_strings, num_strings, strings);
+ dex_cache->SetStrings(strings);
+ }
+ if (num_types != 0u) {
+ GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
+ GcRoot<mirror::Class>* const types =
+ reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+ for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
+ DCHECK(types[j].IsNull());
+ }
+ std::copy_n(image_resolved_types, num_types, types);
+ // Store a pointer to the new location for fast ArtMethod patching without requiring map.
+ // This leaves random garbage at the start of the dex cache array, but nobody should ever
+ // read from it again.
+ *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types;
+ dex_cache->SetResolvedTypes(types);
+ }
+ if (num_methods != 0u) {
+ ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
+ raw_arrays + layout.MethodsOffset());
+ ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
+ for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
+ DCHECK(methods[j] == nullptr);
+ }
+ std::copy_n(image_resolved_methods, num_methods, methods);
+ // Store a pointer to the new location for fast ArtMethod patching without requiring map.
+ *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods;
+ dex_cache->SetResolvedMethods(methods);
+ }
+ if (num_fields != 0u) {
+ ArtField** const fields =
+ reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+ for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
+ DCHECK(fields[j] == nullptr);
+ }
+ std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
+ dex_cache->SetResolvedFields(fields);
+ }
+ }
+ {
+ WriterMutexLock mu2(self, dex_lock_);
+ // Make sure to do this after we update the arrays since we store the resolved types array
+ // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
+ // BSS.
+ mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self,
+ *dex_file,
+ /*allow_failure*/true);
+ CHECK(existing_dex_cache == nullptr);
+ StackHandleScope<1> hs3(self);
+ RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache));
+ }
+ GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
+ const size_t num_types = dex_cache->NumResolvedTypes();
+ if (new_class_set == nullptr) {
+ for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+ // The image space is not yet added to the heap, avoid read barriers.
+ mirror::Class* klass = types[j].Read();
+ if (klass != nullptr) {
+ DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ // Update the class loader from the one in the image class loader to the one that loaded
+ // the app image.
+ klass->SetClassLoader(class_loader.Get());
+ // The resolved type could be from another dex cache, go through the dex cache just in
+ // case. May be null for array classes.
+ if (klass->GetDexCacheStrings() != nullptr) {
+ DCHECK(!klass->IsArrayClass());
+ klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
+ }
+ // If there are multiple dex caches, there may be the same class multiple times
+ // in different dex caches. Check for this since inserting will add duplicates
+ // otherwise.
+ if (num_dex_caches > 1) {
+ mirror::Class* existing = table->LookupByDescriptor(klass);
+ if (existing != nullptr) {
+ DCHECK_EQ(existing, klass) << PrettyClass(klass);
+ } else {
+ table->Insert(klass);
+ }
} else {
table->Insert(klass);
}
- } else {
- table->Insert(klass);
- }
- // Double checked VLOG to avoid overhead.
- if (VLOG_IS_ON(image)) {
- VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus();
- if (!klass->IsArrayClass()) {
- VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation();
- }
- VLOG(image) << "Direct methods";
- for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
- VLOG(image) << PrettyMethod(&m);
- }
- VLOG(image) << "Virtual methods";
- for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
- VLOG(image) << PrettyMethod(&m);
+ // Double checked VLOG to avoid overhead.
+ if (VLOG_IS_ON(image)) {
+ VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus();
+ if (!klass->IsArrayClass()) {
+ VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation();
+ }
+ VLOG(image) << "Direct methods";
+ for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
+ VLOG(image) << PrettyMethod(&m);
+ }
+ VLOG(image) << "Virtual methods";
+ for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
+ VLOG(image) << PrettyMethod(&m);
+ }
}
}
}
}
- }
- if (kIsDebugBuild) {
- for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
- // The image space is not yet added to the heap, avoid read barriers.
- mirror::Class* klass = types[j].Read();
- if (klass != nullptr) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
- if (kIsDebugBuild) {
- if (new_class_set != nullptr) {
- auto it = new_class_set->Find(GcRoot<mirror::Class>(klass));
- DCHECK(it != new_class_set->end());
- DCHECK_EQ(it->Read(), klass);
- mirror::Class* super_class = klass->GetSuperClass();
- if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
- auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class));
- DCHECK(it2 != new_class_set->end());
- DCHECK_EQ(it2->Read(), super_class);
- }
- } else {
- DCHECK_EQ(table->LookupByDescriptor(klass), klass);
- mirror::Class* super_class = klass->GetSuperClass();
- if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
- CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
+ if (kIsDebugBuild) {
+ for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+ // The image space is not yet added to the heap, avoid read barriers.
+ mirror::Class* klass = types[j].Read();
+ if (klass != nullptr) {
+ DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ if (kIsDebugBuild) {
+ if (new_class_set != nullptr) {
+ auto it = new_class_set->Find(GcRoot<mirror::Class>(klass));
+ DCHECK(it != new_class_set->end());
+ DCHECK_EQ(it->Read(), klass);
+ mirror::Class* super_class = klass->GetSuperClass();
+ if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
+ auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class));
+ DCHECK(it2 != new_class_set->end());
+ DCHECK_EQ(it2->Read(), super_class);
+ }
+ } else {
+ DCHECK_EQ(table->LookupByDescriptor(klass), klass);
+ mirror::Class* super_class = klass->GetSuperClass();
+ if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
+ CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
+ }
}
}
- }
- if (kIsDebugBuild) {
- for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
- const void* code = m.GetEntryPointFromQuickCompiledCode();
- const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
- if (!IsQuickResolutionStub(code) &&
- !IsQuickGenericJniStub(code) &&
- !IsQuickToInterpreterBridge(code) &&
- !m.IsNative()) {
- DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+ if (kIsDebugBuild) {
+ for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
+ const void* code = m.GetEntryPointFromQuickCompiledCode();
+ const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
+ if (!IsQuickResolutionStub(code) &&
+ !IsQuickGenericJniStub(code) &&
+ !IsQuickToInterpreterBridge(code) &&
+ !m.IsNative()) {
+ DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+ }
}
- }
- for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
- const void* code = m.GetEntryPointFromQuickCompiledCode();
- const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
- if (!IsQuickResolutionStub(code) &&
- !IsQuickGenericJniStub(code) &&
- !IsQuickToInterpreterBridge(code) &&
- !m.IsNative()) {
- DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+ for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
+ const void* code = m.GetEntryPointFromQuickCompiledCode();
+ const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
+ if (!IsQuickResolutionStub(code) &&
+ !IsQuickGenericJniStub(code) &&
+ !IsQuickToInterpreterBridge(code) &&
+ !m.IsNative()) {
+ DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
+ }
}
}
}
}
}
}
- }
- if (*out_forward_dex_cache_array) {
- FixupArtMethodArrayVisitor visitor(header);
- header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &visitor,
- space->Begin(),
- sizeof(void*));
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
+ if (*out_forward_dex_cache_array) {
+ ScopedTrace timing("Fixup ArtMethod dex cache arrays");
+ FixupArtMethodArrayVisitor visitor(header);
+ header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
+ &visitor,
+ space->Begin(),
+ sizeof(void*));
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
+ }
}
return true;
}
@@ -7709,7 +7714,10 @@
}
++num_resolved;
DCHECK(!klass->IsProxyClass());
- DCHECK(klass->IsResolved());
+ if (!klass->IsResolved()) {
+ DCHECK(klass->IsErroneous());
+ continue;
+ }
mirror::DexCache* klass_dex_cache = klass->GetDexCache();
if (klass_dex_cache == dex_cache) {
const size_t class_def_idx = klass->GetDexClassDefIndex();
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 3df9101..729957f 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -406,6 +406,7 @@
int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
ASSERT_EQ(0, rmdir_cache_result);
TearDownAndroidData(android_data_, true);
+ dalvik_cache_.clear();
// icu4c has a fixed 10-element array "gCommonICUDataArray".
// If we run > 10 tests, we fill that array and u_setCommonData fails.
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 7727b2d..6beb606 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -131,7 +131,7 @@
class BitmapSetSlowPathVisitor {
public:
- void operator()(const mirror::Object* obj) const {
+ void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
// Marking a large object, make sure its aligned as a sanity check.
if (!IsAligned<kPageSize>(obj)) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 01dff19..2e5b599 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1547,7 +1547,6 @@
}
void Heap::DumpSpaces(std::ostream& stream) const {
- ScopedObjectAccess soa(Thread::Current());
for (const auto& space : continuous_spaces_) {
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 889069d..e0a53a0 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -651,8 +651,8 @@
}
}
- std::string DumpSpaces() const WARN_UNUSED;
- void DumpSpaces(std::ostream& stream) const;
+ void DumpSpaces(std::ostream& stream) const SHARED_REQUIRES(Locks::mutator_lock_);
+ std::string DumpSpaces() const SHARED_REQUIRES(Locks::mutator_lock_);
// Dump object should only be used by the signal handler.
void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index a4e5587..9ecd391 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1535,50 +1535,31 @@
// images[0] is f/c/d/e.art
// ----------------------------------------------
// images[1] is g/h/i/j.art -> /a/b/h/i/j.art
-
- // Derive pattern.
- std::vector<std::string> left;
- Split(input_image_file_name, '/', &left);
- std::vector<std::string> right;
- Split(images[0], '/', &right);
-
- size_t common = 1;
- while (common < left.size() && common < right.size()) {
- if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
- break;
- }
- common++;
+ const std::string& first_image = images[0];
+ // Length of common suffix.
+ size_t common = 0;
+ while (common < input_image_file_name.size() &&
+ common < first_image.size() &&
+ *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) {
+ ++common;
}
-
- std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
- std::string common_prefix = Join(prefix_vector, '/');
- if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') {
- common_prefix = "/" + common_prefix;
- }
+ // We want to replace the prefix of the input image with the prefix of the boot class path.
+ // This handles the case where the image file contains @ separators.
+ // Example image_file_name is oats/system@framework@boot.art
+ // images[0] is .../arm/boot.art
+ // means that the image name prefix will be oats/system@framework@
+ // so that the other images are openable.
+ const size_t old_prefix_length = first_image.size() - common;
+ const std::string new_prefix = input_image_file_name.substr(
+ 0,
+ input_image_file_name.size() - common);
// Apply pattern to images[1] .. images[n].
for (size_t i = 1; i < images.size(); ++i) {
- std::string image = images[i];
-
- size_t rslash = std::string::npos;
- for (size_t j = 0; j < common; ++j) {
- if (rslash != std::string::npos) {
- rslash--;
- }
-
- rslash = image.rfind('/', rslash);
- if (rslash == std::string::npos) {
- rslash = 0;
- }
- if (rslash == 0) {
- break;
- }
- }
- std::string image_part = image.substr(rslash);
-
- std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
- image_part;
- image_file_names->push_back(new_image);
+ const std::string& image = images[i];
+ CHECK_GT(image.length(), old_prefix_length);
+ std::string suffix = image.substr(old_prefix_length);
+ image_file_names->push_back(new_prefix + suffix);
}
}
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index e70fe21..010f677 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -27,6 +27,7 @@
#include "base/stl_util.h"
#include "image.h"
#include "os.h"
+#include "scoped_thread_state_change.h"
#include "space-inl.h"
#include "thread-inl.h"
@@ -190,6 +191,7 @@
MutexLock mu(self, lock_);
auto it = large_objects_.find(ptr);
if (UNLIKELY(it == large_objects_.end())) {
+ ScopedObjectAccess soa(self);
Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL));
LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
}
diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S
index ce09da45..66627e2 100644
--- a/runtime/interpreter/mterp/mips/binop.S
+++ b/runtime/interpreter/mterp/mips/binop.S
@@ -7,8 +7,8 @@
*
* If "chkzero" is set to 1, we perform a divide-by-zero check on
* vCC (a1). Useful for integer division and modulus. Note that we
- * *don't* check for (INT_MIN / -1) here, because the ARM math lib
- * handles it correctly.
+ * *don't* check for (INT_MIN / -1) here, because the CPU handles it
+ * correctly.
*
* For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
* xor-int, shl-int, shr-int, ushr-int
diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S
index d39c900..aa5e74b 100644
--- a/runtime/interpreter/mterp/mips64/bincmp.S
+++ b/runtime/interpreter/mterp/mips64/bincmp.S
@@ -6,27 +6,27 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
b${condition}c a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S
index 1a2e22b..14d5fe0 100644
--- a/runtime/interpreter/mterp/mips64/footer.S
+++ b/runtime/interpreter/mterp/mips64/footer.S
@@ -49,6 +49,7 @@
*
*/
.extern MterpHandleException
+ .extern MterpShouldSwitchInterpreters
MterpException:
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -59,8 +60,11 @@
REFRESH_IBASE
daddu rPC, a0, CODEITEM_INSNS_OFFSET
dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr
- sd rPC, OFF_FP_DEX_PC_PTR(rFP)
+ /* Do we need to switch interpreters? */
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -81,10 +85,24 @@
EXPORT_PC
move a0, rSELF
jal MterpSuspendCheck # (self)
+ bnezc v0, MterpFallback # Something in the environment changed, switch interpreters
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST # rINST contains offset
+ jal MterpLogOSR
+#endif
+ li v0, 1 # Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
.extern MterpLogFallback
diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S
index 4c3ca9e..dd0fbe0 100644
--- a/runtime/interpreter/mterp/mips64/header.S
+++ b/runtime/interpreter/mterp/mips64/header.S
@@ -82,14 +82,7 @@
#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
+#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S
index 4ae4fb1..be647b6 100644
--- a/runtime/interpreter/mterp/mips64/invoke.S
+++ b/runtime/interpreter/mterp/mips64/invoke.S
@@ -5,6 +5,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern $helper
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -13,5 +14,7 @@
jal $helper
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S
index f2df3e4..7c7d0ec 100644
--- a/runtime/interpreter/mterp/mips64/op_goto.S
+++ b/runtime/interpreter/mterp/mips64/op_goto.S
@@ -5,19 +5,21 @@
* double to get a byte offset.
*/
/* goto +AA */
- srl a0, rINST, 8
- seb a0, a0 # a0 <- sign-extended AA
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a0, 1f # AA * 2 >= 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a0, MterpCheckSuspendAndContinue
+ .extern MterpProfileBranch
+ srl rINST, rINST, 8
+ seb rINST, rINST # rINST <- offset (sign-extended AA)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S
index cbf8cf2..566e3a7 100644
--- a/runtime/interpreter/mterp/mips64/op_goto_16.S
+++ b/runtime/interpreter/mterp/mips64/op_goto_16.S
@@ -5,18 +5,20 @@
* double to get a byte offset.
*/
/* goto/16 +AAAA */
- lh a0, 2(rPC) # a0 <- sign-extended AAAA
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a0, 1f # AA * 2 >= 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a0, MterpCheckSuspendAndContinue
+ .extern MterpProfileBranch
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S
index 4a1feac..b260083 100644
--- a/runtime/interpreter/mterp/mips64/op_goto_32.S
+++ b/runtime/interpreter/mterp/mips64/op_goto_32.S
@@ -8,20 +8,22 @@
* our "backward branch" test must be "<=0" instead of "<0".
*/
/* goto/32 +AAAAAAAA */
- lh a0, 2(rPC) # a0 <- aaaa (low)
+ .extern MterpProfileBranch
+ lh rINST, 2(rPC) # rINST <- aaaa (low)
lh a1, 4(rPC) # a1 <- AAAA (high)
- ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgtz a0, 1f # AA * 2 > 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- blez a0, MterpCheckSuspendAndContinue
+ ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S
index cdbdf75..2c6eb2f 100644
--- a/runtime/interpreter/mterp/mips64/op_packed_switch.S
+++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S
@@ -10,6 +10,7 @@
*/
/* op vAA, +BBBBBBBB */
.extern $func
+ .extern MterpProfileBranch
lh a0, 2(rPC) # a0 <- bbbb (lo)
lh a1, 4(rPC) # a1 <- BBBB (hi)
srl a3, rINST, 8 # a3 <- AA
@@ -17,15 +18,19 @@
GET_VREG a1, a3 # a1 <- vAA
dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2
jal $func # v0 <- code-unit branch offset
- dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2
- FETCH_INST # load rINST
-#if MTERP_SUSPEND
- bgtz v0, 1f # offset * 2 > 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- blez v0, MterpCheckSuspendAndContinue
+ move rINST, v0
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S
index d7ad894..0e0477f 100644
--- a/runtime/interpreter/mterp/mips64/zcmp.S
+++ b/runtime/interpreter/mterp/mips64/zcmp.S
@@ -6,25 +6,25 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
b${condition}zc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index ca727f4..10b19c5 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -147,16 +147,7 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
const instrumentation::Instrumentation* const instrumentation =
Runtime::Current()->GetInstrumentation();
- bool unhandled_instrumentation;
- // TODO: enable for other targets after more extensive testing.
- if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) ||
- (kRuntimeISA == kX86_64) || (kRuntimeISA == kX86) ||
- (kRuntimeISA == kMips)) {
- unhandled_instrumentation = instrumentation->NonJitProfilingActive();
- } else {
- unhandled_instrumentation = instrumentation->IsActive();
- }
- return unhandled_instrumentation || Dbg::IsDebuggerActive();
+ return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive();
}
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 7cef823..a17252b 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -89,14 +89,7 @@
#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
+#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
@@ -1107,20 +1100,22 @@
* double to get a byte offset.
*/
/* goto +AA */
- srl a0, rINST, 8
- seb a0, a0 # a0 <- sign-extended AA
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a0, 1f # AA * 2 >= 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a0, MterpCheckSuspendAndContinue
+ .extern MterpProfileBranch
+ srl rINST, rINST, 8
+ seb rINST, rINST # rINST <- offset (sign-extended AA)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1135,19 +1130,21 @@
* double to get a byte offset.
*/
/* goto/16 +AAAA */
- lh a0, 2(rPC) # a0 <- sign-extended AAAA
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a0, 1f # AA * 2 >= 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a0, MterpCheckSuspendAndContinue
+ .extern MterpProfileBranch
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1165,21 +1162,23 @@
* our "backward branch" test must be "<=0" instead of "<0".
*/
/* goto/32 +AAAAAAAA */
- lh a0, 2(rPC) # a0 <- aaaa (low)
+ .extern MterpProfileBranch
+ lh rINST, 2(rPC) # rINST <- aaaa (low)
lh a1, 4(rPC) # a1 <- AAAA (high)
- ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa
- dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgtz a0, 1f # AA * 2 > 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- blez a0, MterpCheckSuspendAndContinue
+ ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1198,6 +1197,7 @@
*/
/* op vAA, +BBBBBBBB */
.extern MterpDoPackedSwitch
+ .extern MterpProfileBranch
lh a0, 2(rPC) # a0 <- bbbb (lo)
lh a1, 4(rPC) # a1 <- BBBB (hi)
srl a3, rINST, 8 # a3 <- AA
@@ -1205,16 +1205,20 @@
GET_VREG a1, a3 # a1 <- vAA
dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2
jal MterpDoPackedSwitch # v0 <- code-unit branch offset
- dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2
- FETCH_INST # load rINST
-#if MTERP_SUSPEND
- bgtz v0, 1f # offset * 2 > 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- blez v0, MterpCheckSuspendAndContinue
+ move rINST, v0
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1234,6 +1238,7 @@
*/
/* op vAA, +BBBBBBBB */
.extern MterpDoSparseSwitch
+ .extern MterpProfileBranch
lh a0, 2(rPC) # a0 <- bbbb (lo)
lh a1, 4(rPC) # a1 <- BBBB (hi)
srl a3, rINST, 8 # a3 <- AA
@@ -1241,16 +1246,20 @@
GET_VREG a1, a3 # a1 <- vAA
dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2
jal MterpDoSparseSwitch # v0 <- code-unit branch offset
- dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2
- FETCH_INST # load rINST
-#if MTERP_SUSPEND
- bgtz v0, 1f # offset * 2 > 0 => no suspend check
- REFRESH_IBASE
-1:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- blez v0, MterpCheckSuspendAndContinue
+ move rINST, v0
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1438,28 +1447,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
beqc a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1477,28 +1486,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
bnec a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1516,28 +1525,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
bltc a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1555,28 +1564,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
bgec a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1594,28 +1603,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
bgtc a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1633,28 +1642,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
- lh a4, 2(rPC) # a4 <- sign-extended CCCC
+ .extern MterpProfileBranch
ext a2, rINST, 8, 4 # a2 <- A
ext a3, rINST, 12, 4 # a3 <- B
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC)
GET_VREG a0, a2 # a0 <- vA
GET_VREG a1, a3 # a1 <- vB
-
blec a0, a1, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # CCCC * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1672,26 +1681,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
beqzc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1709,26 +1718,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
bnezc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1746,26 +1755,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
bltzc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1783,26 +1792,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
bgezc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1820,26 +1829,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
bgtzc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -1857,26 +1866,26 @@
* For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
- lh a4, 2(rPC) # a4 <- sign-extended BBBB
+ .extern MterpProfileBranch
srl a2, rINST, 8 # a2 <- AA
+ lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB)
GET_VREG a0, a2 # a0 <- vAA
-
blezc a0, 1f
- li a4, 2 # offset if branch not taken
+ li rINST, 2 # offset if branch not taken
1:
-
- dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2
- FETCH_INST # load rINST
-
-#if MTERP_SUSPEND
- bgez a4, 2f # BBBB * 2 >= 0 => no suspend check
- REFRESH_IBASE
-2:
-#else
- lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
- bltz a4, MterpCheckSuspendAndContinue
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST
+ jal MterpProfileBranch # (self, shadow_frame, offset)
+ bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST
#endif
-
+ dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2
+ lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue
+ move a0, rINST # a0 <- offset
+ FETCH_INST # load rINST
+ bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
@@ -3166,6 +3175,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeVirtual
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3174,6 +3184,8 @@
jal MterpInvokeVirtual
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3196,6 +3208,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeSuper
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3204,6 +3217,8 @@
jal MterpInvokeSuper
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3226,6 +3241,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeDirect
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3234,6 +3250,8 @@
jal MterpInvokeDirect
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3249,6 +3267,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeStatic
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3257,6 +3276,8 @@
jal MterpInvokeStatic
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3272,6 +3293,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeInterface
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3280,6 +3302,8 @@
jal MterpInvokeInterface
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3316,6 +3340,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeVirtualRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3324,6 +3349,8 @@
jal MterpInvokeVirtualRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3339,6 +3366,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeSuperRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3347,6 +3375,8 @@
jal MterpInvokeSuperRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3362,6 +3392,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeDirectRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3370,6 +3401,8 @@
jal MterpInvokeDirectRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3385,6 +3418,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeStaticRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3393,6 +3427,8 @@
jal MterpInvokeStaticRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -3408,6 +3444,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeInterfaceRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -3416,6 +3453,8 @@
jal MterpInvokeInterfaceRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -6962,6 +7001,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeVirtualQuick
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -6970,6 +7010,8 @@
jal MterpInvokeVirtualQuick
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -6985,6 +7027,7 @@
/* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
/* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
.extern MterpInvokeVirtualQuickRange
+ .extern MterpShouldSwitchInterpreters
EXPORT_PC
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -6993,6 +7036,8 @@
jal MterpInvokeVirtualQuickRange
beqzc v0, MterpException
FETCH_ADVANCE_INST 3
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -12256,6 +12301,7 @@
*
*/
.extern MterpHandleException
+ .extern MterpShouldSwitchInterpreters
MterpException:
move a0, rSELF
daddu a1, rFP, OFF_FP_SHADOWFRAME
@@ -12266,8 +12312,11 @@
REFRESH_IBASE
daddu rPC, a0, CODEITEM_INSNS_OFFSET
dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr
- sd rPC, OFF_FP_DEX_PC_PTR(rFP)
+ /* Do we need to switch interpreters? */
+ jal MterpShouldSwitchInterpreters
+ bnezc v0, MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE v0
GOTO_OPCODE v0
@@ -12288,10 +12337,24 @@
EXPORT_PC
move a0, rSELF
jal MterpSuspendCheck # (self)
+ bnezc v0, MterpFallback # Something in the environment changed, switch interpreters
GET_INST_OPCODE v0 # extract opcode from rINST
GOTO_OPCODE v0 # jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ move a0, rSELF
+ daddu a1, rFP, OFF_FP_SHADOWFRAME
+ move a2, rINST # rINST contains offset
+ jal MterpLogOSR
+#endif
+ li v0, 1 # Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
.extern MterpLogFallback
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index a3b99e3..7dbd89c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -188,9 +188,11 @@
}
void Jit::StartProfileSaver(const std::string& filename,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (save_profiling_info_) {
- ProfileSaver::Start(filename, code_cache_.get(), code_paths);
+ ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 3f54192..ee416d8 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -70,7 +70,17 @@
return instrumentation_cache_.get();
}
- void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths);
+ // Starts the profile saver if the config options allow profile recording.
+ // The profile will be stored in the specified `filename` and will contain
+ // information collected from the given `code_paths` (a set of dex locations).
+ // The `foreign_dex_profile_path` is the path where the saver will put the
+ // profile markers for loaded dex files which are not owned by the application.
+ // The `app_dir` is the application directory and is used to decide which
+ // dex files belong to the application.
+ void StartProfileSaver(const std::string& filename,
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
void StopProfileSaver();
void DumpForSigQuit(std::ostream& os) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4f87e5b..050bb68 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -169,12 +169,16 @@
return false;
}
-class ScopedCodeCacheWrite {
+class ScopedCodeCacheWrite : ScopedTrace {
public:
- explicit ScopedCodeCacheWrite(MemMap* code_map) : code_map_(code_map) {
+ explicit ScopedCodeCacheWrite(MemMap* code_map)
+ : ScopedTrace("ScopedCodeCacheWrite"),
+ code_map_(code_map) {
+ ScopedTrace trace("mprotect all");
CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtAll);
}
~ScopedCodeCacheWrite() {
+ ScopedTrace trace("mprotect code");
CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode);
}
private:
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index bd58157..6fe17db 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -16,6 +16,10 @@
#include "profile_saver.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
#include "art_method-inl.h"
#include "base/systrace.h"
#include "scoped_thread_state_change.h"
@@ -43,14 +47,31 @@
ProfileSaver::ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
+ foreign_dex_profile_path_(foreign_dex_profile_path),
code_cache_last_update_time_ns_(0),
shutting_down_(false),
first_profile_(true),
wait_lock_("ProfileSaver wait lock"),
period_condition_("ProfileSaver period condition", wait_lock_) {
AddTrackedLocations(output_filename, code_paths);
+ app_data_dir_ = "";
+ if (!app_data_dir.empty()) {
+ // The application directory is used to determine which dex files are owned by app.
+ // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
+ // don't have control over how the dex files are actually loaded (symlink or canonical path),
+ // store it's canonical form to be sure we use the same base when comparing.
+ UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
+ if (app_data_dir_real_path != nullptr) {
+ app_data_dir_.assign(app_data_dir_real_path.get());
+ } else {
+ LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+ << ". The app dir will not be used to determine which dex files belong to the app";
+ }
+ }
}
void ProfileSaver::Run() {
@@ -164,7 +185,9 @@
void ProfileSaver::Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
DCHECK(Runtime::Current()->UseJit());
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
@@ -183,7 +206,11 @@
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
<< ". Tracking: " << Join(code_paths, ':');
- instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths);
+ instance_ = new ProfileSaver(output_filename,
+ jit_code_cache,
+ code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
// Create a new thread which does the saving.
CHECK_PTHREAD_CALL(
@@ -250,4 +277,97 @@
}
}
+void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+ std::set<std::string> app_code_paths;
+ std::string foreign_dex_profile_path;
+ std::string app_data_dir;
+ {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ DCHECK(instance_ != nullptr);
+ // Make a copy so that we don't hold the lock while doing I/O.
+ for (const auto& it : instance_->tracked_dex_base_locations_) {
+ app_code_paths.insert(it.second.begin(), it.second.end());
+ }
+ foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
+ app_data_dir = instance_->app_data_dir_;
+ }
+
+ MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
+}
+
+void ProfileSaver::MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& app_code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
+ if (dex_location.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
+ return;
+ }
+ if (foreign_dex_profile_path.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
+ return;
+ }
+
+ UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
+ if (dex_location_real_path == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << dex_location;
+ }
+ std::string dex_location_real_path_str((dex_location_real_path == nullptr)
+ ? dex_location.c_str()
+ : dex_location_real_path.get());
+
+ if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
+ // The dex location is under the application folder. Nothing to record.
+ return;
+ }
+
+ if (app_code_paths.find(dex_location) != app_code_paths.end()) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ // Do another round of checks with the real paths.
+ // Note that we could cache all the real locations in the saver (since it's an expensive
+ // operation). However we expect that app_code_paths is small (usually 1 element), and
+ // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
+ // to save some bytes of memory usage.
+ for (const auto& app_code_location : app_code_paths) {
+ UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr));
+ if (real_app_code_location == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << app_code_location;
+ }
+ std::string real_app_code_location_str((real_app_code_location == nullptr)
+ ? app_code_location.c_str()
+ : real_app_code_location.get());
+ if (real_app_code_location_str == dex_location_real_path_str) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ }
+
+ // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
+ // into account when deciding how to optimize the loaded dex file.
+ // The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
+ // (it needs to be kept in sync with
+ // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
+ std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@');
+ std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str;
+ // No need to give any sort of access to flag_path. The system has enough permissions
+ // to test for its existence.
+ int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0));
+ if (fd != -1) {
+ if (close(fd) != 0) {
+ PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
+ }
+ } else {
+ if (errno != EEXIST) {
+ // Another app could have already created the file.
+ PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 21017c1..e7eab95 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -30,7 +30,9 @@
// If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
static void Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Stops the profile saver thread.
@@ -42,10 +44,14 @@
// Returns true if the profile saver is started.
static bool IsStarted() REQUIRES(!Locks::profiler_lock_);
+ static void NotifyDexUse(const std::string& dex_location);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths);
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
static void* RunProfileSaverThread(void* arg)
@@ -64,6 +70,12 @@
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
+ static void MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& tracked_locations,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
+
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
// Profile saver thread.
@@ -72,6 +84,8 @@
jit::JitCodeCache* jit_code_cache_;
SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
GUARDED_BY(Locks::profiler_lock_);
+ std::string foreign_dex_profile_path_;
+ std::string app_data_dir_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
bool first_profile_ = true;
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 11156c6..421641c 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -590,7 +590,19 @@
}
bool MemMap::Sync() {
- return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+ bool result;
+ if (redzone_size_ != 0) {
+ // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing
+ // it to msync() as it only accepts page-aligned base address, and exclude the higher-end
+ // noaccess protection from the msync range. b/27552451.
+ uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
+ MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin);
+ result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0;
+ MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin);
+ } else {
+ result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+ }
+ return result;
}
bool MemMap::Protect(int prot) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 103a8b7..19584ed 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -29,7 +29,6 @@
#include "dex_cache.h"
#include "dex_file.h"
#include "gc/heap-inl.h"
-#include "jit/profiling_info.h"
#include "iftable.h"
#include "object_array-inl.h"
#include "read_barrier-inl.h"
@@ -940,12 +939,6 @@
}
for (ArtMethod& method : GetMethods(pointer_size)) {
method.VisitRoots(visitor, pointer_size);
- if (method.GetDeclaringClassUnchecked() != nullptr && !method.IsNative()) {
- ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size);
- if (profiling_info != nullptr) {
- profiling_info->VisitRoots(visitor);
- }
- }
}
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 6643ac2..f1e0fa7 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -347,15 +347,14 @@
static jint GetDexOptNeeded(JNIEnv* env,
const char* filename,
- const char* pkgname,
const char* instruction_set,
- const jboolean defer) {
+ const int target_compilation_type_mask) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
const char* message = (filename == nullptr) ? "<empty file name>" : filename;
env->ThrowNew(fnfe.get(), message);
- return OatFileAssistant::kNoDexOptNeeded;
+ return -1;
}
const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
@@ -363,73 +362,52 @@
ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
env->ThrowNew(iae.get(), message.c_str());
- return 0;
+ return -1;
}
// TODO: Verify the dex location is well formed, and throw an IOException if
// not?
-
- OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);
+ OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask,
+ target_instruction_set, false);
// Always treat elements of the bootclasspath as up-to-date.
if (oat_file_assistant.IsInBootClassPath()) {
return OatFileAssistant::kNoDexOptNeeded;
}
- // TODO: Checking the profile should probably be done in the GetStatus()
- // function. We have it here because GetStatus() should not be copying
- // profile files. But who should be copying profile files?
- if (oat_file_assistant.OdexFileIsOutOfDate()) {
- // Needs recompile if profile has changed significantly.
- if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
- if (oat_file_assistant.IsProfileChangeSignificant()) {
- if (!defer) {
- oat_file_assistant.CopyProfileFile();
- }
- return OatFileAssistant::kDex2OatNeeded;
- } else if (oat_file_assistant.ProfileExists()
- && !oat_file_assistant.OldProfileExists()) {
- if (!defer) {
- oat_file_assistant.CopyProfileFile();
- }
- }
- }
- }
-
return oat_file_assistant.GetDexOptNeeded();
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
jclass,
jstring javaFilename,
- jstring javaPkgname,
jstring javaInstructionSet,
- jboolean defer) {
+ jint javaTargetCompilationTypeMask) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
- return 0;
+ return -1;
}
- NullableScopedUtfChars pkgname(env, javaPkgname);
-
ScopedUtfChars instruction_set(env, javaInstructionSet);
if (env->ExceptionCheck()) {
- return 0;
+ return -1;
}
return GetDexOptNeeded(env,
filename.c_str(),
- pkgname.c_str(),
instruction_set.c_str(),
- defer);
+ javaTargetCompilationTypeMask);
}
-// public API, null pkgname
+// public API
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
const char* instruction_set = GetInstructionSetString(kRuntimeISA);
ScopedUtfChars filename(env, javaFilename);
- jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */,
- instruction_set, false /* defer */);
+ jint status = GetDexOptNeeded(
+ env,
+ filename.c_str(),
+ instruction_set,
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation);
return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
}
@@ -445,7 +423,7 @@
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
+ "(Ljava/lang/String;Ljava/lang/String;I)I"),
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index da4a891..f6b2f21 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -566,8 +566,9 @@
static void VMRuntime_registerAppInfo(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED,
jstring profile_file,
- jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument
- jobjectArray code_paths) {
+ jstring app_dir,
+ jobjectArray code_paths,
+ jstring foreign_dex_profile_path) {
std::vector<std::string> code_paths_vec;
int code_paths_length = env->GetArrayLength(code_paths);
for (int i = 0; i < code_paths_length; i++) {
@@ -581,7 +582,22 @@
std::string profile_file_str(raw_profile_file);
env->ReleaseStringUTFChars(profile_file, raw_profile_file);
- Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str);
+ std::string foreign_dex_profile_path_str = "";
+ if (foreign_dex_profile_path != nullptr) {
+ const char* raw_foreign_dex_profile_path =
+ env->GetStringUTFChars(foreign_dex_profile_path, nullptr);
+ foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path);
+ env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path);
+ }
+
+ const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr);
+ std::string app_dir_str(raw_app_dir);
+ env->ReleaseStringUTFChars(app_dir, raw_app_dir);
+
+ Runtime::Current()->RegisterAppInfo(code_paths_vec,
+ profile_file_str,
+ foreign_dex_profile_path_str,
+ app_dir_str);
}
static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
@@ -638,7 +654,7 @@
NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"),
NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
NATIVE_METHOD(VMRuntime, registerAppInfo,
- "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"),
+ "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
};
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 6ffd476..858849f 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <atomic>
namespace art {
@@ -473,6 +474,18 @@
obj->SetField64<false>(MemberOffset(offset), conv.converted);
}
+static void Unsafe_loadFence(JNIEnv*, jobject) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+}
+
+static void Unsafe_storeFence(JNIEnv*, jobject) {
+ std::atomic_thread_fence(std::memory_order_release);
+}
+
+static void Unsafe_fullFence(JNIEnv*, jobject) {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
@@ -532,6 +545,11 @@
OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ),
OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF),
OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD),
+
+ // CAS
+ NATIVE_METHOD(Unsafe, loadFence, "!()V"),
+ NATIVE_METHOD(Unsafe, storeFence, "!()V"),
+ NATIVE_METHOD(Unsafe, fullFence, "!()V"),
};
void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 4948558..2ac1052 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,6 +29,8 @@
constexpr uint8_t OatHeader::kOatVersion[4];
constexpr const char OatHeader::kTrueValue[];
constexpr const char OatHeader::kFalseValue[];
+constexpr const char OatHeader::kExtractOnlyValue[];
+constexpr const char OatHeader::kProfileGuideCompiledValue[];
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
@@ -467,12 +469,24 @@
}
bool OatHeader::IsExtractOnly() const {
- return IsKeyEnabled(OatHeader::kExtractOnlyKey);
+ return KeyHasValue(kCompilationType,
+ kExtractOnlyValue,
+ sizeof(kExtractOnlyValue));
+}
+
+bool OatHeader::IsProfileGuideCompiled() const {
+ return KeyHasValue(kCompilationType,
+ kProfileGuideCompiledValue,
+ sizeof(kProfileGuideCompiledValue));
+}
+
+bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
+ const char* key_value = GetStoreValueByKey(key);
+ return (key_value != nullptr && strncmp(key_value, value, value_size) == 0);
}
bool OatHeader::IsKeyEnabled(const char* key) const {
- const char* key_value = GetStoreValueByKey(key);
- return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0);
+ return KeyHasValue(key, kTrueValue, sizeof(kTrueValue));
}
void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
diff --git a/runtime/oat.h b/runtime/oat.h
index fde386f..0660e19 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,12 +38,15 @@
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
- static constexpr const char* kExtractOnlyKey = "extract-only";
+ static constexpr const char* kCompilationType = "compilation-type";
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPath = "bootclasspath";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
+ static constexpr const char kExtractOnlyValue[] = "extract-only";
+ static constexpr const char kProfileGuideCompiledValue[] = "profile-guide";
+
static OatHeader* Create(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
@@ -108,8 +111,11 @@
bool IsPic() const;
bool IsDebuggable() const;
bool IsExtractOnly() const;
+ bool IsProfileGuideCompiled() const;
private:
+ bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
+
OatHeader(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
uint32_t dex_file_count,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c389547..7155c79 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -169,7 +169,10 @@
return false;
}
if (requested_base != nullptr && begin_ != requested_base) {
- PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ // Host can fail this check. Do not dump there to avoid polluting the output.
+ if (kIsTargetBuild) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ }
*error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
"oatdata=%p != expected=%p. See process maps in the log.",
begin_, requested_base);
@@ -1232,6 +1235,10 @@
return GetOatHeader().IsExtractOnly();
}
+bool OatFile::IsProfileGuideCompiled() const {
+ return GetOatHeader().IsProfileGuideCompiled();
+}
+
static constexpr char kDexClassPathEncodingSeparator = '*';
std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index fb91a8c..1084253 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -93,6 +93,8 @@
bool IsExtractOnly() const;
+ bool IsProfileGuideCompiled() const;
+
const std::string& GetLocation() const {
return location_;
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 262c932..90712c6 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,7 +36,6 @@
#include "image.h"
#include "oat.h"
#include "os.h"
-#include "profiler.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "ScopedFd.h"
@@ -45,28 +44,19 @@
namespace art {
OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const int target_compilation_type_mask,
const InstructionSet isa,
bool load_executable)
- : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { }
+ : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable)
+{ }
OatFileAssistant::OatFileAssistant(const char* dex_location,
const char* oat_location,
+ const int target_compilation_type_mask,
const InstructionSet isa,
bool load_executable)
- : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
- const InstructionSet isa,
- bool load_executable,
- const char* package_name)
- : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
- const char* oat_location,
- const InstructionSet isa,
- bool load_executable,
- const char* package_name)
- : isa_(isa), package_name_(package_name), load_executable_(load_executable) {
+ : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa),
+ load_executable_(load_executable) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -83,18 +73,6 @@
cached_oat_file_name_attempted_ = true;
cached_oat_file_name_found_ = true;
}
-
- // If there is no package name given, we will not be able to find any
- // profiles associated with this dex location. Preemptively mark that to
- // be the case, rather than trying to find and load the profiles later.
- // Similarly, if profiling is disabled.
- if (package_name == nullptr
- || !Runtime::Current()->GetProfilerOptions().IsEnabled()) {
- profile_load_attempted_ = true;
- profile_load_succeeded_ = false;
- old_profile_load_attempted_ = true;
- old_profile_load_succeeded_ = false;
- }
}
OatFileAssistant::~OatFileAssistant() {
@@ -138,10 +116,23 @@
return true;
}
-OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
- // TODO: If the profiling code is ever restored, it's worth considering
- // whether we should check to see if the profile is out of date here.
+// Returns the compilation mode of the given oat file.
+static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) {
+ if (oat_file.IsExtractOnly()) {
+ return OatFileAssistant::kExtractOnly;
+ }
+ if (oat_file.IsProfileGuideCompiled()) {
+ return OatFileAssistant::kProfileGuideCompilation;
+ }
+ // Assume that if the oat files is not extract-only or profile-guide compiled
+ // then it must be fully compiled.
+ // NB: this does not necessary mean that the oat file is actually fully compiled. It
+ // might have been compiled in a different way (e.g. interpret-only) which does
+ // not record a type in the header.
+ return OatFileAssistant::kFullCompilation;
+}
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
return kNoDexOptNeeded;
}
@@ -419,6 +410,11 @@
}
bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
+ // Verify the file satisfies the desired compilation type.
+ if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) {
+ return true;
+ }
+
// Verify the dex checksum.
// Note: GetOatDexFile will return null if the dex checksum doesn't match
// what we provide, which verifies the primary dex checksum for us.
@@ -541,104 +537,6 @@
return true;
}
-bool OatFileAssistant::ProfileExists() {
- return GetProfile() != nullptr;
-}
-
-bool OatFileAssistant::OldProfileExists() {
- return GetOldProfile() != nullptr;
-}
-
-// TODO: The IsProfileChangeSignificant implementation was copied from likely
-// bit-rotted code.
-bool OatFileAssistant::IsProfileChangeSignificant() {
- ProfileFile* profile = GetProfile();
- if (profile == nullptr) {
- return false;
- }
-
- ProfileFile* old_profile = GetOldProfile();
- if (old_profile == nullptr) {
- return false;
- }
-
- // TODO: The following code to compare two profile files should live with
- // the rest of the profiler code, not the oat file assistant code.
-
- // A change in profile is considered significant if X% (change_thr property)
- // of the top K% (compile_thr property) samples has changed.
- const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions();
- const double top_k_threshold = options.GetTopKThreshold();
- const double change_threshold = options.GetTopKChangeThreshold();
- std::set<std::string> top_k, old_top_k;
- profile->GetTopKSamples(top_k, top_k_threshold);
- old_profile->GetTopKSamples(old_top_k, top_k_threshold);
- std::set<std::string> diff;
- std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(),
- old_top_k.end(), std::inserter(diff, diff.end()));
-
- // TODO: consider using the usedPercentage instead of the plain diff count.
- double change_percent = 100.0 * static_cast<double>(diff.size())
- / static_cast<double>(top_k.size());
- std::set<std::string>::iterator end = diff.end();
- for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
- VLOG(oat) << "Profile new in topK: " << *it;
- }
-
- if (change_percent > change_threshold) {
- VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_
- << "has changed significantly: (top "
- << top_k_threshold << "% samples changed in proportion of "
- << change_percent << "%)";
- return true;
- }
- return false;
-}
-
-// TODO: The CopyProfileFile implementation was copied from likely bit-rotted
-// code.
-void OatFileAssistant::CopyProfileFile() {
- if (!ProfileExists()) {
- return;
- }
-
- std::string profile_name = ProfileFileName();
- std::string old_profile_name = OldProfileFileName();
-
- ScopedFd src(open(old_profile_name.c_str(), O_RDONLY));
- if (src.get() == -1) {
- PLOG(WARNING) << "Failed to open profile file " << old_profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- struct stat stat_src;
- if (fstat(src.get(), &stat_src) == -1) {
- PLOG(WARNING) << "Failed to get stats for profile file " << old_profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- // Create the copy with rw------- (only accessible by system)
- ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600));
- if (dst.get() == -1) {
- PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
-#ifdef __linux__
- if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
-#else
- off_t len;
- if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
-#endif
- PLOG(WARNING) << "Failed to copy profile file " << old_profile_name
- << " to " << profile_name << ". My uid:gid is " << getuid()
- << ":" << getgid();
- }
-}
-
bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
std::string* error_msg) {
CHECK(error_msg != nullptr);
@@ -694,6 +592,15 @@
bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
CHECK(error_msg != nullptr);
+ // TODO: Currently we only know how to make a fully-compiled oat file.
+ // Perhaps we should support generating other kinds of oat files?
+ if ((target_compilation_type_mask_ & kFullCompilation) == 0) {
+ *error_msg = "Generation of oat file for dex location " + dex_location_
+ + " not attempted because full compilation was not specified"
+ + " as an acceptable target compilation type.";
+ return false;
+ }
+
Runtime* runtime = Runtime::Current();
if (!runtime->IsDex2OatEnabled()) {
*error_msg = "Generation of oat file for dex location " + dex_location_
@@ -861,21 +768,6 @@
return result;
}
-std::string OatFileAssistant::ProfileFileName() {
- if (package_name_ != nullptr) {
- return DalvikCacheDirectory() + std::string("profiles/") + package_name_;
- }
- return "";
-}
-
-std::string OatFileAssistant::OldProfileFileName() {
- std::string profile_name = ProfileFileName();
- if (profile_name.empty()) {
- return "";
- }
- return profile_name + "@old";
-}
-
std::string OatFileAssistant::ImageLocation() {
Runtime* runtime = Runtime::Current();
const std::vector<gc::space::ImageSpace*>& image_spaces =
@@ -1007,34 +899,6 @@
return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
}
-ProfileFile* OatFileAssistant::GetProfile() {
- if (!profile_load_attempted_) {
- CHECK(package_name_ != nullptr)
- << "pakage_name_ is nullptr: "
- << "profile_load_attempted_ should have been true";
- profile_load_attempted_ = true;
- std::string profile_name = ProfileFileName();
- if (!profile_name.empty()) {
- profile_load_succeeded_ = cached_profile_.LoadFile(profile_name);
- }
- }
- return profile_load_succeeded_ ? &cached_profile_ : nullptr;
-}
-
-ProfileFile* OatFileAssistant::GetOldProfile() {
- if (!old_profile_load_attempted_) {
- CHECK(package_name_ != nullptr)
- << "pakage_name_ is nullptr: "
- << "old_profile_load_attempted_ should have been true";
- old_profile_load_attempted_ = true;
- std::string old_profile_name = OldProfileFileName();
- if (!old_profile_name.empty()) {
- old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name);
- }
- }
- return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr;
-}
-
gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
DCHECK(oat_file != nullptr);
std::string art_file = ArtFileName(oat_file);
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 7b45bca..893aea2 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -44,9 +44,6 @@
// The oat file assistant is intended to be used with dex locations not on the
// boot class path. See the IsInBootClassPath method for a way to check if the
// dex location is in the boot class path.
-//
-// TODO: All the profiling related code is old and untested. It should either
-// be restored and tested, or removed.
class OatFileAssistant {
public:
enum DexOptNeeded {
@@ -73,8 +70,8 @@
enum OatStatus {
// kOatOutOfDate - An oat file is said to be out of date if the file does
- // not exist, or is out of date with respect to the dex file or boot
- // image.
+ // not exist, is out of date with respect to the dex file or boot image,
+ // or does not meet the target compilation type.
kOatOutOfDate,
// kOatNeedsRelocation - An oat file is said to need relocation if the
@@ -88,6 +85,20 @@
kOatUpToDate,
};
+ // Represents the different compilation types of oat files that OatFileAssitant
+ // and external GetDexOptNeeded callers care about.
+ // Note: these should be able to be used as part of a mask.
+ enum CompilationType {
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1
+ kFullCompilation = 1,
+
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2
+ kProfileGuideCompilation = 2,
+
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4
+ kExtractOnly = 4,
+ };
+
// Constructs an OatFileAssistant object to assist the oat file
// corresponding to the given dex location with the target instruction set.
//
@@ -99,31 +110,28 @@
// Note: Currently the dex_location must have an extension.
// TODO: Relax this restriction?
//
+ // The target compilation type specifies a set of CompilationTypes that
+ // should be considered up to date. An oat file compiled in a way not
+ // included in the set is considered out of date. For example, to consider
+ // otherwise up-to-date fully compiled and profile-guide compiled oat
+ // files as up to date, but to consider extract-only files as out of date,
+ // specify: (kFullCompilation | kProfileGuideCompilation).
+ //
// The isa should be either the 32 bit or 64 bit variant for the current
// device. For example, on an arm device, use arm or arm64. An oat file can
// be loaded executable only if the ISA matches the current runtime.
- OatFileAssistant(const char* dex_location, const InstructionSet isa,
+ OatFileAssistant(const char* dex_location,
+ int target_compilation_type_mask,
+ const InstructionSet isa,
bool load_executable);
// Constructs an OatFileAssistant, providing an explicit target oat_location
// to use instead of the standard oat location.
- OatFileAssistant(const char* dex_location, const char* oat_location,
- const InstructionSet isa, bool load_executable);
-
- // Constructs an OatFileAssistant, providing an additional package_name used
- // solely for the purpose of locating profile files.
- //
- // TODO: Why is the name of the profile file based on the package name and
- // not the dex location? If there is no technical reason the dex_location
- // can't be used, we should prefer that instead.
- OatFileAssistant(const char* dex_location, const InstructionSet isa,
- bool load_executable, const char* package_name);
-
- // Constructs an OatFileAssistant with user specified oat location and a
- // package name.
- OatFileAssistant(const char* dex_location, const char* oat_location,
- const InstructionSet isa, bool load_executable,
- const char* package_name);
+ OatFileAssistant(const char* dex_location,
+ const char* oat_location,
+ int target_compilation_type_mask,
+ const InstructionSet isa,
+ bool load_executable);
~OatFileAssistant();
@@ -233,28 +241,6 @@
bool GivenOatFileNeedsRelocation(const OatFile& file);
bool GivenOatFileIsUpToDate(const OatFile& file);
- // Returns true if there is an accessible profile associated with the dex
- // location.
- // This returns false if profiling is disabled.
- bool ProfileExists();
-
- // The old profile is a file containing a previous snapshot of profiling
- // information associated with the dex file code. This is used to track how
- // the profiling information has changed over time.
- //
- // Returns true if there is an accessible old profile associated with the
- // dex location.
- // This returns false if profiling is disabled.
- bool OldProfileExists();
-
- // Returns true if there has been a significant change between the old
- // profile and the current profile.
- // This returns false if profiling is disabled.
- bool IsProfileChangeSignificant();
-
- // Copy the current profile to the old profile location.
- void CopyProfileFile();
-
// Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
// oat file.
@@ -309,16 +295,6 @@
// Returns an empty string if we can't get the dalvik cache directory path.
std::string DalvikCacheDirectory();
- // Constructs the filename for the profile file.
- // Returns an empty string if we do not have the necessary information to
- // construct the filename.
- std::string ProfileFileName();
-
- // Constructs the filename for the old profile file.
- // Returns an empty string if we do not have the necessary information to
- // construct the filename.
- std::string OldProfileFileName();
-
// Returns the current image location.
// Returns an empty string if the image location could not be retrieved.
//
@@ -364,35 +340,18 @@
// The caller shouldn't clean up or free the returned pointer.
const ImageInfo* GetImageInfo();
- // Returns the loaded profile.
- // Loads the profile if needed. Returns null if the profile failed
- // to load.
- // The caller shouldn't clean up or free the returned pointer.
- ProfileFile* GetProfile();
-
- // Returns the loaded old profile.
- // Loads the old profile if needed. Returns null if the old profile
- // failed to load.
- // The caller shouldn't clean up or free the returned pointer.
- ProfileFile* GetOldProfile();
-
// To implement Lock(), we lock a dummy file where the oat file would go
// (adding ".flock" to the target file name) and retain the lock for the
// remaining lifetime of the OatFileAssistant object.
ScopedFlock flock_;
std::string dex_location_;
+ const int target_compilation_type_mask_;
// In a properly constructed OatFileAssistant object, isa_ should be either
// the 32 or 64 bit variant for the current device.
const InstructionSet isa_ = kNone;
- // The package name, used solely to find the profile file.
- // This may be null in a properly constructed object. In this case,
- // profile_load_attempted_ and old_profile_load_attempted_ will be true, and
- // profile_load_succeeded_ and old_profile_load_succeeded_ will be false.
- const char* package_name_ = nullptr;
-
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
@@ -451,18 +410,6 @@
bool image_info_load_succeeded_ = false;
ImageInfo cached_image_info_;
- // Cached value of the profile file.
- // Use the GetProfile method rather than accessing these directly.
- bool profile_load_attempted_ = false;
- bool profile_load_succeeded_ = false;
- ProfileFile cached_profile_;
-
- // Cached value of the profile file.
- // Use the GetOldProfile method rather than accessing these directly.
- bool old_profile_load_attempted_ = false;
- bool old_profile_load_succeeded_ = false;
- ProfileFile cached_old_profile_;
-
// For debugging only.
// If this flag is set, the oat or odex file has been released to the user
// of the OatFileAssistant object and the OatFileAssistant object is in a
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 83d4457..4541468 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -260,7 +260,7 @@
}
void GenerateExtractOnlyOdexForTest(const std::string& dex_location,
- const std::string& odex_location) {
+ const std::string& odex_location) {
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
args.push_back("--oat-file=" + odex_location);
@@ -277,7 +277,26 @@
EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u);
EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0);
-}
+ }
+
+ void GenerateProfileGuideOdexForTest(const std::string& dex_location,
+ const std::string& odex_location) {
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ ScratchFile profile_file;
+ args.push_back("--profile-file=" + profile_file.GetFilename());
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(
+ odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
+ false, dex_location.c_str(), &error_msg));
+ printf("error %s", error_msg.c_str());
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
+ }
private:
// Reserve memory around where the image will be loaded so other memory
@@ -344,7 +363,8 @@
// Generate an oat file for the purposes of test, as opposed to testing
// generation of oat files.
static void GenerateOatForTest(const char* dex_location) {
- OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location,
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
@@ -356,7 +376,8 @@
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -379,7 +400,8 @@
TEST_F(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
@@ -400,7 +422,8 @@
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -422,7 +445,8 @@
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
@@ -448,7 +472,8 @@
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -475,6 +500,7 @@
// Verify we can load both dex files.
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(),
+ OatFileAssistant::kFullCompilation,
kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -495,7 +521,8 @@
GenerateOatForTest(dex_location.c_str());
Copy(GetDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -508,32 +535,6 @@
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
-// Case: We have a DEX file and an extract-only ODEX file out of date relative
-// to the DEX file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) {
- std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar";
- std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex";
-
- // We create a dex, generate an oat for it, then overwrite the dex with a
- // different dex to make the oat out of date.
- Copy(GetDexSrc1(), dex_location);
- GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str());
- Copy(GetDexSrc2(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-}
-
// Case: We have a DEX file and an ODEX file, but no OAT file.
// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
@@ -545,7 +546,8 @@
GenerateOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -578,7 +580,8 @@
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -633,7 +636,8 @@
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -681,7 +685,8 @@
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -724,7 +729,7 @@
GenerateOdexForTest(dex_location, oat_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
+ oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -782,7 +787,7 @@
// Verify things don't go bad.
OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
+ oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -816,7 +821,8 @@
GeneratePicOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -841,7 +847,9 @@
GenerateExtractOnlyOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+ kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -864,7 +872,8 @@
GenerateOatForTest(dex_location.c_str());
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -883,7 +892,8 @@
GenerateOatForTest(dex_location.c_str());
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -903,7 +913,8 @@
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
@@ -917,7 +928,8 @@
EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
// Verify it didn't create an oat in the default location.
- OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant ofm(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_FALSE(ofm.OatFileExists());
}
@@ -933,7 +945,8 @@
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
@@ -948,7 +961,8 @@
std::string oat_location = GetScratchDir() + "/GenNoDex.oat";
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg));
}
@@ -996,7 +1010,8 @@
Copy(GetDexSrc1(), abs_dex_location);
std::string dex_location = MakePathRelative(abs_dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1013,7 +1028,8 @@
TEST_F(OatFileAssistantTest, ShortDexLocation) {
std::string dex_location = "/xx";
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1037,7 +1053,8 @@
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1134,7 +1151,8 @@
GenerateOdexForTest(dex_location, odex_location);
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1156,7 +1174,8 @@
GenerateOdexForTest(dex_location, odex_location);
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1184,6 +1203,45 @@
"/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
}
+// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT.
+// Expect: The status depends on the target compilation type mask.
+TEST_F(OatFileAssistantTest, TargetCompilationType) {
+ std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar";
+ std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateExtractOnlyOdexForTest(dex_location, odex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ OatFileAssistant ofa_full(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_full.IsInBootClassPath());
+ EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate());
+ EXPECT_TRUE(ofa_full.OatFileIsUpToDate());
+
+ OatFileAssistant ofa_extract(dex_location.c_str(),
+ OatFileAssistant::kExtractOnly, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_extract.IsInBootClassPath());
+ EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate());
+ EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate());
+
+ OatFileAssistant ofa_profile(dex_location.c_str(),
+ OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_profile.IsInBootClassPath());
+ EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate());
+ EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate());
+
+ OatFileAssistant ofa_extract_full(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+ kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_extract_full.IsInBootClassPath());
+ EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate());
+ EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate());
+}
+
// Verify the dexopt status values from dalvik.system.DexFile
// match the OatFileAssistant::DexOptStatus values.
TEST_F(OatFileAssistantTest, DexOptStatusValues) {
@@ -1218,13 +1276,31 @@
ASSERT_FALSE(self_patchoat_needed == nullptr);
EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_full = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I");
+ ASSERT_FALSE(compilation_type_full == nullptr);
+ EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I");
+ ASSERT_FALSE(compilation_type_profile_guide == nullptr);
+ EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation,
+ compilation_type_profile_guide->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_extract_only = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I");
+ ASSERT_FALSE(compilation_type_extract_only == nullptr);
+ EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get()));
}
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
// * Test using secondary isa
-// * Test with profiling info?
// * Test for status of oat while oat is being generated (how?)
// * Test case where 32 and 64 bit boot class paths differ,
// and we ask IsInBootClassPath for a class in exactly one of the 32 or
@@ -1233,5 +1309,7 @@
// - Dex is stripped, don't have odex.
// - Oat file corrupted after status check, before reload unexecutable
// because it's unrelocated and no dex2oat
+// * Test unrelocated specific target compilation type can be relocated to
+// make it up to date.
} // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9ae179f..e57125b 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -307,8 +307,13 @@
Thread* const self = Thread::Current();
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
+
+ int target_compilation_type_mask = OatFileAssistant::kFullCompilation
+ | OatFileAssistant::kProfileGuideCompilation
+ | OatFileAssistant::kExtractOnly;
OatFileAssistant oat_file_assistant(dex_location,
oat_location,
+ target_compilation_type_mask,
kRuntimeISA,
!runtime->IsAotCompiler());
@@ -443,6 +448,10 @@
+ std::string(dex_location));
}
}
+
+ // TODO(calin): Consider optimizing this knowing that is useless to record the
+ // use of fully compiled apks.
+ Runtime::Current()->NotifyDexLoaded(dex_location);
return dex_files;
}
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 9b10f2e..c7ccee2 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -744,9 +744,12 @@
return false;
}
DCHECK_GE(field->GetOffset().Int32Value(), 0);
+ // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
+ uint32_t field_offset = field->GetOffset().Uint32Value();
+ bool is_volatile = field->IsVolatile();
result->field_idx = field_idx;
- result->field_offset = field->GetOffset().Int32Value();
- result->is_volatile = field->IsVolatile();
+ result->field_offset = field_offset;
+ result->is_volatile = is_volatile ? 1u : 0u;
return true;
}
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 7e84b40..0e12d73 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -101,6 +101,17 @@
kIntrinsicCas,
kIntrinsicUnsafeGet,
kIntrinsicUnsafePut,
+
+ // 1.8.
+ kIntrinsicUnsafeGetAndAddInt,
+ kIntrinsicUnsafeGetAndAddLong,
+ kIntrinsicUnsafeGetAndSetInt,
+ kIntrinsicUnsafeGetAndSetLong,
+ kIntrinsicUnsafeGetAndSetObject,
+ kIntrinsicUnsafeLoadFence,
+ kIntrinsicUnsafeStoreFence,
+ kIntrinsicUnsafeFullFence,
+
kIntrinsicSystemArrayCopyCharArray,
kIntrinsicSystemArrayCopy,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bbb79af..e95f2c5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -119,6 +119,7 @@
#include "os.h"
#include "parsed_options.h"
#include "profiler.h"
+#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
#include "runtime_options.h"
@@ -1700,7 +1701,9 @@
}
void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename) {
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (jit_.get() == nullptr) {
// We are not JITing. Nothing to do.
return;
@@ -1723,7 +1726,18 @@
}
profile_output_filename_ = profile_output_filename;
- jit_->StartProfileSaver(profile_output_filename, code_paths);
+ jit_->StartProfileSaver(profile_output_filename,
+ code_paths,
+ foreign_dex_profile_path,
+ app_dir);
+}
+
+void Runtime::NotifyDexLoaded(const std::string& dex_location) {
+ VLOG(profiler) << "Notify dex loaded: " << dex_location;
+ // We know that if the ProfileSaver is started then we can record profile information.
+ if (ProfileSaver::IsStarted()) {
+ ProfileSaver::NotifyDexUse(dex_location);
+ }
}
// Transaction support.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 83e77d2..8e99f80 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -467,7 +467,10 @@
}
void RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename);
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
+ void NotifyDexLoaded(const std::string& dex_location);
// Transaction support.
bool IsActiveTransaction() const {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 8237b06..bc963c5 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -36,6 +36,7 @@
static constexpr bool kDumpHeapObjectOnSigsevg = false;
static constexpr bool kUseSigRTTimeout = true;
+static constexpr bool kDumpNativeStackOnTimeout = true;
struct Backtrace {
public:
@@ -350,7 +351,9 @@
if (runtime != nullptr) {
if (IsTimeoutSignal(signal_number)) {
// Special timeout signal. Try to dump all threads.
- runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+ // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
+ // are of value here.
+ runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout);
}
gc::Heap* heap = runtime->GetHeap();
LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index cf515b6..4c81d4f 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -171,9 +171,9 @@
closedir(d);
}
-// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke
-// addr2line when available.
-static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000;
+// Dump checkpoint timeout in milliseconds. Larger amount on the target, since the device could be
+// overloaded with ANR dumps.
+static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000;
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 30f613c..b171b75 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
#include "reg_type_cache-inl.h"
#include "base/arena_bit_vector.h"
+#include "base/bit_vector-inl.h"
#include "base/casts.h"
#include "base/scoped_arena_allocator.h"
#include "base/stl_util.h"
@@ -351,9 +352,11 @@
types.Copy(&left_merge->GetUnresolvedTypes());
left_resolved = &left_merge->GetResolvedPart();
} else if (left.IsUnresolvedTypes()) {
+ types.ClearAllBits();
types.SetBit(left.GetId());
left_resolved = &Zero();
} else {
+ types.ClearAllBits();
left_resolved = &left;
}
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index 22ac7e4..42a74f8 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -30,23 +30,14 @@
namespace art {
namespace verifier {
-class BaseRegTypeTest : public CommonRuntimeTest {
- public:
- void PostRuntimeCreate() OVERRIDE {
- stack.reset(new ArenaStack(Runtime::Current()->GetArenaPool()));
- allocator.reset(new ScopedArenaAllocator(stack.get()));
- }
-
- std::unique_ptr<ArenaStack> stack;
- std::unique_ptr<ScopedArenaAllocator> allocator;
-};
-
-class RegTypeTest : public BaseRegTypeTest {};
+class RegTypeTest : public CommonRuntimeTest {};
TEST_F(RegTypeTest, ConstLoHi) {
// Tests creating primitive types types.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
const RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
const RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
@@ -67,8 +58,10 @@
}
TEST_F(RegTypeTest, Pairs) {
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
int64_t val = static_cast<int32_t>(1234);
const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
@@ -91,8 +84,10 @@
}
TEST_F(RegTypeTest, Primitives) {
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& bool_reg_type = cache.Boolean();
EXPECT_FALSE(bool_reg_type.IsUndefined());
@@ -359,13 +354,15 @@
EXPECT_TRUE(double_reg_type.HasClass());
}
-class RegTypeReferenceTest : public BaseRegTypeTest {};
+class RegTypeReferenceTest : public CommonRuntimeTest {};
TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) {
// Tests matching precisions. A reference type that was created precise doesn't
// match the one that is imprecise.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& imprecise_obj = cache.JavaLangObject(false);
const RegType& precise_obj = cache.JavaLangObject(true);
const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
@@ -379,8 +376,10 @@
TEST_F(RegTypeReferenceTest, UnresolvedType) {
// Tests creating unresolved types. Miss for the first time asking the cache and
// a hit second time.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes());
@@ -395,8 +394,10 @@
TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) {
// Tests creating types uninitialized types from unresolved types.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
@@ -417,8 +418,10 @@
TEST_F(RegTypeReferenceTest, Dump) {
// Tests types for proper Dump messages.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true);
const RegType& resolved_ref = cache.JavaLangString();
@@ -442,8 +445,10 @@
// Add a class to the cache then look for the same class and make sure it is a
// Hit the second time. Then check for the same effect when using
// The JavaLangObject method instead of FromDescriptor. String class is final.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& ref_type = cache.JavaLangString();
const RegType& ref_type_2 = cache.JavaLangString();
const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true);
@@ -462,8 +467,10 @@
// Add a class to the cache then look for the same class and make sure it is a
// Hit the second time. Then I am checking for the same effect when using
// The JavaLangObject method instead of FromDescriptor. Object Class in not final.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache(true, *allocator);
+ RegTypeCache cache(true, allocator);
const RegType& ref_type = cache.JavaLangObject(true);
const RegType& ref_type_2 = cache.JavaLangObject(true);
const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
@@ -476,7 +483,9 @@
// Tests merging logic
// String and object , LUB is object.
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache_new(true, *allocator);
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
+ RegTypeCache cache_new(true, allocator);
const RegType& string = cache_new.JavaLangString();
const RegType& Object = cache_new.JavaLangObject(true);
EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject());
@@ -498,8 +507,10 @@
TEST_F(RegTypeTest, MergingFloat) {
// Testing merging logic with float and float constants.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache_new(true, *allocator);
+ RegTypeCache cache_new(true, allocator);
constexpr int32_t kTestConstantValue = 10;
const RegType& float_type = cache_new.Float();
@@ -529,8 +540,10 @@
TEST_F(RegTypeTest, MergingLong) {
// Testing merging logic with long and long constants.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache_new(true, *allocator);
+ RegTypeCache cache_new(true, allocator);
constexpr int32_t kTestConstantValue = 10;
const RegType& long_lo_type = cache_new.LongLo();
@@ -583,8 +596,10 @@
TEST_F(RegTypeTest, MergingDouble) {
// Testing merging logic with double and double constants.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache_new(true, *allocator);
+ RegTypeCache cache_new(true, allocator);
constexpr int32_t kTestConstantValue = 10;
const RegType& double_lo_type = cache_new.DoubleLo();
@@ -637,8 +652,10 @@
TEST_F(RegTypeTest, ConstPrecision) {
// Tests creating primitive types types.
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
ScopedObjectAccess soa(Thread::Current());
- RegTypeCache cache_new(true, *allocator);
+ RegTypeCache cache_new(true, allocator);
const RegType& imprecise_const = cache_new.FromCat1Const(10, false);
const RegType& precise_const = cache_new.FromCat1Const(10, true);
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index a9a7a05..b2f905e 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -40,7 +40,7 @@
}
private static Unsafe getUnsafe() throws Exception {
- Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+ Class<?> unsafeClass = Unsafe.class;
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
diff --git a/test/004-checker-UnsafeTest18/expected.txt b/test/004-checker-UnsafeTest18/expected.txt
new file mode 100644
index 0000000..651da72
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/expected.txt
@@ -0,0 +1,2 @@
+starting
+passed
diff --git a/test/004-checker-UnsafeTest18/info.txt b/test/004-checker-UnsafeTest18/info.txt
new file mode 100644
index 0000000..0fca5eb
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/info.txt
@@ -0,0 +1 @@
+Test support for 1.8 sun.misc.Unsafe.
diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java
new file mode 100644
index 0000000..bb020b9
--- /dev/null
+++ b/test/004-checker-UnsafeTest18/src/Main.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.Field;
+
+import sun.misc.Unsafe;
+
+/**
+ * Checker test on the 1.8 unsafe operations. Note, this is by no means an
+ * exhaustive unit test for these CAS (compare-and-swap) and fence operations.
+ * Instead, this test ensures the methods are recognized as intrinsic and behave
+ * as expected.
+ */
+public class Main {
+
+ private static final Unsafe unsafe = getUnsafe();
+
+ private static Thread[] sThreads = new Thread[10];
+
+ //
+ // Fields accessed by setters and adders.
+ //
+
+ public int i = 0;
+ public long l = 0;
+ public Object o = null;
+
+ //
+ // Setters.
+ //
+
+ /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int set32(Object o, long offset, int newValue) {
+ return unsafe.getAndSetInt(o, offset, newValue);
+ }
+
+ /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long set64(Object o, long offset, long newValue) {
+ return unsafe.getAndSetLong(o, offset, newValue);
+ }
+
+ /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject
+ /// CHECK-DAG: Return [<<Result>>]
+ private static Object setObj(Object o, long offset, Object newValue) {
+ return unsafe.getAndSetObject(o, offset, newValue);
+ }
+
+ //
+ // Adders.
+ //
+
+ /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int add32(Object o, long offset, int delta) {
+ return unsafe.getAndAddInt(o, offset, delta);
+ }
+
+ /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long add64(Object o, long offset, long delta) {
+ return unsafe.getAndAddLong(o, offset, delta);
+ }
+
+ //
+ // Fences (native).
+ //
+
+ /// CHECK-START: void Main.load() intrinsics_recognition (after)
+ /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence
+ //
+ /// CHECK-START: void Main.load() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence
+ //
+ /// CHECK-START: void Main.load() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:LoadAny
+ private static void load() {
+ unsafe.loadFence();
+ }
+
+ /// CHECK-START: void Main.store() intrinsics_recognition (after)
+ /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence
+ //
+ /// CHECK-START: void Main.store() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence
+ //
+ /// CHECK-START: void Main.store() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:AnyStore
+ private static void store() {
+ unsafe.storeFence();
+ }
+
+ /// CHECK-START: void Main.full() intrinsics_recognition (after)
+ /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence
+ //
+ /// CHECK-START: void Main.full() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence
+ //
+ /// CHECK-START: void Main.full() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:AnyAny
+ private static void full() {
+ unsafe.fullFence();
+ }
+
+ //
+ // Thread fork/join.
+ //
+
+ private static void fork(Runnable r) {
+ for (int i = 0; i < 10; i++) {
+ sThreads[i] = new Thread(r);
+ sThreads[i].start();
+ }
+ }
+
+ private static void join() {
+ try {
+ for (int i = 0; i < 10; i++) {
+ sThreads[i].join();
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Failed join: " + e);
+ }
+ }
+
+ //
+ // Driver.
+ //
+
+ public static void main(String[] args) {
+ System.out.println("starting");
+
+ final Main m = new Main();
+
+ // Get the offsets.
+
+ final long intOffset, longOffset, objOffset;
+ try {
+ Field intField = Main.class.getDeclaredField("i");
+ Field longField = Main.class.getDeclaredField("l");
+ Field objField = Main.class.getDeclaredField("o");
+
+ intOffset = unsafe.objectFieldOffset(intField);
+ longOffset = unsafe.objectFieldOffset(longField);
+ objOffset = unsafe.objectFieldOffset(objField);
+
+ } catch (NoSuchFieldException e) {
+ throw new Error("No offset: " + e);
+ }
+
+ // Some sanity within same thread.
+
+ set32(m, intOffset, 3);
+ expectEquals32(3, m.i);
+
+ set64(m, longOffset, 7L);
+ expectEquals64(7L, m.l);
+
+ setObj(m, objOffset, m);
+ expectEqualsObj(m, m.o);
+
+ add32(m, intOffset, 11);
+ expectEquals32(14, m.i);
+
+ add64(m, longOffset, 13L);
+ expectEquals64(20L, m.l);
+
+ // Some sanity on setters within different threads.
+
+ fork(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 10; i++)
+ set32(m, intOffset, i);
+ }
+ });
+ join();
+ expectEquals32(9, m.i); // one thread's last value wins
+
+ fork(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 10; i++)
+ set64(m, longOffset, (long) (100 + i));
+ }
+ });
+ join();
+ expectEquals64(109L, m.l); // one thread's last value wins
+
+ fork(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 10; i++)
+ setObj(m, objOffset, sThreads[i]);
+ }
+ });
+ join();
+ expectEqualsObj(sThreads[9], m.o); // one thread's last value wins
+
+ // Some sanity on adders within different threads.
+
+ fork(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 10; i++)
+ add32(m, intOffset, i + 1);
+ }
+ });
+ join();
+ expectEquals32(559, m.i); // all values accounted for
+
+ fork(new Runnable() {
+ public void run() {
+ for (int i = 0; i < 10; i++)
+ add64(m, longOffset, (long) (i + 1));
+ }
+ });
+ join();
+ expectEquals64(659L, m.l); // all values accounted for
+
+ // TODO: the fences
+
+ System.out.println("passed");
+ }
+
+ // Use reflection to implement "Unsafe.getUnsafe()";
+ private static Unsafe getUnsafe() {
+ try {
+ Class<?> unsafeClass = Unsafe.class;
+ Field f = unsafeClass.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ return (Unsafe) f.get(null);
+ } catch (Exception e) {
+ throw new Error("Cannot get Unsafe instance");
+ }
+ }
+
+ private static void expectEquals32(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals64(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEqualsObj(Object expected, Object result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java
index 752fdd9..418690a 100644
--- a/test/145-alloc-tracking-stress/src/Main.java
+++ b/test/145-alloc-tracking-stress/src/Main.java
@@ -1,5 +1,4 @@
/*
-
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/test/462-checker-inlining-across-dex-files/multidex.jpp b/test/462-checker-inlining-across-dex-files/multidex.jpp
new file mode 100644
index 0000000..ae55456
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/multidex.jpp
@@ -0,0 +1,8 @@
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
+
+AAA:
+ @@com.android.jack.annotations.ForceInMainDex
+ class AAA
+
diff --git a/test/556-invoke-super/multidex.jpp b/test/556-invoke-super/multidex.jpp
new file mode 100644
index 0000000..fe01801
--- /dev/null
+++ b/test/556-invoke-super/multidex.jpp
@@ -0,0 +1,4 @@
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main*
+
diff --git a/test/569-checker-pattern-replacement/multidex.jpp b/test/569-checker-pattern-replacement/multidex.jpp
new file mode 100644
index 0000000..cfc8ad1
--- /dev/null
+++ b/test/569-checker-pattern-replacement/multidex.jpp
@@ -0,0 +1,8 @@
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
+
+BaseInMainDex:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BaseInMainDex
+
diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/577-profile-foreign-dex/expected.txt
diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt
new file mode 100644
index 0000000..090db3f
--- /dev/null
+++ b/test/577-profile-foreign-dex/info.txt
@@ -0,0 +1 @@
+Check that we record the use of foreign dex files when profiles are enabled.
diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run
new file mode 100644
index 0000000..ad57d14
--- /dev/null
+++ b/test/577-profile-foreign-dex/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+exec ${RUN} \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:true \
+ "${@}"
diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java
new file mode 100644
index 0000000..cba73b3
--- /dev/null
+++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+public class OtherDex {
+}
diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java
new file mode 100644
index 0000000..0cd85b5
--- /dev/null
+++ b/test/577-profile-foreign-dex/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+public class Main {
+
+ private static final String PROFILE_NAME = "primary.prof";
+ private static final String APP_DIR_PREFIX = "app_dir_";
+ private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex";
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ public static void main(String[] args) throws Exception {
+ File tmpFile = null;
+ File appDir = null;
+ File profileFile = null;
+ File foreignDexProfileDir = null;
+
+ try {
+ // Create the necessary files layout.
+ tmpFile = createTempFile();
+ appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName());
+ appDir.mkdir();
+ foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR);
+ foreignDexProfileDir.mkdir();
+ profileFile = createTempFile();
+
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+
+ // Register the app with the runtime
+ VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(),
+ new String[] { codePath }, foreignDexProfileDir.getPath());
+
+ testMarkerForForeignDex(foreignDexProfileDir);
+ testMarkerForCodePath(foreignDexProfileDir);
+ testMarkerForApplicationDexFile(foreignDexProfileDir, appDir);
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ if (profileFile != null) {
+ profileFile.delete();
+ }
+ if (foreignDexProfileDir != null) {
+ foreignDexProfileDir.delete();
+ }
+ if (appDir != null) {
+ appDir.delete();
+ }
+ }
+ }
+
+ // Verify we actually create a marker on disk for foreign dex files.
+ private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception {
+ String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar";
+ loadDexFile(foreignDex);
+ checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true);
+ }
+
+ // Verify we do not create a marker on disk for dex files path of the code path.
+ private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception {
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+ loadDexFile(codePath);
+ checkMarker(foreignDexProfileDir, codePath, /* exists */ false);
+ }
+
+ private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir)
+ throws Exception {
+ // Copy the -ex jar to the application directory and load it from there.
+ // This will record duplicate class conflicts but we don't care for this use case.
+ File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar");
+ File appDex = new File(appDir, "appDex.jar");
+ try {
+ copyFile(foreignDex, appDex);
+
+ loadDexFile(appDex.getAbsolutePath());
+ checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false);
+ } finally {
+ if (appDex != null) {
+ appDex.delete();
+ }
+ }
+ }
+
+ private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) {
+ File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@'));
+ boolean result_ok = exists ? marker.exists() : !marker.exists();
+ if (!result_ok) {
+ throw new RuntimeException("Marker test failed for:" + marker.getPath());
+ }
+ }
+
+ private static void loadDexFile(String dexFile) throws Exception {
+ Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new RuntimeException("Couldn't find path class loader class");
+ }
+ Constructor constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ constructor.newInstance(
+ dexFile, ClassLoader.getSystemClassLoader());
+ }
+
+ private static class VMRuntime {
+ private static final Method registerAppInfoMethod;
+ static {
+ try {
+ Class c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+ String.class, String.class, String[].class, String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String pkgName, String appDir,
+ String[] codePath, String foreignDexProfileDir) throws Exception {
+ registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir);
+ }
+ }
+
+ private static void copyFile(File fromFile, File toFile) throws Exception {
+ FileInputStream in = new FileInputStream(fromFile);
+ FileOutputStream out = new FileOutputStream(toFile);
+ try {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ out.flush();
+ try {
+ out.getFD().sync();
+ } catch (IOException e) {
+ }
+ out.close();
+ in.close();
+ }
+ }
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c4f0171..7036bdc 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -567,7 +567,9 @@
537-checker-arraycopy
# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
-TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
+# 145: Test sometimes times out in read barrier configuration (b/27467554).
+TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \
+ 145-alloc-tracking-stress
ifeq ($(ART_USE_READ_BARRIER),true)
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
diff --git a/test/etc/default-build b/test/etc/default-build
index 6e855ec..5f78496 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -116,28 +116,33 @@
SKIP_DX_MERGER="true"
fi
-if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
- # Jack does not support this configuration unless we specify how to partition the DEX file
- # with a .jpp file.
- USE_JACK="false"
-fi
-
if [ ${USE_JACK} = "true" ]; then
# Jack toolchain
if [ "${HAS_SRC}" = "true" ]; then
- ${JACK} ${JACK_ARGS} --output-jack src.jack src
- imported_jack_files="--import src.jack"
+ if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
+ # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning
+ # when creating the output .dex file.
+ ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex
+ jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex"
+ jack_extra_args="${jack_extra_args} -D jack.preprocessor=true"
+ jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp"
+ else
+ ${JACK} ${JACK_ARGS} --output-jack src.jack src
+ fi
+ jack_extra_args="${jack_extra_args} --import src.jack"
fi
if [ "${HAS_SRC2}" = "true" ]; then
${JACK} ${JACK_ARGS} --output-jack src2.jack src2
- imported_jack_files="--import src2.jack ${imported_jack_files}"
+ # In case of duplicate classes, we want to take into account the classes from src2. Therefore
+ # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file.
+ jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first"
+ jack_extra_args="--import src2.jack ${jack_extra_args}"
fi
- # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider
- # class definitions from src2 first.
+ # Compile jack files into a DEX file.
if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
- ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
+ ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
fi
else
# Legacy toolchain with javac+dx
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index fab4599..46100ae 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -270,10 +270,5 @@
description: "Only work with --mode=activity",
result: EXEC_FAILED,
names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
-},
-{
- description: "Temporary suppressing while test is fixed",
- result: EXEC_FAILED,
- names: [ "org.apache.harmony.tests.java.util.ArrayDequeTest#test_forEachRemaining_iterator" ]
}
]
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index 19a61dc..95f0c2d 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -16,20 +16,5 @@
names: ["jsr166.LinkedTransferQueueTest#testTransfer2",
"jsr166.LinkedTransferQueueTest#testWaitingConsumer"],
bug: 25883050
-},
-{
- description: "libcore.java.lang.OldSystemTest#test_gc failure on armv8-concurrent-collector.",
- result: EXEC_FAILED,
- names: ["libcore.java.lang.OldSystemTest#test_gc"],
- bug: 26155567
-},
-{
- description: "TimeoutException on hammerhead-concurrent-collector",
- result: EXEC_FAILED,
- modes: [device],
- names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
- "libcore.java.text.SimpleDateFormatTest#testLocales",
- "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"],
- bug: 26711853
}
]
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 9e085b5..1e9c763 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -46,5 +46,13 @@
adb logcat -p
echo -e "${green}Kill stalled dalvikvm processes${nc}"
-processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
-for i in $processes; do adb shell kill -9 $i; done
+# 'ps' on M can sometimes hang.
+timeout 2s adb shell "ps"
+if [ $? = 124 ]; then
+ echo -e "${green}Rebooting device to fix 'ps'${nc}"
+ adb reboot
+ adb wait-for-device root
+else
+ processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
+ for i in $processes; do adb shell kill -9 $i; done
+fi