summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dexdump/dexdump.cc17
-rw-r--r--dexlayout/dexlayout.cc43
-rw-r--r--runtime/gc/collector/concurrent_copying.cc4
-rw-r--r--runtime/gc/collector/mark_sweep.cc2
-rw-r--r--runtime/gc/heap.cc2
-rw-r--r--runtime/gc/verification.cc2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_end.S2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_end_alt.S2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_end_sister.S2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_start.S2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_start_alt.S2
-rw-r--r--runtime/interpreter/mterp/arm/instruction_start_sister.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_end.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_end_alt.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_end_sister.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_start.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_start_alt.S2
-rw-r--r--runtime/interpreter/mterp/arm64/instruction_start_sister.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm.S12
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm64.S12
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S14
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86_64.S14
-rw-r--r--runtime/interpreter/mterp/x86/header.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_end.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_end_alt.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_end_sister.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_start.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_start_alt.S2
-rw-r--r--runtime/interpreter/mterp/x86/instruction_start_sister.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/header.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_end.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_end_alt.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_end_sister.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_start.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_start_alt.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/instruction_start_sister.S2
-rw-r--r--test/565-checker-condition-liveness/src/Main.java70
-rw-r--r--test/dexdump/bytecodes.txt4
-rwxr-xr-xtest/dexdump/bytecodes.xml12
-rwxr-xr-xtest/dexdump/checkers.xml2
-rw-r--r--test/dexdump/invoke-custom.txt10
-rw-r--r--tools/art213
-rw-r--r--tools/dexanalyze/Android.bp1
-rw-r--r--tools/dexanalyze/dexanalyze.cc1
-rw-r--r--tools/dexanalyze/dexanalyze_bytecode.cc131
-rw-r--r--tools/dexanalyze/dexanalyze_bytecode.h16
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.cc131
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.h19
-rw-r--r--tools/dexanalyze/dexanalyze_strings.cc162
-rw-r--r--tools/dexanalyze/dexanalyze_strings.h54
-rwxr-xr-xtools/golem/build-target.sh2
-rwxr-xr-xtools/run-jdwp-tests.sh8
52 files changed, 686 insertions, 324 deletions
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index f8274e2f9a..e9b64028de 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -123,8 +123,7 @@ static const char* primitiveTypeLabel(char typeChar) {
/*
* Converts a type descriptor to human-readable "dotted" form. For
* example, "Ljava/lang/String;" becomes "java.lang.String", and
- * "[I" becomes "int[]". Also converts '$' to '.', which means this
- * form can't be converted back to a descriptor.
+ * "[I" becomes "int[]".
*/
static std::unique_ptr<char[]> descriptorToDot(const char* str) {
int targetLen = strlen(str);
@@ -157,7 +156,7 @@ static std::unique_ptr<char[]> descriptorToDot(const char* str) {
int i = 0;
for (; i < targetLen; i++) {
const char ch = str[offset + i];
- newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+ newStr[i] = (ch == '/') ? '.' : ch;
} // for
// Add the appropriate number of brackets for arrays.
@@ -171,10 +170,9 @@ static std::unique_ptr<char[]> descriptorToDot(const char* str) {
}
/*
- * Converts the class name portion of a type descriptor to human-readable
- * "dotted" form. For example, "Ljava/lang/String;" becomes "String".
+ * Retrieves the class name portion of a type descriptor.
*/
-static std::unique_ptr<char[]> descriptorClassToDot(const char* str) {
+static std::unique_ptr<char[]> descriptorClassToName(const char* str) {
// Reduce to just the class name prefix.
const char* lastSlash = strrchr(str, '/');
if (lastSlash == nullptr) {
@@ -187,8 +185,7 @@ static std::unique_ptr<char[]> descriptorClassToDot(const char* str) {
const int targetLen = strlen(lastSlash);
std::unique_ptr<char[]> newStr(new char[targetLen]);
for (int i = 0; i < targetLen - 1; i++) {
- const char ch = lastSlash[i];
- newStr[i] = ch == '$' ? '.' : ch;
+ newStr[i] = lastSlash[i];
} // for
newStr[targetLen - 1] = '\0';
return newStr;
@@ -1250,7 +1247,7 @@ static void dumpMethod(const ClassAccessor::Method& method, int i) {
// Method name and prototype.
if (constructor) {
- std::unique_ptr<char[]> dot(descriptorClassToDot(backDescriptor));
+ std::unique_ptr<char[]> dot(descriptorClassToName(backDescriptor));
fprintf(gOutFile, "<constructor name=\"%s\"\n", dot.get());
dot = descriptorToDot(backDescriptor);
fprintf(gOutFile, " type=\"%s\"\n", dot.get());
@@ -1469,7 +1466,7 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
}
fprintf(gOutFile, " Interfaces -\n");
} else {
- std::unique_ptr<char[]> dot(descriptorClassToDot(classDescriptor));
+ std::unique_ptr<char[]> dot(descriptorClassToName(classDescriptor));
fprintf(gOutFile, "<class name=\"%s\"\n", dot.get());
if (superclassDescriptor != nullptr) {
dot = descriptorToDot(superclassDescriptor);
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index d6dd9d1829..bd7a301a56 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -71,26 +71,10 @@ static inline uint16_t Get2LE(unsigned char const* src) {
}
/*
- * Converts a type descriptor to human-readable "dotted" form. For
- * example, "Ljava/lang/String;" becomes "java.lang.String", and
- * "[I" becomes "int[]". Also converts '$' to '.', which means this
- * form can't be converted back to a descriptor.
- */
-static std::string DescriptorToDotWrapper(const char* descriptor) {
- std::string result = DescriptorToDot(descriptor);
- size_t found = result.find('$');
- while (found != std::string::npos) {
- result[found] = '.';
- found = result.find('$', found);
- }
- return result;
-}
-
-/*
* Converts the class name portion of a type descriptor to human-readable
* "dotted" form. For example, "Ljava/lang/String;" becomes "String".
*/
-static std::string DescriptorClassToDot(const char* str) {
+static std::string DescriptorClassToName(const char* str) {
std::string descriptor(str);
// Reduce to just the class name prefix.
size_t last_slash = descriptor.rfind('/');
@@ -104,13 +88,6 @@ static std::string DescriptorClassToDot(const char* str) {
size_t size = descriptor.size() - 1 - last_slash;
std::string result(descriptor.substr(last_slash, size));
- // Replace '$' with '.'.
- size_t dollar_sign = result.find('$');
- while (dollar_sign != std::string::npos) {
- result[dollar_sign] = '.';
- dollar_sign = result.find('$', dollar_sign);
- }
-
return result;
}
@@ -786,7 +763,7 @@ void DexLayout::DumpInterface(const dex_ir::TypeId* type_item, int i) {
if (options_.output_format_ == kOutputPlain) {
fprintf(out_file_, " #%d : '%s'\n", i, interface_name);
} else {
- std::string dot(DescriptorToDotWrapper(interface_name));
+ std::string dot(DescriptorToDot(interface_name));
fprintf(out_file_, "<implements name=\"%s\">\n</implements>\n", dot.c_str());
}
}
@@ -1044,7 +1021,7 @@ void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32
const char* back_descriptor = method_id->Class()->GetStringId()->Data();
// Generate header.
- std::string dot(DescriptorToDotWrapper(back_descriptor));
+ std::string dot(DescriptorToDot(back_descriptor));
fprintf(out_file_, "%06x: |[%06x] %s.%s:%s\n",
code_offset, code_offset, dot.c_str(), name, type_descriptor.c_str());
@@ -1212,9 +1189,9 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
// Method name and prototype.
if (constructor) {
- std::string dot(DescriptorClassToDot(back_descriptor));
+ std::string dot(DescriptorClassToName(back_descriptor));
fprintf(out_file_, "<constructor name=\"%s\"\n", dot.c_str());
- dot = DescriptorToDotWrapper(back_descriptor);
+ dot = DescriptorToDot(back_descriptor);
fprintf(out_file_, " type=\"%s\"\n", dot.c_str());
} else {
fprintf(out_file_, "<method name=\"%s\"\n", name);
@@ -1223,7 +1200,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
LOG(ERROR) << "bad method type descriptor '" << type_descriptor << "'";
goto bail;
}
- std::string dot(DescriptorToDotWrapper(return_type + 1));
+ std::string dot(DescriptorToDot(return_type + 1));
fprintf(out_file_, " return=\"%s\"\n", dot.c_str());
fprintf(out_file_, " abstract=%s\n", QuotedBool((flags & kAccAbstract) != 0));
fprintf(out_file_, " native=%s\n", QuotedBool((flags & kAccNative) != 0));
@@ -1265,7 +1242,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
}
// Null terminate and display.
*cp++ = '\0';
- std::string dot(DescriptorToDotWrapper(tmp_buf));
+ std::string dot(DescriptorToDot(tmp_buf));
fprintf(out_file_, "<parameter name=\"arg%d\" type=\"%s\">\n"
"</parameter>\n", arg_num++, dot.c_str());
} // while
@@ -1309,7 +1286,7 @@ void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedV
}
} else if (options_.output_format_ == kOutputXml) {
fprintf(out_file_, "<field name=\"%s\"\n", name);
- std::string dot(DescriptorToDotWrapper(type_descriptor));
+ std::string dot(DescriptorToDot(type_descriptor));
fprintf(out_file_, " type=\"%s\"\n", dot.c_str());
fprintf(out_file_, " transient=%s\n", QuotedBool((flags & kAccTransient) != 0));
fprintf(out_file_, " volatile=%s\n", QuotedBool((flags & kAccVolatile) != 0));
@@ -1415,10 +1392,10 @@ void DexLayout::DumpClass(int idx, char** last_package) {
}
fprintf(out_file_, " Interfaces -\n");
} else {
- std::string dot(DescriptorClassToDot(class_descriptor));
+ std::string dot(DescriptorClassToName(class_descriptor));
fprintf(out_file_, "<class name=\"%s\"\n", dot.c_str());
if (superclass_descriptor != nullptr) {
- dot = DescriptorToDotWrapper(superclass_descriptor);
+ dot = DescriptorToDot(superclass_descriptor);
fprintf(out_file_, " extends=\"%s\"\n", dot.c_str());
}
fprintf(out_file_, " interface=%s\n",
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index fdd0b62d3e..c7a5f79cb2 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2152,7 +2152,7 @@ void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj,
LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:";
region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
- MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), /* terse */ true);
LOG(FATAL) << "Invalid reference " << ref
<< " referenced from object " << obj << " at offset " << offset;
}
@@ -2245,7 +2245,7 @@ void ConcurrentCopying::AssertToSpaceInvariant(GcRootSource* gc_root_source,
LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:";
region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
- MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), /* terse */ true);
LOG(FATAL) << "Invalid reference " << ref;
}
} else {
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 997d3b6f87..58a75ee189 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -446,7 +446,7 @@ class MarkSweep::MarkObjectSlowPath {
!large_object_space->Contains(obj)))) {
// Lowest priority logging first:
PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
- MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), /* terse */ true);
// Buffer the output in the string stream since it is more important than the stack traces
// and we want it to have log priority. The stack traces are printed from Runtime::Abort
// which is called from LOG(FATAL) but before the abort message.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 7913354433..bf06cf9758 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -642,7 +642,7 @@ Heap::Heap(size_t initial_size,
bool no_gap = MemMap::CheckNoGaps(*first_space->GetMemMap(), *non_moving_space_->GetMemMap());
if (!no_gap) {
PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
- MemMap::DumpMaps(LOG_STREAM(ERROR), true);
+ MemMap::DumpMaps(LOG_STREAM(ERROR), /* terse */ true);
LOG(FATAL) << "There's a gap between the image space and the non-moving space";
}
}
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index fb5db1147f..d6a2fa0cb5 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -87,7 +87,7 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
bool fatal) const {
// Lowest priority logging first:
PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT);
- MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), /* terse */ true);
// Buffer the output in the string stream since it is more important than the stack traces
// and we want it to have log priority. The stack traces are printed from Runtime::Abort
// which is called from LOG(FATAL) but before the abort message.
diff --git a/runtime/interpreter/mterp/arm/instruction_end.S b/runtime/interpreter/mterp/arm/instruction_end.S
index 32c725c7d9..f90ebd0221 100644
--- a/runtime/interpreter/mterp/arm/instruction_end.S
+++ b/runtime/interpreter/mterp/arm/instruction_end.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmInstructionEnd, #object
+ .hidden artMterpAsmInstructionEnd
.global artMterpAsmInstructionEnd
artMterpAsmInstructionEnd:
diff --git a/runtime/interpreter/mterp/arm/instruction_end_alt.S b/runtime/interpreter/mterp/arm/instruction_end_alt.S
index f90916fc02..0b66dbb947 100644
--- a/runtime/interpreter/mterp/arm/instruction_end_alt.S
+++ b/runtime/interpreter/mterp/arm/instruction_end_alt.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmAltInstructionEnd, #object
+ .hidden artMterpAsmAltInstructionEnd
.global artMterpAsmAltInstructionEnd
artMterpAsmAltInstructionEnd:
diff --git a/runtime/interpreter/mterp/arm/instruction_end_sister.S b/runtime/interpreter/mterp/arm/instruction_end_sister.S
index c5f4886697..71c0300f6d 100644
--- a/runtime/interpreter/mterp/arm/instruction_end_sister.S
+++ b/runtime/interpreter/mterp/arm/instruction_end_sister.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmSisterEnd, #object
+ .hidden artMterpAsmSisterEnd
.global artMterpAsmSisterEnd
artMterpAsmSisterEnd:
diff --git a/runtime/interpreter/mterp/arm/instruction_start.S b/runtime/interpreter/mterp/arm/instruction_start.S
index 8874c20540..b7e9cf51e4 100644
--- a/runtime/interpreter/mterp/arm/instruction_start.S
+++ b/runtime/interpreter/mterp/arm/instruction_start.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmInstructionStart, #object
+ .hidden artMterpAsmInstructionStart
.global artMterpAsmInstructionStart
artMterpAsmInstructionStart = .L_op_nop
.text
diff --git a/runtime/interpreter/mterp/arm/instruction_start_alt.S b/runtime/interpreter/mterp/arm/instruction_start_alt.S
index 0c9ffdb7d6..7a67ba064c 100644
--- a/runtime/interpreter/mterp/arm/instruction_start_alt.S
+++ b/runtime/interpreter/mterp/arm/instruction_start_alt.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmAltInstructionStart, #object
+ .hidden artMterpAsmAltInstructionStart
.global artMterpAsmAltInstructionStart
artMterpAsmAltInstructionStart = .L_ALT_op_nop
.text
diff --git a/runtime/interpreter/mterp/arm/instruction_start_sister.S b/runtime/interpreter/mterp/arm/instruction_start_sister.S
index 2ec51f7261..0036061605 100644
--- a/runtime/interpreter/mterp/arm/instruction_start_sister.S
+++ b/runtime/interpreter/mterp/arm/instruction_start_sister.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmSisterStart, #object
+ .hidden artMterpAsmSisterStart
.global artMterpAsmSisterStart
.text
.balign 4
diff --git a/runtime/interpreter/mterp/arm64/instruction_end.S b/runtime/interpreter/mterp/arm64/instruction_end.S
index 32c725c7d9..f90ebd0221 100644
--- a/runtime/interpreter/mterp/arm64/instruction_end.S
+++ b/runtime/interpreter/mterp/arm64/instruction_end.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmInstructionEnd, #object
+ .hidden artMterpAsmInstructionEnd
.global artMterpAsmInstructionEnd
artMterpAsmInstructionEnd:
diff --git a/runtime/interpreter/mterp/arm64/instruction_end_alt.S b/runtime/interpreter/mterp/arm64/instruction_end_alt.S
index f90916fc02..0b66dbb947 100644
--- a/runtime/interpreter/mterp/arm64/instruction_end_alt.S
+++ b/runtime/interpreter/mterp/arm64/instruction_end_alt.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmAltInstructionEnd, #object
+ .hidden artMterpAsmAltInstructionEnd
.global artMterpAsmAltInstructionEnd
artMterpAsmAltInstructionEnd:
diff --git a/runtime/interpreter/mterp/arm64/instruction_end_sister.S b/runtime/interpreter/mterp/arm64/instruction_end_sister.S
index c5f4886697..71c0300f6d 100644
--- a/runtime/interpreter/mterp/arm64/instruction_end_sister.S
+++ b/runtime/interpreter/mterp/arm64/instruction_end_sister.S
@@ -1,3 +1,5 @@
+ .type artMterpAsmSisterEnd, #object
+ .hidden artMterpAsmSisterEnd
.global artMterpAsmSisterEnd
artMterpAsmSisterEnd:
diff --git a/runtime/interpreter/mterp/arm64/instruction_start.S b/runtime/interpreter/mterp/arm64/instruction_start.S
index 8874c20540..b7e9cf51e4 100644
--- a/runtime/interpreter/mterp/arm64/instruction_start.S
+++ b/runtime/interpreter/mterp/arm64/instruction_start.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmInstructionStart, #object
+ .hidden artMterpAsmInstructionStart
.global artMterpAsmInstructionStart
artMterpAsmInstructionStart = .L_op_nop
.text
diff --git a/runtime/interpreter/mterp/arm64/instruction_start_alt.S b/runtime/interpreter/mterp/arm64/instruction_start_alt.S
index 0c9ffdb7d6..7a67ba064c 100644
--- a/runtime/interpreter/mterp/arm64/instruction_start_alt.S
+++ b/runtime/interpreter/mterp/arm64/instruction_start_alt.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmAltInstructionStart, #object
+ .hidden artMterpAsmAltInstructionStart
.global artMterpAsmAltInstructionStart
artMterpAsmAltInstructionStart = .L_ALT_op_nop
.text
diff --git a/runtime/interpreter/mterp/arm64/instruction_start_sister.S b/runtime/interpreter/mterp/arm64/instruction_start_sister.S
index 2ec51f7261..0036061605 100644
--- a/runtime/interpreter/mterp/arm64/instruction_start_sister.S
+++ b/runtime/interpreter/mterp/arm64/instruction_start_sister.S
@@ -1,4 +1,6 @@
+ .type artMterpAsmSisterStart, #object
+ .hidden artMterpAsmSisterStart
.global artMterpAsmSisterStart
.text
.balign 4
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index b73067f64a..394a84924c 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -398,6 +398,8 @@ ENTRY ExecuteMterpImpl
/* File: arm/instruction_start.S */
+ .type artMterpAsmInstructionStart, #object
+ .hidden artMterpAsmInstructionStart
.global artMterpAsmInstructionStart
artMterpAsmInstructionStart = .L_op_nop
.text
@@ -7513,6 +7515,8 @@ constvalop_long_to_double:
.balign 128
/* File: arm/instruction_end.S */
+ .type artMterpAsmInstructionEnd, #object
+ .hidden artMterpAsmInstructionEnd
.global artMterpAsmInstructionEnd
artMterpAsmInstructionEnd:
@@ -7524,6 +7528,8 @@ artMterpAsmInstructionEnd:
*/
/* File: arm/instruction_start_sister.S */
+ .type artMterpAsmSisterStart, #object
+ .hidden artMterpAsmSisterStart
.global artMterpAsmSisterStart
.text
.balign 4
@@ -7593,11 +7599,15 @@ d2l_maybeNaN:
bx lr @ return 0 for NaN
/* File: arm/instruction_end_sister.S */
+ .type artMterpAsmSisterEnd, #object
+ .hidden artMterpAsmSisterEnd
.global artMterpAsmSisterEnd
artMterpAsmSisterEnd:
/* File: arm/instruction_start_alt.S */
+ .type artMterpAsmAltInstructionStart, #object
+ .hidden artMterpAsmAltInstructionStart
.global artMterpAsmAltInstructionStart
artMterpAsmAltInstructionStart = .L_ALT_op_nop
.text
@@ -12213,6 +12223,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop
.balign 128
/* File: arm/instruction_end_alt.S */
+ .type artMterpAsmAltInstructionEnd, #object
+ .hidden artMterpAsmAltInstructionEnd
.global artMterpAsmAltInstructionEnd
artMterpAsmAltInstructionEnd:
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 770b109f41..5f4aa4f256 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -429,6 +429,8 @@ ENTRY ExecuteMterpImpl
/* File: arm64/instruction_start.S */
+ .type artMterpAsmInstructionStart, #object
+ .hidden artMterpAsmInstructionStart
.global artMterpAsmInstructionStart
artMterpAsmInstructionStart = .L_op_nop
.text
@@ -7078,6 +7080,8 @@ artMterpAsmInstructionStart = .L_op_nop
.balign 128
/* File: arm64/instruction_end.S */
+ .type artMterpAsmInstructionEnd, #object
+ .hidden artMterpAsmInstructionEnd
.global artMterpAsmInstructionEnd
artMterpAsmInstructionEnd:
@@ -7089,6 +7093,8 @@ artMterpAsmInstructionEnd:
*/
/* File: arm64/instruction_start_sister.S */
+ .type artMterpAsmSisterStart, #object
+ .hidden artMterpAsmSisterStart
.global artMterpAsmSisterStart
.text
.balign 4
@@ -7096,6 +7102,8 @@ artMterpAsmSisterStart:
/* File: arm64/instruction_end_sister.S */
+ .type artMterpAsmSisterEnd, #object
+ .hidden artMterpAsmSisterEnd
.global artMterpAsmSisterEnd
artMterpAsmSisterEnd:
@@ -7409,6 +7417,8 @@ MterpProfileActive:
/* File: arm64/instruction_start_alt.S */
+ .type artMterpAsmAltInstructionStart, #object
+ .hidden artMterpAsmAltInstructionStart
.global artMterpAsmAltInstructionStart
artMterpAsmAltInstructionStart = .L_ALT_op_nop
.text
@@ -11768,6 +11778,8 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop
.balign 128
/* File: arm64/instruction_end_alt.S */
+ .type artMterpAsmAltInstructionEnd, #object
+ .hidden artMterpAsmAltInstructionEnd
.global artMterpAsmAltInstructionEnd
artMterpAsmAltInstructionEnd:
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index c78eb4942a..3f709199b9 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -103,6 +103,7 @@ unspecified registers or condition codes.
#if defined(__APPLE__)
#define MACRO_LITERAL(value) $(value)
#define FUNCTION_TYPE(name)
+ #define OBJECT_TYPE(name)
#define SIZE(start,end)
// Mac OS' symbols have an _ prefix.
#define SYMBOL(name) _ ## name
@@ -110,6 +111,7 @@ unspecified registers or condition codes.
#else
#define MACRO_LITERAL(value) $value
#define FUNCTION_TYPE(name) .type name, @function
+ #define OBJECT_TYPE(name) .type name, @object
#define SIZE(start,end) .size start, .-end
#define SYMBOL(name) name
#define ASM_HIDDEN .hidden
@@ -407,6 +409,8 @@ SYMBOL(ExecuteMterpImpl):
/* File: x86/instruction_start.S */
+ OBJECT_TYPE(artMterpAsmInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionStart)
.global SYMBOL(artMterpAsmInstructionStart)
SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.text
@@ -6473,6 +6477,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.balign 128
/* File: x86/instruction_end.S */
+ OBJECT_TYPE(artMterpAsmInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionEnd)
.global SYMBOL(artMterpAsmInstructionEnd)
SYMBOL(artMterpAsmInstructionEnd):
@@ -6484,6 +6490,8 @@ SYMBOL(artMterpAsmInstructionEnd):
*/
/* File: x86/instruction_start_sister.S */
+ OBJECT_TYPE(artMterpAsmSisterStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterStart)
.global SYMBOL(artMterpAsmSisterStart)
.text
.balign 4
@@ -6491,11 +6499,15 @@ SYMBOL(artMterpAsmSisterStart):
/* File: x86/instruction_end_sister.S */
+ OBJECT_TYPE(artMterpAsmSisterEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterEnd)
.global SYMBOL(artMterpAsmSisterEnd)
SYMBOL(artMterpAsmSisterEnd):
/* File: x86/instruction_start_alt.S */
+ OBJECT_TYPE(artMterpAsmAltInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionStart)
.global SYMBOL(artMterpAsmAltInstructionStart)
.text
SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
@@ -12647,6 +12659,8 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
.balign 128
/* File: x86/instruction_end_alt.S */
+ OBJECT_TYPE(artMterpAsmAltInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionEnd)
.global SYMBOL(artMterpAsmAltInstructionEnd)
SYMBOL(artMterpAsmAltInstructionEnd):
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index 524dce412e..89d56372af 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -99,6 +99,7 @@ unspecified registers or condition codes.
#if defined(__APPLE__)
#define MACRO_LITERAL(value) $(value)
#define FUNCTION_TYPE(name)
+ #define OBJECT_TYPE(name)
#define SIZE(start,end)
// Mac OS' symbols have an _ prefix.
#define SYMBOL(name) _ ## name
@@ -106,6 +107,7 @@ unspecified registers or condition codes.
#else
#define MACRO_LITERAL(value) $value
#define FUNCTION_TYPE(name) .type name, @function
+ #define OBJECT_TYPE(name) .type name, @object
#define SIZE(start,end) .size start, .-end
#define SYMBOL(name) name
#define ASM_HIDDEN .hidden
@@ -389,6 +391,8 @@ SYMBOL(ExecuteMterpImpl):
/* File: x86_64/instruction_start.S */
+ OBJECT_TYPE(artMterpAsmInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionStart)
.global SYMBOL(artMterpAsmInstructionStart)
SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.text
@@ -6220,6 +6224,8 @@ movswl %ax, %eax
.balign 128
/* File: x86_64/instruction_end.S */
+ OBJECT_TYPE(artMterpAsmInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionEnd)
.global SYMBOL(artMterpAsmInstructionEnd)
SYMBOL(artMterpAsmInstructionEnd):
@@ -6231,6 +6237,8 @@ SYMBOL(artMterpAsmInstructionEnd):
*/
/* File: x86_64/instruction_start_sister.S */
+ OBJECT_TYPE(artMterpAsmSisterStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterStart)
.global SYMBOL(artMterpAsmSisterStart)
.text
.balign 4
@@ -6238,11 +6246,15 @@ SYMBOL(artMterpAsmSisterStart):
/* File: x86_64/instruction_end_sister.S */
+ OBJECT_TYPE(artMterpAsmSisterEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterEnd)
.global SYMBOL(artMterpAsmSisterEnd)
SYMBOL(artMterpAsmSisterEnd):
/* File: x86_64/instruction_start_alt.S */
+ OBJECT_TYPE(artMterpAsmAltInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionStart)
.global SYMBOL(artMterpAsmAltInstructionStart)
.text
SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
@@ -11882,6 +11894,8 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
.balign 128
/* File: x86_64/instruction_end_alt.S */
+ OBJECT_TYPE(artMterpAsmAltInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionEnd)
.global SYMBOL(artMterpAsmAltInstructionEnd)
SYMBOL(artMterpAsmAltInstructionEnd):
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 6f31228005..a79db27abf 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -96,6 +96,7 @@ unspecified registers or condition codes.
#if defined(__APPLE__)
#define MACRO_LITERAL(value) $$(value)
#define FUNCTION_TYPE(name)
+ #define OBJECT_TYPE(name)
#define SIZE(start,end)
// Mac OS' symbols have an _ prefix.
#define SYMBOL(name) _ ## name
@@ -103,6 +104,7 @@ unspecified registers or condition codes.
#else
#define MACRO_LITERAL(value) $$value
#define FUNCTION_TYPE(name) .type name, @function
+ #define OBJECT_TYPE(name) .type name, @object
#define SIZE(start,end) .size start, .-end
#define SYMBOL(name) name
#define ASM_HIDDEN .hidden
diff --git a/runtime/interpreter/mterp/x86/instruction_end.S b/runtime/interpreter/mterp/x86/instruction_end.S
index 3a02a212e6..94587f83b7 100644
--- a/runtime/interpreter/mterp/x86/instruction_end.S
+++ b/runtime/interpreter/mterp/x86/instruction_end.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionEnd)
.global SYMBOL(artMterpAsmInstructionEnd)
SYMBOL(artMterpAsmInstructionEnd):
diff --git a/runtime/interpreter/mterp/x86/instruction_end_alt.S b/runtime/interpreter/mterp/x86/instruction_end_alt.S
index 33c2b8e2a0..7757bce9a7 100644
--- a/runtime/interpreter/mterp/x86/instruction_end_alt.S
+++ b/runtime/interpreter/mterp/x86/instruction_end_alt.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmAltInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionEnd)
.global SYMBOL(artMterpAsmAltInstructionEnd)
SYMBOL(artMterpAsmAltInstructionEnd):
diff --git a/runtime/interpreter/mterp/x86/instruction_end_sister.S b/runtime/interpreter/mterp/x86/instruction_end_sister.S
index ea14b11ede..8eb79accdf 100644
--- a/runtime/interpreter/mterp/x86/instruction_end_sister.S
+++ b/runtime/interpreter/mterp/x86/instruction_end_sister.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmSisterEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterEnd)
.global SYMBOL(artMterpAsmSisterEnd)
SYMBOL(artMterpAsmSisterEnd):
diff --git a/runtime/interpreter/mterp/x86/instruction_start.S b/runtime/interpreter/mterp/x86/instruction_start.S
index ca711de00c..5d29a81993 100644
--- a/runtime/interpreter/mterp/x86/instruction_start.S
+++ b/runtime/interpreter/mterp/x86/instruction_start.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionStart)
.global SYMBOL(artMterpAsmInstructionStart)
SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.text
diff --git a/runtime/interpreter/mterp/x86/instruction_start_alt.S b/runtime/interpreter/mterp/x86/instruction_start_alt.S
index 9272a6a7b0..8dcf5bfaf9 100644
--- a/runtime/interpreter/mterp/x86/instruction_start_alt.S
+++ b/runtime/interpreter/mterp/x86/instruction_start_alt.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmAltInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionStart)
.global SYMBOL(artMterpAsmAltInstructionStart)
.text
SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
diff --git a/runtime/interpreter/mterp/x86/instruction_start_sister.S b/runtime/interpreter/mterp/x86/instruction_start_sister.S
index b9ac994d32..796e98b09a 100644
--- a/runtime/interpreter/mterp/x86/instruction_start_sister.S
+++ b/runtime/interpreter/mterp/x86/instruction_start_sister.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmSisterStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterStart)
.global SYMBOL(artMterpAsmSisterStart)
.text
.balign 4
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index 4ebe95e987..0332ce272c 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -92,6 +92,7 @@ unspecified registers or condition codes.
#if defined(__APPLE__)
#define MACRO_LITERAL(value) $$(value)
#define FUNCTION_TYPE(name)
+ #define OBJECT_TYPE(name)
#define SIZE(start,end)
// Mac OS' symbols have an _ prefix.
#define SYMBOL(name) _ ## name
@@ -99,6 +100,7 @@ unspecified registers or condition codes.
#else
#define MACRO_LITERAL(value) $$value
#define FUNCTION_TYPE(name) .type name, @function
+ #define OBJECT_TYPE(name) .type name, @object
#define SIZE(start,end) .size start, .-end
#define SYMBOL(name) name
#define ASM_HIDDEN .hidden
diff --git a/runtime/interpreter/mterp/x86_64/instruction_end.S b/runtime/interpreter/mterp/x86_64/instruction_end.S
index 3a02a212e6..94587f83b7 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_end.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_end.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionEnd)
.global SYMBOL(artMterpAsmInstructionEnd)
SYMBOL(artMterpAsmInstructionEnd):
diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_alt.S b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S
index 33c2b8e2a0..7757bce9a7 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_end_alt.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmAltInstructionEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionEnd)
.global SYMBOL(artMterpAsmAltInstructionEnd)
SYMBOL(artMterpAsmAltInstructionEnd):
diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_sister.S b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S
index ea14b11ede..8eb79accdf 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_end_sister.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S
@@ -1,3 +1,5 @@
+ OBJECT_TYPE(artMterpAsmSisterEnd)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterEnd)
.global SYMBOL(artMterpAsmSisterEnd)
SYMBOL(artMterpAsmSisterEnd):
diff --git a/runtime/interpreter/mterp/x86_64/instruction_start.S b/runtime/interpreter/mterp/x86_64/instruction_start.S
index ca711de00c..5d29a81993 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_start.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_start.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmInstructionStart)
.global SYMBOL(artMterpAsmInstructionStart)
SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.text
diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_alt.S b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S
index 9272a6a7b0..8dcf5bfaf9 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_start_alt.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmAltInstructionStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmAltInstructionStart)
.global SYMBOL(artMterpAsmAltInstructionStart)
.text
SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_sister.S b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S
index b9ac994d32..796e98b09a 100644
--- a/runtime/interpreter/mterp/x86_64/instruction_start_sister.S
+++ b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S
@@ -1,4 +1,6 @@
+ OBJECT_TYPE(artMterpAsmSisterStart)
+ ASM_HIDDEN SYMBOL(artMterpAsmSisterStart)
.global SYMBOL(artMterpAsmSisterStart)
.text
.balign 4
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
index 374e136885..25ec3f53b1 100644
--- a/test/565-checker-condition-liveness/src/Main.java
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -30,26 +30,52 @@ public class Main {
public static int p(float arg) {
return (arg > 5.0f) ? 0 : -1;
}
+
+ /// CHECK-START-{ARM,ARM64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[23,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
+ /// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
+ /// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+ /// CHECK-DAG: ArrayLength liveness:22
+ /// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
+ /// CHECK-DAG: TryBoundary
+
+ /// CHECK-START-{ARM,ARM64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
+ /// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
+ /// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+ /// CHECK-DAG: ArrayLength liveness:22
+ /// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
+ /// CHECK-DAG: TryBoundary
- /// CHECK-START: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
+ // X86 and X86_64 generate at use site the ArrayLength, meaning only the BoundsCheck will have environment uses.
+ /// CHECK-START-{X86,X86_64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[25]
/// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25]
/// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+ /// CHECK-DAG: ArrayLength liveness:22
/// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
/// CHECK-DAG: TryBoundary
- /// CHECK-START-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-START-{X86,X86_64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,25]
/// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25]
/// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+ /// CHECK-DAG: ArrayLength liveness:22
/// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
/// CHECK-DAG: TryBoundary
+
//
// A value live at a throwing instruction in a try block may be copied by
// the exception handler to its location at the top of the catch block.
@@ -60,22 +86,44 @@ public class Main {
}
}
- /// CHECK-START: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-START-{ARM,ARM64}: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,19,21]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,19,21]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[]
+ /// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
+ /// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:16
+ /// CHECK-DAG: ArrayLength liveness:18
+ /// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+
+ /// CHECK-START-{ARM,ARM64}-DEBUGGABLE: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,19,21]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,19,21]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,19,21]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[19,21]
+ /// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
+ /// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:16
+ /// CHECK-DAG: ArrayLength liveness:18
+ /// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
+
+ /// CHECK-START-{X86,X86_64}: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[]
/// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,21]
/// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,21]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:16
+ /// CHECK-DAG: ArrayLength liveness:18
/// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
- /// CHECK-START-DEBUGGABLE: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-START-{X86,X86_64}-DEBUGGABLE: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,21]
/// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,21]
/// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,21]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[21]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:16
+ /// CHECK-DAG: ArrayLength liveness:18
/// CHECK-DAG: BoundsCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
public static void testBoundsCheck(int x, Object y, int[] a) {
a[1] = x;
@@ -90,12 +138,22 @@ public class Main {
/// CHECK-DAG: NullCheck env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:18
/// CHECK-DAG: ArrayLength liveness:20
/// CHECK-DAG: Deoptimize env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
+
+ /// CHECK-START-{ARM,ARM64}-DEBUGGABLE: void Main.testDeoptimize(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[13,21,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[13,21,25]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[13,21,25]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 env_uses:[21,25]
+ /// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:12
+ /// CHECK-DAG: NullCheck env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:18
+ /// CHECK-DAG: ArrayLength liveness:20
+ /// CHECK-DAG: Deoptimize env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:24
- /// CHECK-START-DEBUGGABLE: void Main.testDeoptimize(int, java.lang.Object, int[]) liveness (after)
+ /// CHECK-START-{X86,X86_64}-DEBUGGABLE: void Main.testDeoptimize(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[13,21,25]
/// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[13,21,25]
/// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[13,21,25]
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 env_uses:[19,25]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 env_uses:[21,25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:12
/// CHECK-DAG: NullCheck env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:18
/// CHECK-DAG: ArrayLength liveness:20
diff --git a/test/dexdump/bytecodes.txt b/test/dexdump/bytecodes.txt
index e1a381ec09..1ed66e8a10 100644
--- a/test/dexdump/bytecodes.txt
+++ b/test/dexdump/bytecodes.txt
@@ -176,7 +176,7 @@ Class #3 -
ins : 1
outs : 1
insns size : 4 16-bit code units
-0009a8: |[0009a8] com.google.android.test.R.attr.<init>:()V
+0009a8: |[0009a8] com.google.android.test.R$attr.<init>:()V
0009b8: 7010 1900 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0019
0009be: 0e00 |0003: return-void
catches : (none)
@@ -228,7 +228,7 @@ Class #4 -
ins : 1
outs : 1
insns size : 4 16-bit code units
-0009c0: |[0009c0] com.google.android.test.R.drawable.<init>:()V
+0009c0: |[0009c0] com.google.android.test.R$drawable.<init>:()V
0009d0: 7010 1900 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0019
0009d6: 0e00 |0003: return-void
catches : (none)
diff --git a/test/dexdump/bytecodes.xml b/test/dexdump/bytecodes.xml
index d08c2e929c..d4ee3a7eee 100755
--- a/test/dexdump/bytecodes.xml
+++ b/test/dexdump/bytecodes.xml
@@ -71,7 +71,7 @@
>
</constructor>
</class>
-<class name="R.attr"
+<class name="R$attr"
extends="java.lang.Object"
interface="false"
abstract="false"
@@ -79,15 +79,15 @@
final="true"
visibility="public"
>
-<constructor name="R.attr"
- type="com.google.android.test.R.attr"
+<constructor name="R$attr"
+ type="com.google.android.test.R$attr"
static="false"
final="false"
visibility="public"
>
</constructor>
</class>
-<class name="R.drawable"
+<class name="R$drawable"
extends="java.lang.Object"
interface="false"
abstract="false"
@@ -105,8 +105,8 @@
value="2130837504"
>
</field>
-<constructor name="R.drawable"
- type="com.google.android.test.R.drawable"
+<constructor name="R$drawable"
+ type="com.google.android.test.R$drawable"
static="false"
final="false"
visibility="public"
diff --git a/test/dexdump/checkers.xml b/test/dexdump/checkers.xml
index 4e56ea2d66..3d3bac2d69 100755
--- a/test/dexdump/checkers.xml
+++ b/test/dexdump/checkers.xml
@@ -181,7 +181,7 @@
final="true"
visibility="public"
>
-<parameter name="arg0" type="android.content.SharedPreferences.Editor">
+<parameter name="arg0" type="android.content.SharedPreferences$Editor">
</parameter>
</method>
<method name="a"
diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt
index cfab248168..1bfa0532b2 100644
--- a/test/dexdump/invoke-custom.txt
+++ b/test/dexdump/invoke-custom.txt
@@ -58,7 +58,7 @@ Class #0 -
ins : 2
outs : 2
insns size : 4 16-bit code units
-001b18: |[001b18] TestBadBootstrapArguments.TestersConstantCallSite.<init>:(Ljava/lang/invoke/MethodHandle;)V
+001b18: |[001b18] TestBadBootstrapArguments$TestersConstantCallSite.<init>:(Ljava/lang/invoke/MethodHandle;)V
001b28: 7020 d200 1000 |0000: invoke-direct {v0, v1}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@00d2
001b2e: 0e00 |0003: return-void
catches : (none)
@@ -537,7 +537,7 @@ Class #2 -
ins : 2
outs : 1
insns size : 4 16-bit code units
-002abc: |[002abc] TestInvocationKinds.Widget.<init>:(I)V
+002abc: |[002abc] TestInvocationKinds$Widget.<init>:(I)V
002acc: 7010 bf00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@00bf
002ad2: 0e00 |0003: return-void
catches : (none)
@@ -586,7 +586,7 @@ Class #3 -
ins : 1
outs : 1
insns size : 4 16-bit code units
-002ee8: |[002ee8] TestInvokeCustomWithConcurrentThreads.1.<init>:()V
+002ee8: |[002ee8] TestInvokeCustomWithConcurrentThreads$1.<init>:()V
002ef8: 7010 cf00 0000 |0000: invoke-direct {v0}, Ljava/lang/ThreadLocal;.<init>:()V // method@00cf
002efe: 0e00 |0003: return-void
catches : (none)
@@ -605,7 +605,7 @@ Class #3 -
ins : 1
outs : 1
insns size : 13 16-bit code units
-002ea0: |[002ea0] TestInvokeCustomWithConcurrentThreads.1.initialValue:()Ljava/lang/Integer;
+002ea0: |[002ea0] TestInvokeCustomWithConcurrentThreads$1.initialValue:()Ljava/lang/Integer;
002eb0: 7100 6500 0000 |0000: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.access$000:()Ljava/util/concurrent/atomic/AtomicInteger; // method@0065
002eb6: 0c00 |0003: move-result-object v0
002eb8: 6e10 f100 0000 |0004: invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicInteger;.getAndIncrement:()I // method@00f1
@@ -628,7 +628,7 @@ Class #3 -
ins : 1
outs : 1
insns size : 5 16-bit code units
-002ecc: |[002ecc] TestInvokeCustomWithConcurrentThreads.1.initialValue:()Ljava/lang/Object;
+002ecc: |[002ecc] TestInvokeCustomWithConcurrentThreads$1.initialValue:()Ljava/lang/Object;
002edc: 6e10 6100 0100 |0000: invoke-virtual {v1}, LTestInvokeCustomWithConcurrentThreads$1;.initialValue:()Ljava/lang/Integer; // method@0061
002ee2: 0c00 |0003: move-result-object v0
002ee4: 1100 |0004: return-object v0
diff --git a/tools/art b/tools/art
index aebf5a6778..7bd4c54a22 100644
--- a/tools/art
+++ b/tools/art
@@ -16,28 +16,9 @@
# shell dialect that should work on the host (e.g. bash), and
# Android (e.g. mksh).
-# Globals
-ART_BINARY=dalvikvm
-DELETE_ANDROID_DATA="no"
-LAUNCH_WRAPPER=
-LIBART=libart.so
-JIT_PROFILE="no"
-ALLOW_DEFAULT_JDWP="no"
-VERBOSE="no"
-CLEAN_OAT_FILES="yes"
-EXTRA_OPTIONS=()
-
-# Follow all sym links to get the program name.
-if [ z"$BASH_SOURCE" != z ]; then
- PROG_NAME="$BASH_SOURCE"
-else
- PROG_NAME="$0"
-fi
-while [ -h "$PROG_NAME" ]; do
- # On Mac OS, readlink -f doesn't work.
- PROG_NAME="$(readlink "$PROG_NAME")"
-done
-
+######################################
+# Functions
+######################################
function find_libdir() {
# Get the actual file, $1 is the ART_BINARY_PATH and may be a symbolic link.
# Use realpath instead of readlink because Android does not have a readlink.
@@ -48,29 +29,6 @@ function find_libdir() {
fi
}
-function replace_compiler_filter_with_quicken() {
- ARGS_WITH_QUICKEN=("$@")
-
- found="false"
- ((index=0))
- while ((index <= $#)); do
- what="${ARGS_WITH_QUICKEN[$index]}"
-
- case "$what" in
- --compiler-filter=*)
- ARGS_WITH_QUICKEN[$index]="--compiler-filter=quicken"
- found="true"
- ;;
- esac
-
- ((index++))
- shift
- done
- if [ "$found" != "true" ]; then
- ARGS_WITH_QUICKEN=(-Xcompiler-option --compiler-filter=quicken "${ARGS_WITH_QUICKEN[@]}")
- fi
-}
-
function usage() {
cat 1>&2 <<EOF
Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS
@@ -224,19 +182,81 @@ function detect_boot_image_location() {
echo "$image_location"
}
-# If android logging is not explicitly set, only print warnings and errors.
-if [ -z "$ANDROID_LOG_TAGS" ]; then
- ANDROID_LOG_TAGS='*:w'
-fi
+function run_dex2oat() {
+ CLASS_LOADER_CONTEXT=
+ for dex_file in "${DEX2OAT_CLASSPATH[@]}"
+ do
+ while [ -h "$dex_file" ]; do
+ # On Mac OS, readlink -f doesn't work.
+ dex_file="$(readlink "$dex_file")"
+ done
+ # Create oat file directory.
+ verbose_run mkdir -p $(dirname "$dex_file")/oat/$ISA
+ local oat_file=$(basename "$dex_file")
+ local oat_file=$(dirname "$dex_file")/oat/$ISA/${oat_file%.*}.odex
+ # When running dex2oat use the exact same context as when running dalvikvm.
+ # (see run_art function)
+ verbose_run ANDROID_DATA=$ANDROID_DATA \
+ ANDROID_ROOT=$ANDROID_ROOT \
+ LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
+ PATH=$ANDROID_ROOT/bin:$PATH \
+ LD_USE_LOAD_BIAS=1 \
+ ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS \
+ $DEX2OAT_BINARY_PATH \
+ --runtime-arg -Xnorelocate \
+ --boot-image=$DEX2OAT_BOOT_IMAGE \
+ --instruction-set=$ISA \
+ --class-loader-context="PCL[$CLASS_LOADER_CONTEXT]" \
+ "${DEX2OAT_FLAGS[@]}" \
+ --dex-file=$dex_file \
+ --oat-file=$oat_file
+ if [[ ! -z $CLASS_LOADER_CONTEXT ]]; then
+ CLASS_LOADER_CONTEXT+=":"
+ fi
+ CLASS_LOADER_CONTEXT+="$dex_file"
+ done
+}
+
+# Extract the dex2oat flags from the list of arguments.
+# -Xcompiler-options arguments are stored in DEX2OAT_FLAGS array
+# -cp argument is split by ':' and stored in DEX2OAT_CLASSPATH
+# -Ximage argument is stored in DEX2OAT_BOOT_IMAGE
+function extract_dex2oat_flags() {
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -Xcompiler-option)
+ DEX2OAT_FLAGS+=("$2")
+ shift
+ ;;
+ -Ximage:*)
+ DEX2OAT_BOOT_IMAGE=$1
+ # Remove '-Ximage:' from the argument.
+ DEX2OAT_BOOT_IMAGE=${DEX2OAT_BOOT_IMAGE##-Ximage:}
+ ;;
+ -cp)
+ # Reset any previously parsed classpath, just like dalvikvm
+ # only supports one -cp argument.
+ DEX2OAT_CLASSPATH=()
+ # TODO: support -classpath and CLASSPATH
+ local oifs=$IFS
+ IFS=':'
+ for classpath_elem in $2
+ do
+ DEX2OAT_CLASSPATH+=("$classpath_elem")
+ done
+ shift
+ IFS=$oifs
+ ;;
+ esac
+ shift
+ done
+}
# Runs dalvikvm, returns its exit code.
# (Oat directories are cleaned up in between runs)
function run_art() {
- local image_location="$(detect_boot_image_location)"
local ret
- # First cleanup any left-over 'oat' files from the last time dalvikvm was run.
- cleanup_oat_directory_for_classpath "$@"
# Run dalvikvm.
verbose_run ANDROID_DATA="$ANDROID_DATA" \
ANDROID_ROOT="$ANDROID_ROOT" \
@@ -247,7 +267,7 @@ function run_art() {
$LAUNCH_WRAPPER $ART_BINARY_PATH $lib \
-XXlib:"$LIBART" \
-Xnorelocate \
- -Ximage:"$image_location" \
+ -Ximage:"$DEFAULT_IMAGE_LOCATION" \
"$@"
ret=$?
@@ -258,6 +278,23 @@ function run_art() {
return $ret
}
+######################################
+# Globals
+######################################
+ART_BINARY=dalvikvm
+DEX2OAT_BINARY=dex2oat
+DELETE_ANDROID_DATA="no"
+LAUNCH_WRAPPER=
+LIBART=libart.so
+JIT_PROFILE="no"
+ALLOW_DEFAULT_JDWP="no"
+VERBOSE="no"
+CLEAN_OAT_FILES="yes"
+EXTRA_OPTIONS=()
+DEX2OAT_FLAGS=()
+DEX2OAT_CLASSPATH=()
+
+# Parse arguments
while [[ "$1" = "-"* ]]; do
case "$1" in
--)
@@ -275,6 +312,7 @@ while [[ "$1" = "-"* ]]; do
;& # Fallthrough
--debug)
LIBART="libartd.so"
+ DEX2OAT_BINARY=dex2oatd
# Expect that debug mode wants all checks.
EXTRA_OPTIONS+=(-XX:SlowDebug=true)
;;
@@ -329,9 +367,21 @@ if [ $# -eq 0 ]; then
exit 1
fi
+# Follow all sym links to get the program name.
+if [ z"$BASH_SOURCE" != z ]; then
+ PROG_NAME="$BASH_SOURCE"
+else
+ PROG_NAME="$0"
+fi
+while [ -h "$PROG_NAME" ]; do
+ # On Mac OS, readlink -f doesn't work.
+ PROG_NAME="$(readlink "$PROG_NAME")"
+done
+
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
ANDROID_ROOT=$PROG_DIR/..
ART_BINARY_PATH=$ANDROID_ROOT/bin/$ART_BINARY
+ISA=$($ART_BINARY_PATH -showversion | (read art version number isa && echo $isa))
if [ ! -x "$ART_BINARY_PATH" ]; then
cat 1>&2 <<EOF
@@ -341,8 +391,31 @@ EOF
exit 1
fi
+DEX2OAT_BINARY_PATH=$ANDROID_ROOT/bin/$DEX2OAT_BINARY
+
+if [ ! -x "$DEX2OAT_BINARY_PATH" ]; then
+ echo "Warning: Android Compiler not found: $DEX2OAT_BINARY_PATH"
+fi
+
+######################################
+# Main program
+######################################
+
+# If android logging is not explicitly set, only print warnings and errors.
+if [ -z "$ANDROID_LOG_TAGS" ]; then
+ ANDROID_LOG_TAGS='*:w'
+fi
+
LIBDIR="$(find_libdir $ART_BINARY_PATH)"
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
+DEFAULT_IMAGE_LOCATION="$(detect_boot_image_location)"
+DEX2OAT_BOOT_IMAGE="$DEFAULT_IMAGE_LOCATION"
+
+# Extract the dex2oat flags from the list of arguments.
+# -Xcompiler-options arguments are stored in DEX2OAT_FLAGS array
+# -cp argument is split by ':' and stored in DEX2OAT_CLASSPATH
+# -Ximage argument is stored in DEX2OAT_BOOTIMAGE
+extract_dex2oat_flags "$@"
# If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
# and ensure we delete it at the end.
@@ -360,31 +433,34 @@ fi
if [ "$PERF" != "" ]; then
LAUNCH_WRAPPER="perf record -g --call-graph dwarf -F 10000 -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER"
- EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info)
+ DEX2OAT_FLAGS+=(--generate-debug-info)
fi
if [ "$ALLOW_DEFAULT_JDWP" = "no" ]; then
EXTRA_OPTIONS+=(-XjdwpProvider:none)
fi
+# First cleanup any left-over 'oat' files from the last time dalvikvm was run.
+cleanup_oat_directory_for_classpath "$@"
+
+# Protect additional arguments in quotes to preserve whitespaces (used by
+# run-jdwp-test.sh when running on device), '$' (may be used as part of
+# classpath) and other special characters when evaluated.
+EXTRA_OPTIONS+=("$@")
+
if [ "$JIT_PROFILE" = "yes" ]; then
# Create the profile. The runtime expects profiles to be created before
# execution.
PROFILE_PATH="$ANDROID_DATA/primary.prof"
touch "$PROFILE_PATH"
- # Replace the compiler filter with quicken so that we
- # can capture the profile.
- ARGS_WITH_QUICKEN=
- replace_compiler_filter_with_quicken "$@"
-
run_art -Xjitsaveprofilinginfo \
-Xps-min-methods-to-save:1 \
-Xps-min-classes-to-save:1 \
-Xps-min-notification-before-wake:10 \
-Xps-profile-path:$PROFILE_PATH \
-Xusejit:true \
- "${ARGS_WITH_QUICKEN[@]}" \
+ ${EXTRA_OPTIONS[@]} \
&> "$ANDROID_DATA/profile_gen.log"
EXIT_STATUS=$?
@@ -400,13 +476,20 @@ if [ "$JIT_PROFILE" = "yes" ]; then
rm -rf "$ANDROID_DATA/dalvik-cache"
# Append arguments so next invocation of run_art uses the profile.
- EXTRA_OPTIONS+=(-Xcompiler-option --profile-file="$PROFILE_PATH")
+ DEX2OAT_FLAGS+=(--profile-file="$PROFILE_PATH")
fi
-# Protect additional arguments in quotes to preserve whitespaces (used by
-# run-jdwp-test.sh when running on device), '$' (may be used as part of
-# classpath) and other special characters when evaluated.
-EXTRA_OPTIONS+=("$@")
+if [ -x "$DEX2OAT_BINARY_PATH" ]; then
+ # Run dex2oat before launching ART to generate the oat files for the classpath.
+ run_dex2oat
+fi
+
+# Do not continue if the dex2oat failed.
+EXIT_STATUS=$?
+if [ $EXIT_STATUS != 0 ]; then
+ echo "Failed dex2oat invocation" >&2
+ exit $EXIT_STATUS
+fi
run_art "${EXTRA_OPTIONS[@]}"
EXIT_STATUS=$?
diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp
index 9515ca5c50..a85bf562af 100644
--- a/tools/dexanalyze/Android.bp
+++ b/tools/dexanalyze/Android.bp
@@ -22,6 +22,7 @@ cc_defaults {
"dexanalyze.cc",
"dexanalyze_bytecode.cc",
"dexanalyze_experiments.cc",
+ "dexanalyze_strings.cc",
],
target: {
android: {
diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc
index 841719b821..040f41ba5d 100644
--- a/tools/dexanalyze/dexanalyze.cc
+++ b/tools/dexanalyze/dexanalyze.cc
@@ -23,6 +23,7 @@
#include "dexanalyze_bytecode.h"
#include "dexanalyze_experiments.h"
+#include "dexanalyze_strings.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
diff --git a/tools/dexanalyze/dexanalyze_bytecode.cc b/tools/dexanalyze/dexanalyze_bytecode.cc
index 0bb3f911a2..e6e58c0ecc 100644
--- a/tools/dexanalyze/dexanalyze_bytecode.cc
+++ b/tools/dexanalyze/dexanalyze_bytecode.cc
@@ -50,6 +50,53 @@ static inline SafeMap<T, U> SortByOrder(const SafeMap<T, U>& usage, Order order)
return ret;
}
+template <typename A, typename B>
+std::ostream& operator <<(std::ostream& os, const std::pair<A, B>& pair) {
+ return os << "{" << pair.first << ", " << pair.second << "}";
+}
+
+template <typename T, typename... Args, template <typename...> class ArrayType>
+SafeMap<size_t, T> MakeUsageMap(const ArrayType<T, Args...>& array) {
+ SafeMap<size_t, T> ret;
+ for (size_t i = 0; i < array.size(); ++i) {
+ if (array[i] > 0) {
+ ret.Put(i, array[i]);
+ }
+ }
+ return ret;
+}
+
+template <typename T, typename U, typename... Args, template <typename...> class Map>
+void PrintMostUsed(std::ostream& os,
+ const Map<T, U, Args...>& usage,
+ size_t max_count,
+ std::function<void(std::ostream& os, T)> printer =
+ [](std::ostream& os, T v) {
+ os << v;
+ }) {
+ std::vector<std::pair<U, T>> sorted;
+ uint64_t total = 0u;
+ for (const auto& pair : usage) {
+ sorted.emplace_back(pair.second, pair.first);
+ total += pair.second;
+ }
+ std::sort(sorted.rbegin(), sorted.rend());
+ uint64_t other = 0u;
+ for (auto&& pair : sorted) {
+ if (max_count > 0) {
+ os << Percent(pair.first, total) << " : ";
+ printer(os, pair.second);
+ os << "\n";
+ --max_count;
+ } else {
+ other += pair.first;
+ }
+ }
+ if (other != 0u) {
+ os << "other: " << Percent(other, total) << "\n";
+ }
+}
+
static inline std::ostream& operator<<(std::ostream& os, const std::vector<uint8_t>& bytes) {
os << std::hex;
for (const uint8_t& c : bytes) {
@@ -125,32 +172,42 @@ void NewRegisterInstructions::Dump(std::ostream& os, uint64_t total_size) const
std::vector<std::pair<size_t, std::vector<uint8_t>>> pairs;
for (auto&& pair : instruction_freq_) {
if (pair.second > 0 && !pair.first.empty()) {
- // Savings exclude one byte per occurrence and one occurence from having the macro
+ // Savings exclude one byte per occurrence and one occurrence from having the macro
// dictionary.
pairs.emplace_back((pair.second - 1) * (pair.first.size() - 1), pair.first);
}
}
std::sort(pairs.rbegin(), pairs.rend());
static constexpr size_t kMaxMacros = 128;
+ static constexpr size_t kMaxPrintedMacros = 32;
uint64_t top_instructions_savings = 0u;
for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) {
top_instructions_savings += pairs[i].first;
}
if (verbose_level_ >= VerboseLevel::kNormal) {
+ os << "Move result register distribution" << "\n";
+ PrintMostUsed(os, MakeUsageMap(move_result_reg_), 16);
+ os << "First arg register usage\n";
+ std::function<void(std::ostream& os, size_t)> printer = [&](std::ostream& os, size_t idx) {
+ os << Instruction::Name(static_cast<Instruction::Code>(idx));
+ };
+ PrintMostUsed(os, MakeUsageMap(first_arg_reg_count_), 16, printer);
+ os << "Most used field linkage pairs\n";
+ PrintMostUsed(os, field_linkage_counts_, 32);
+ os << "Current extended " << extended_field_ << "\n";
+ os << "Most used method linkage pairs\n";
+ PrintMostUsed(os, method_linkage_counts_, 32);
+ os << "Current extended " << extended_method_ << "\n";
os << "Top " << kMaxMacros << " instruction bytecode sizes and hex dump" << "\n";
for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) {
auto bytes = pairs[i].second;
// Remove opcode bytes.
bytes.erase(bytes.begin());
- os << Percent(pairs[i].first, total_size) << " "
- << Instruction::Name(static_cast<Instruction::Code>(pairs[i].second[0]))
- << "(" << bytes << ")\n";
- }
- os << "Move result register distribution" << "\n";
- const size_t move_result_total =
- std::accumulate(move_result_reg_.begin(), move_result_reg_.end(), 0u);
- for (size_t i = 0; i < move_result_reg_.size(); ++i) {
- os << i << ": " << Percent(move_result_reg_[i], move_result_total) << "\n";
+ if (i < kMaxPrintedMacros) {
+ os << Percent(pairs[i].first, total_size) << " "
+ << Instruction::Name(static_cast<Instruction::Code>(pairs[i].second[0]))
+ << "(" << bytes << ")\n";
+ }
}
}
os << "Top instructions 1b macro savings "
@@ -164,10 +221,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
std::map<size_t, TypeLinkage>& types) {
TypeLinkage& current_type = types[current_class_type.index_];
bool skip_next = false;
- for (auto inst = code_item.begin(); ; ++inst) {
- if (inst == code_item.end()) {
- break;
- }
+ for (auto inst = code_item.begin(); inst != code_item.end(); ++inst) {
if (verbose_level_ >= VerboseLevel::kEverything) {
std::cout << std::endl;
std::cout << inst->DumpString(nullptr);
@@ -182,6 +236,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
bool is_iget = false;
const Instruction::Code opcode = inst->Opcode();
Instruction::Code new_opcode = opcode;
+ ++opcode_count_[opcode];
switch (opcode) {
case Instruction::IGET:
case Instruction::IGET_WIDE:
@@ -209,18 +264,18 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
uint32_t receiver = inst->VRegB_22c();
uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
uint32_t out_reg = inst->VRegA_22c();
- if (Enabled(kExperimentInstanceFieldSelf) &&
- first_arg_reg == receiver &&
- holder_type == current_class_type) {
- if (count_types) {
- ++current_type.fields_.FindOrAdd(dex_field_idx)->second;
- } else {
- uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx);
- ExtendPrefix(&out_reg, &field_idx);
- CHECK(InstNibbles(new_opcode, {out_reg, field_idx}));
- continue;
- }
- } else if (Enabled(kExperimentInstanceField)) {
+ if (Enabled(kExperimentInstanceFieldSelf) &&
+ first_arg_reg == receiver &&
+ holder_type == current_class_type) {
+ if (count_types) {
+ ++current_type.fields_.FindOrAdd(dex_field_idx)->second;
+ } else {
+ uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx);
+ ExtendPrefix(&out_reg, &field_idx);
+ CHECK(InstNibbles(new_opcode, {out_reg, field_idx}));
+ continue;
+ }
+ } else if (Enabled(kExperimentInstanceField)) {
if (count_types) {
++current_type.types_.FindOrAdd(holder_type.index_)->second;
++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second;
@@ -288,7 +343,8 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
} else {
uint32_t type_idx = current_type.types_.Get(holder_type.index_);
uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx);
- ExtendPrefix(&type_idx, &field_idx);
+ ++field_linkage_counts_[std::make_pair(type_idx, field_idx)];
+ extended_field_ += ExtendPrefix(&type_idx, &field_idx) ? 1u : 0u;
if (InstNibbles(new_opcode, {out_reg >> 4, out_reg & 0xF, type_idx, field_idx})) {
continue;
}
@@ -313,6 +369,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
} else {
uint32_t args[6] = {};
uint32_t arg_count = inst->GetVarArgs(args);
+ const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
bool next_move_result = false;
uint32_t dest_reg = 0;
@@ -330,6 +387,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
uint32_t type_idx = current_type.types_.Get(receiver_type.index_);
uint32_t local_idx = types[receiver_type.index_].methods_.Get(method_idx);
+ ++method_linkage_counts_[std::make_pair(type_idx, local_idx)];
// If true, we always put the return value in r0.
static constexpr bool kMoveToDestReg = true;
@@ -338,15 +396,21 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
if (kMoveToDestReg && arg_count % 2 == 1) {
// Use the extra nibble to sneak in part of the type index.
new_args.push_back(local_idx >> 4);
- local_idx ^= local_idx & 0xF0;
+ local_idx &= ~0xF0;
}
- ExtendPrefix(&type_idx, &local_idx);
+ extended_method_ += ExtendPrefix(&type_idx, &local_idx) ? 1u : 0u;
new_args.push_back(type_idx);
new_args.push_back(local_idx);
if (!kMoveToDestReg) {
ExtendPrefix(&dest_reg, &local_idx);
new_args.push_back(dest_reg);
}
+ for (size_t i = 0; i < arg_count; ++i) {
+ if (args[i] == first_arg_reg) {
+ ++first_arg_reg_count_[opcode];
+ break;
+ }
+ }
new_args.insert(new_args.end(), args, args + arg_count);
if (InstNibbles(opcode, new_args)) {
skip_next = next_move_result;
@@ -467,18 +531,18 @@ void NewRegisterInstructions::Add(Instruction::Code opcode, const Instruction& i
++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())];
}
-void NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) {
+bool NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) {
if (*value1 < 16 && *value2 < 16) {
- return;
+ return false;
}
if ((*value1 >> 4) == 1 && *value2 < 16) {
InstNibbles(0xE5, {});
*value1 ^= 1u << 4;
- return;
+ return true;
} else if ((*value2 >> 4) == 1 && *value1 < 16) {
InstNibbles(0xE6, {});
*value2 ^= 1u << 4;
- return;
+ return true;
}
if (*value1 < 256 && *value2 < 256) {
// Extend each value by 4 bits.
@@ -495,6 +559,7 @@ void NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) {
}
*value1 &= 0xF;
*value2 &= 0XF;
+ return true;
}
bool NewRegisterInstructions::InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args) {
diff --git a/tools/dexanalyze/dexanalyze_bytecode.h b/tools/dexanalyze/dexanalyze_bytecode.h
index db009b03b8..015801f516 100644
--- a/tools/dexanalyze/dexanalyze_bytecode.h
+++ b/tools/dexanalyze/dexanalyze_bytecode.h
@@ -54,7 +54,11 @@ struct TypeLinkage {
class NewRegisterInstructions : public Experiment {
public:
- explicit NewRegisterInstructions(uint64_t experiments) : experiments_(experiments) {}
+ explicit NewRegisterInstructions(uint64_t experiments)
+ : experiments_(experiments),
+ move_result_reg_(256),
+ first_arg_reg_count_(256),
+ opcode_count_(256) {}
void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
void Dump(std::ostream& os, uint64_t total_size) const;
@@ -65,7 +69,7 @@ class NewRegisterInstructions : public Experiment {
std::map<size_t, TypeLinkage>& types);
void Add(Instruction::Code opcode, const Instruction& inst);
bool InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args);
- void ExtendPrefix(uint32_t* value1, uint32_t* value2);
+ bool ExtendPrefix(uint32_t* value1, uint32_t* value2);
bool Enabled(BytecodeExperiment experiment) const {
return experiments_ & (1u << static_cast<uint64_t>(experiment));
}
@@ -76,7 +80,13 @@ class NewRegisterInstructions : public Experiment {
uint64_t deduped_size_ = 0u;
uint64_t dex_code_bytes_ = 0u;
uint64_t experiments_ = std::numeric_limits<uint64_t>::max();
- std::array<size_t, 256> move_result_reg_;
+ uint64_t extended_field_ = 0u;
+ uint64_t extended_method_ = 0u;
+ std::vector<size_t> move_result_reg_;
+ std::vector<size_t> first_arg_reg_count_;
+ std::vector<size_t> opcode_count_;
+ std::map<std::pair<uint32_t, uint32_t>, size_t> method_linkage_counts_;
+ std::map<std::pair<uint32_t, uint32_t>, size_t> field_linkage_counts_;
std::map<std::vector<uint8_t>, size_t> instruction_freq_;
// Output instruction buffer.
std::vector<uint8_t> buffer_;
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index 1f6fe4694e..b124f433b3 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -208,137 +208,6 @@ void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const {
<< Percent(total_unique_non_header_bytes_, total_size) << "\n";
}
-void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
- std::set<std::string> unique_strings;
- for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
- for (size_t i = 0; i < dex_file->NumStringIds(); ++i) {
- uint32_t length = 0;
- const char* data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length);
- // Analyze if the string has any UTF16 chars.
- bool have_wide_char = false;
- const char* ptr = data;
- for (size_t j = 0; j < length; ++j) {
- have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
- }
- if (have_wide_char) {
- wide_string_bytes_ += 2 * length;
- } else {
- ascii_string_bytes_ += length;
- }
- string_data_bytes_ += ptr - data;
- unique_strings.insert(data);
- }
- }
- // Unique strings only since we want to exclude savings from multidex duplication.
- std::vector<std::string> strings(unique_strings.begin(), unique_strings.end());
- unique_strings.clear();
-
- // Tunable parameters.
- static const size_t kMinPrefixLen = 1;
- static const size_t kMaxPrefixLen = 255;
- static const size_t kPrefixConstantCost = 4;
- static const size_t kPrefixIndexCost = 2;
-
- // Calculate total shared prefix.
- std::vector<size_t> shared_len;
- prefixes_.clear();
- for (size_t i = 0; i < strings.size(); ++i) {
- size_t best_len = 0;
- if (i > 0) {
- best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
- }
- if (i < strings.size() - 1) {
- best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
- }
- best_len = std::min(best_len, kMaxPrefixLen);
- std::string prefix;
- if (best_len >= kMinPrefixLen) {
- prefix = strings[i].substr(0, best_len);
- ++prefixes_[prefix];
- }
- total_prefix_index_cost_ += kPrefixIndexCost;
- }
- // Optimize the result by moving long prefixes to shorter ones if it causes savings.
- while (true) {
- bool have_savings = false;
- auto it = prefixes_.begin();
- std::vector<std::string> longest;
- for (const auto& pair : prefixes_) {
- longest.push_back(pair.first);
- }
- std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) {
- return a.length() > b.length();
- });
- // Do longest first since this provides the best results.
- for (const std::string& s : longest) {
- it = prefixes_.find(s);
- CHECK(it != prefixes_.end());
- const std::string& prefix = it->first;
- int64_t best_savings = 0u;
- int64_t best_len = -1;
- for (int64_t len = prefix.length() - 1; len >= 0; --len) {
- auto found = prefixes_.find(prefix.substr(0, len));
- if (len != 0 && found == prefixes_.end()) {
- continue;
- }
- // Calculate savings from downgrading the prefix.
- int64_t savings = kPrefixConstantCost + prefix.length() -
- (prefix.length() - len) * it->second;
- if (savings > best_savings) {
- best_savings = savings;
- best_len = len;
- break;
- }
- }
- if (best_len != -1) {
- prefixes_[prefix.substr(0, best_len)] += it->second;
- it = prefixes_.erase(it);
- optimization_savings_ += best_savings;
- have_savings = true;
- } else {
- ++it;
- }
- }
- if (!have_savings) {
- break;
- }
- }
- total_num_prefixes_ += prefixes_.size();
- for (const auto& pair : prefixes_) {
- // 4 bytes for an offset, one for length.
- total_prefix_dict_ += pair.first.length();
- total_prefix_table_ += kPrefixConstantCost;
- total_prefix_savings_ += pair.first.length() * pair.second;
- }
-}
-
-void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
- os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
- os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
- os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
-
- // Prefix based strings.
- os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
- os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
- os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
- os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
- int64_t net_savings = total_prefix_savings_;
- net_savings -= total_prefix_dict_;
- net_savings -= total_prefix_table_;
- net_savings -= total_prefix_index_cost_;
- os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
- os << "Optimization savings " << Percent(optimization_savings_, total_size) << "\n";
- os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
- if (verbose_level_ >= VerboseLevel::kEverything) {
- std::vector<std::pair<std::string, size_t>> pairs(prefixes_.begin(), prefixes_.end());
- // Sort lexicographically.
- std::sort(pairs.begin(), pairs.end());
- for (const auto& pair : pairs) {
- os << pair.first << " : " << pair.second << "\n";
- }
- }
-}
-
void CountDexIndices::ProcessDexFiles(
const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
std::set<std::string> unique_field_names;
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 4e66b3cf3b..3542d959ba 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -62,25 +62,6 @@ class Experiment {
VerboseLevel verbose_level_ = VerboseLevel::kNormal;
};
-// Analyze string data and strings accessed from code.
-class AnalyzeStrings : public Experiment {
- public:
- void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) OVERRIDE;
- void Dump(std::ostream& os, uint64_t total_size) const OVERRIDE;
-
- private:
- int64_t wide_string_bytes_ = 0u;
- int64_t ascii_string_bytes_ = 0u;
- int64_t string_data_bytes_ = 0u;
- int64_t total_prefix_savings_ = 0u;
- int64_t total_prefix_dict_ = 0u;
- int64_t total_prefix_table_ = 0u;
- int64_t total_prefix_index_cost_ = 0u;
- int64_t total_num_prefixes_ = 0u;
- int64_t optimization_savings_ = 0u;
- std::unordered_map<std::string, size_t> prefixes_;
-};
-
// Analyze debug info sizes.
class AnalyzeDebugInfo : public Experiment {
public:
diff --git a/tools/dexanalyze/dexanalyze_strings.cc b/tools/dexanalyze/dexanalyze_strings.cc
new file mode 100644
index 0000000000..9f67ff431a
--- /dev/null
+++ b/tools/dexanalyze/dexanalyze_strings.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "dexanalyze_strings.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+
+#include "dex/class_accessor-inl.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+
+namespace art {
+namespace dexanalyze {
+
+void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+ std::set<std::string> unique_strings;
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ for (size_t i = 0; i < dex_file->NumStringIds(); ++i) {
+ uint32_t length = 0;
+ const char* data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length);
+ // Analyze if the string has any UTF16 chars.
+ bool have_wide_char = false;
+ const char* ptr = data;
+ for (size_t j = 0; j < length; ++j) {
+ have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
+ }
+ if (have_wide_char) {
+ wide_string_bytes_ += 2 * length;
+ } else {
+ ascii_string_bytes_ += length;
+ }
+ string_data_bytes_ += ptr - data;
+ unique_strings.insert(data);
+ }
+ }
+ // Unique strings only since we want to exclude savings from multidex duplication.
+ std::vector<std::string> strings(unique_strings.begin(), unique_strings.end());
+ unique_strings.clear();
+
+ // Tunable parameters.
+ static const size_t kMinPrefixLen = 1;
+ static const size_t kMaxPrefixLen = 255;
+ static const size_t kPrefixConstantCost = 4;
+ static const size_t kPrefixIndexCost = 2;
+
+ // Calculate total shared prefix.
+ std::vector<size_t> shared_len;
+ prefixes_.clear();
+ for (size_t i = 0; i < strings.size(); ++i) {
+ size_t best_len = 0;
+ if (i > 0) {
+ best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
+ }
+ if (i < strings.size() - 1) {
+ best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
+ }
+ best_len = std::min(best_len, kMaxPrefixLen);
+ std::string prefix;
+ if (best_len >= kMinPrefixLen) {
+ prefix = strings[i].substr(0, best_len);
+ ++prefixes_[prefix];
+ }
+ total_prefix_index_cost_ += kPrefixIndexCost;
+ }
+ // Optimize the result by moving long prefixes to shorter ones if it causes savings.
+ while (true) {
+ bool have_savings = false;
+ auto it = prefixes_.begin();
+ std::vector<std::string> longest;
+ for (const auto& pair : prefixes_) {
+ longest.push_back(pair.first);
+ }
+ std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) {
+ return a.length() > b.length();
+ });
+ // Do longest first since this provides the best results.
+ for (const std::string& s : longest) {
+ it = prefixes_.find(s);
+ CHECK(it != prefixes_.end());
+ const std::string& prefix = it->first;
+ int64_t best_savings = 0u;
+ int64_t best_len = -1;
+ for (int64_t len = prefix.length() - 1; len >= 0; --len) {
+ auto found = prefixes_.find(prefix.substr(0, len));
+ if (len != 0 && found == prefixes_.end()) {
+ continue;
+ }
+ // Calculate savings from downgrading the prefix.
+ int64_t savings = kPrefixConstantCost + prefix.length() -
+ (prefix.length() - len) * it->second;
+ if (savings > best_savings) {
+ best_savings = savings;
+ best_len = len;
+ break;
+ }
+ }
+ if (best_len != -1) {
+ prefixes_[prefix.substr(0, best_len)] += it->second;
+ it = prefixes_.erase(it);
+ optimization_savings_ += best_savings;
+ have_savings = true;
+ } else {
+ ++it;
+ }
+ }
+ if (!have_savings) {
+ break;
+ }
+ }
+ total_num_prefixes_ += prefixes_.size();
+ for (const auto& pair : prefixes_) {
+ // 4 bytes for an offset, one for length.
+ total_prefix_dict_ += pair.first.length();
+ total_prefix_table_ += kPrefixConstantCost;
+ total_prefix_savings_ += pair.first.length() * pair.second;
+ }
+}
+
+void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
+ os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
+ os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
+ os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
+
+ // Prefix based strings.
+ os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
+ os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
+ os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
+ os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
+ int64_t net_savings = total_prefix_savings_;
+ net_savings -= total_prefix_dict_;
+ net_savings -= total_prefix_table_;
+ net_savings -= total_prefix_index_cost_;
+ os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
+ os << "Optimization savings " << Percent(optimization_savings_, total_size) << "\n";
+ os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
+ if (verbose_level_ >= VerboseLevel::kEverything) {
+ std::vector<std::pair<std::string, size_t>> pairs(prefixes_.begin(), prefixes_.end());
+ // Sort lexicographically.
+ std::sort(pairs.begin(), pairs.end());
+ for (const auto& pair : pairs) {
+ os << pair.first << " : " << pair.second << "\n";
+ }
+ }
+}
+
+} // namespace dexanalyze
+} // namespace art
diff --git a/tools/dexanalyze/dexanalyze_strings.h b/tools/dexanalyze/dexanalyze_strings.h
new file mode 100644
index 0000000000..a5c202e31f
--- /dev/null
+++ b/tools/dexanalyze/dexanalyze_strings.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_
+#define ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_
+
+#include <array>
+#include <vector>
+#include <map>
+
+#include "base/safe_map.h"
+#include "dexanalyze_experiments.h"
+#include "dex/code_item_accessors.h"
+#include "dex/utf-inl.h"
+
+namespace art {
+namespace dexanalyze {
+
+// Analyze string data and strings accessed from code.
+class AnalyzeStrings : public Experiment {
+ public:
+ void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) OVERRIDE;
+ void Dump(std::ostream& os, uint64_t total_size) const OVERRIDE;
+
+ private:
+ int64_t wide_string_bytes_ = 0u;
+ int64_t ascii_string_bytes_ = 0u;
+ int64_t string_data_bytes_ = 0u;
+ int64_t total_prefix_savings_ = 0u;
+ int64_t total_prefix_dict_ = 0u;
+ int64_t total_prefix_table_ = 0u;
+ int64_t total_prefix_index_cost_ = 0u;
+ int64_t total_num_prefixes_ = 0u;
+ int64_t optimization_savings_ = 0u;
+ std::unordered_map<std::string, size_t> prefixes_;
+};
+
+} // namespace dexanalyze
+} // namespace art
+
+#endif // ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_
diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh
index 921a8cbe36..45c9125930 100755
--- a/tools/golem/build-target.sh
+++ b/tools/golem/build-target.sh
@@ -367,7 +367,7 @@ if [[ "$make_tarball" == "make_tarball" ]]; then
dirs_rooted+=("$root_dir/$tar_dir")
done
- execute tar -czf "${tarball}" "${dirs_rooted[@]}" --exclude .git --exclude .gitignore
+ execute tar -czf "${tarball}" --exclude ".git" --exclude ".gitignore" "${dirs_rooted[@]}"
tar_result=$?
if [[ $tar_result -ne 0 ]]; then
[[ -f $tarball ]] && rm $tarball
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index a435f2e03e..b0b5810dcc 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -311,10 +311,10 @@ if [[ "$plugin" != "" ]]; then
vm_args="$vm_args --vm-arg $plugin"
fi
-if $use_jit; then
- vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken"
- debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken"
-fi
+# Because we're running debuggable, we discard any AOT code.
+# Therefore we run de2oat with 'quicken' to avoid spending time compiling.
+vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken"
+debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken"
if $instant_jit; then
debuggee_args="$debuggee_args -Xjitthreshold:0"