Merge "DWARF: Compensate for compiler's off-by-one instruction."
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index badbd93..f9d33c1 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -175,18 +175,6 @@
CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT);
typedef typename ElfTypes::Addr Elf_Addr;
- if (method_infos.empty()) {
- return;
- }
-
- std::vector<uint32_t> binary_search_table;
- std::vector<uintptr_t> patch_locations;
- if (format == dwarf::DW_EH_FRAME_FORMAT) {
- binary_search_table.reserve(2 * method_infos.size());
- } else {
- patch_locations.reserve(method_infos.size());
- }
-
// The methods can be written in any order.
// Let's therefore sort them in the lexicographical order of the opcodes.
// This has no effect on its own. However, if the final .debug_frame section is
@@ -194,9 +182,14 @@
std::vector<const MethodDebugInfo*> sorted_method_infos;
sorted_method_infos.reserve(method_infos.size());
for (size_t i = 0; i < method_infos.size(); i++) {
- sorted_method_infos.push_back(&method_infos[i]);
+ if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) {
+ sorted_method_infos.push_back(&method_infos[i]);
+ }
}
- std::sort(
+ if (sorted_method_infos.empty()) {
+ return;
+ }
+ std::stable_sort(
sorted_method_infos.begin(),
sorted_method_infos.end(),
[](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
@@ -205,6 +198,14 @@
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
});
+ std::vector<uint32_t> binary_search_table;
+ std::vector<uintptr_t> patch_locations;
+ if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ binary_search_table.reserve(2 * sorted_method_infos.size());
+ } else {
+ patch_locations.reserve(sorted_method_infos.size());
+ }
+
// Write .eh_frame/.debug_frame section.
auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT
? builder->GetDebugFrame()
@@ -221,26 +222,21 @@
buffer_address += buffer.size();
buffer.clear();
for (const MethodDebugInfo* mi : sorted_method_infos) {
- if (!mi->deduped) { // Only one FDE per unique address.
- ArrayRef<const uint8_t> opcodes = mi->cfi;
- if (!opcodes.empty()) {
- const Elf_Addr code_address = mi->code_address +
- (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0);
- if (format == dwarf::DW_EH_FRAME_FORMAT) {
- binary_search_table.push_back(
- dchecked_integral_cast<uint32_t>(code_address));
- binary_search_table.push_back(
- dchecked_integral_cast<uint32_t>(buffer_address));
- }
- WriteFDE(is64bit, cfi_address, cie_address,
- code_address, mi->code_size,
- opcodes, format, buffer_address, &buffer,
- &patch_locations);
- cfi_section->WriteFully(buffer.data(), buffer.size());
- buffer_address += buffer.size();
- buffer.clear();
- }
+ DCHECK(!mi->deduped);
+ DCHECK(!mi->cfi.empty());
+ const Elf_Addr code_address = mi->code_address +
+ (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0);
+ if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address));
+ binary_search_table.push_back(dchecked_integral_cast<uint32_t>(buffer_address));
}
+ WriteFDE(is64bit, cfi_address, cie_address,
+ code_address, mi->code_size,
+ mi->cfi, format, buffer_address, &buffer,
+ &patch_locations);
+ cfi_section->WriteFully(buffer.data(), buffer.size());
+ buffer_address += buffer.size();
+ buffer.clear();
}
cfi_section->End();
}
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index af74d4c..a6e6f8b 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -46,6 +46,7 @@
static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
std::vector<const char*> names;
if (mi->code_item != nullptr) {
+ DCHECK(mi->dex_file != nullptr);
const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item);
if (stream != nullptr) {
DecodeUnsignedLeb128(&stream); // line.
@@ -133,6 +134,7 @@
const char* last_dex_class_desc = nullptr;
for (auto mi : compilation_unit.methods) {
+ DCHECK(mi->dex_file != nullptr);
const DexFile* dex = mi->dex_file;
const DexFile::CodeItem* dex_code = mi->code_item;
const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 0d3673d..8a4508d 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -151,6 +151,7 @@
Elf_Addr method_address = base_address + mi->code_address;
PositionInfos dex2line_map;
+ DCHECK(mi->dex_file != nullptr);
const DexFile* dex = mi->dex_file;
if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) {
continue;
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 0ca7370..4dd8024 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -39,35 +39,31 @@
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat cfi_format,
bool write_oat_patches) {
- // Add methods to .symtab.
+ // Write .strtab and .symtab.
WriteDebugSymbols(builder, method_infos, true /* with_signature */);
- // Generate CFI (stack unwinding information).
- WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
- // Write DWARF .debug_* sections.
- WriteDebugSections(builder, method_infos, write_oat_patches);
-}
-template<typename ElfTypes>
-static void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- bool write_oat_patches) {
+ // Write .debug_frame.
+ WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
+
// Group the methods into compilation units based on source file.
std::vector<ElfCompilationUnit> compilation_units;
const char* last_source_file = nullptr;
for (const MethodDebugInfo& mi : method_infos) {
- auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
- const char* source_file = mi.dex_file->GetSourceFile(dex_class_def);
- if (compilation_units.empty() || source_file != last_source_file) {
- compilation_units.push_back(ElfCompilationUnit());
+ if (mi.dex_file != nullptr) {
+ auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
+ const char* source_file = mi.dex_file->GetSourceFile(dex_class_def);
+ if (compilation_units.empty() || source_file != last_source_file) {
+ compilation_units.push_back(ElfCompilationUnit());
+ }
+ ElfCompilationUnit& cu = compilation_units.back();
+ cu.methods.push_back(&mi);
+ // All methods must have the same addressing mode otherwise the min/max below does not work.
+ DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative);
+ cu.is_code_address_text_relative = mi.is_code_address_text_relative;
+ cu.code_address = std::min(cu.code_address, mi.code_address);
+ cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size);
+ last_source_file = source_file;
}
- ElfCompilationUnit& cu = compilation_units.back();
- cu.methods.push_back(&mi);
- // All methods must have the same addressing mode otherwise the min/max below does not work.
- DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative);
- cu.is_code_address_text_relative = mi.is_code_address_text_relative;
- cu.code_address = std::min(cu.code_address, mi.code_address);
- cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size);
- last_source_file = source_file;
}
// Write .debug_line section.
@@ -185,6 +181,31 @@
}
}
+std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& header) {
+ std::map<const char*, uint32_t> trampolines = {
+ { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() },
+ { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() },
+ { "jniDlsymLookup", header.GetJniDlsymLookupOffset() },
+ { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() },
+ { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() },
+ { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() },
+ { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() },
+ };
+ std::vector<MethodDebugInfo> result;
+ for (const auto& it : trampolines) {
+ if (it.second != 0) {
+ MethodDebugInfo info = MethodDebugInfo();
+ info.trampoline_name = it.first;
+ info.isa = header.GetInstructionSet();
+ info.is_code_address_text_relative = true;
+ info.code_address = it.second - header.GetExecutableOffset();
+ info.code_size = 0; // The symbol lasts until the next symbol.
+ result.push_back(std::move(info));
+ }
+ }
+ return result;
+}
+
// Explicit instantiations
template void WriteDebugInfo<ElfTypes32>(
ElfBuilder<ElfTypes32>* builder,
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 7f5d24d..736370e 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -17,6 +17,8 @@
#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
#define ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
+#include <vector>
+
#include "base/macros.h"
#include "base/mutex.h"
#include "debug/dwarf/dwarf_constants.h"
@@ -24,6 +26,7 @@
#include "utils/array_ref.h"
namespace art {
+class OatHeader;
namespace mirror {
class Class;
}
@@ -55,6 +58,8 @@
const ArrayRef<mirror::Class*>& types)
SHARED_REQUIRES(Locks::mutator_lock_);
+std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& oat_header);
+
} // namespace debug
} // namespace art
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 0a199da..045eddd 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -39,7 +39,7 @@
static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
bool with_signature) {
- bool generated_mapping_symbol = false;
+ uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max();
auto* strtab = builder->GetStrTab();
auto* symtab = builder->GetSymTab();
@@ -64,12 +64,20 @@
if (info.deduped) {
continue; // Add symbol only for the first instance.
}
- std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature);
- if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) {
- name += " [DEDUPED]";
+ size_t name_offset;
+ if (info.trampoline_name != nullptr) {
+ name_offset = strtab->Write(info.trampoline_name);
+ } else {
+ DCHECK(info.dex_file != nullptr);
+ std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature);
+ if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) {
+ name += " [DEDUPED]";
+ }
+ // If we write method names without signature, we might see the same name multiple times.
+ name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
+ last_name = std::move(name);
+ last_name_offset = name_offset;
}
- // If we write method names without signature, we might see the same name multiple times.
- size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr;
uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0);
@@ -82,14 +90,11 @@
// Note that even if we generate just a single mapping symbol, ARM's Streamline
// requires it to match function symbol. Just address 0 does not work.
if (info.isa == kThumb2) {
- if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+ if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) {
symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE);
- generated_mapping_symbol = true;
+ mapping_symbol_address = address;
}
}
-
- last_name = std::move(name);
- last_name_offset = name_offset;
}
strtab->End();
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 1ccc705..ed1da2c 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -24,7 +24,8 @@
namespace debug {
struct MethodDebugInfo {
- const DexFile* dex_file;
+ const char* trampoline_name;
+ const DexFile* dex_file; // Native methods (trampolines) do not reference dex file.
size_t class_def_index;
uint32_t dex_method_index;
uint32_t access_flags;
@@ -37,7 +38,7 @@
uint64_t code_address;
uint32_t code_size;
uint32_t frame_size_in_bytes;
- const uint8_t* code_info;
+ const void* code_info;
ArrayRef<const uint8_t> cfi;
};
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 2b511fc..c2f19c9 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -811,7 +811,8 @@
if (compiler_options.GenerateAnyDebugInfo() && code_size != 0) {
bool has_code_info = method_header->IsOptimized();
// Record debug information for this function if we are doing that.
- debug::MethodDebugInfo info;
+ debug::MethodDebugInfo info = debug::MethodDebugInfo();
+ info.trampoline_name = nullptr;
info.dex_file = dex_file_;
info.class_def_index = class_def_index_;
info.dex_method_index = it.GetMemberIndex();
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 74aab4e..5e7a4a3 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -202,6 +202,10 @@
~OatWriter();
+ void AddMethodDebugInfos(const std::vector<debug::MethodDebugInfo>& infos) {
+ method_info_.insert(method_info_.end(), infos.begin(), infos.end());
+ }
+
ArrayRef<const debug::MethodDebugInfo> GetMethodDebugInfo() const {
return ArrayRef<const debug::MethodDebugInfo>(method_info_);
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index b95ece5..049901b 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -70,6 +70,10 @@
void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) OVERRIDE;
void VisitLessThan(HLessThan* condition) OVERRIDE;
void VisitLessThanOrEqual(HLessThanOrEqual* condition) OVERRIDE;
+ void VisitBelow(HBelow* condition) OVERRIDE;
+ void VisitBelowOrEqual(HBelowOrEqual* condition) OVERRIDE;
+ void VisitAbove(HAbove* condition) OVERRIDE;
+ void VisitAboveOrEqual(HAboveOrEqual* condition) OVERRIDE;
void VisitDiv(HDiv* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
void VisitNeg(HNeg* instruction) OVERRIDE;
@@ -559,6 +563,36 @@
block->RemoveInstruction(check);
}
+static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) {
+ HInstruction *lhs = cond->InputAt(0);
+ HInstruction *rhs = cond->InputAt(1);
+ switch (cond->GetKind()) {
+ case HInstruction::kEqual:
+ return new (arena) HEqual(rhs, lhs);
+ case HInstruction::kNotEqual:
+ return new (arena) HNotEqual(rhs, lhs);
+ case HInstruction::kLessThan:
+ return new (arena) HGreaterThan(rhs, lhs);
+ case HInstruction::kLessThanOrEqual:
+ return new (arena) HGreaterThanOrEqual(rhs, lhs);
+ case HInstruction::kGreaterThan:
+ return new (arena) HLessThan(rhs, lhs);
+ case HInstruction::kGreaterThanOrEqual:
+ return new (arena) HLessThanOrEqual(rhs, lhs);
+ case HInstruction::kBelow:
+ return new (arena) HAbove(rhs, lhs);
+ case HInstruction::kBelowOrEqual:
+ return new (arena) HAboveOrEqual(rhs, lhs);
+ case HInstruction::kAbove:
+ return new (arena) HBelow(rhs, lhs);
+ case HInstruction::kAboveOrEqual:
+ return new (arena) HBelowOrEqual(rhs, lhs);
+ default:
+ LOG(FATAL) << "Unknown ConditionType " << cond->GetKind();
+ }
+ return nullptr;
+}
+
void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) {
HInstruction* input_const = equal->GetConstantRight();
if (input_const != nullptr) {
@@ -982,13 +1016,47 @@
VisitCondition(condition);
}
-// TODO: unsigned comparisons too?
+void InstructionSimplifierVisitor::VisitBelow(HBelow* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitBelowOrEqual(HBelowOrEqual* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitAbove(HAbove* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitAboveOrEqual(HAboveOrEqual* condition) {
+ VisitCondition(condition);
+}
void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
- // Try to fold an HCompare into this HCondition.
+ // Reverse condition if left is constant. Our code generators prefer constant
+ // on the right hand side.
+ if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) {
+ HBasicBlock* block = condition->GetBlock();
+ HCondition* replacement = GetOppositeConditionSwapOps(block->GetGraph()->GetArena(), condition);
+ // If it is a fp we must set the opposite bias.
+ if (replacement != nullptr) {
+ if (condition->IsLtBias()) {
+ replacement->SetBias(ComparisonBias::kGtBias);
+ } else if (condition->IsGtBias()) {
+ replacement->SetBias(ComparisonBias::kLtBias);
+ }
+ block->ReplaceAndRemoveInstructionWith(condition, replacement);
+ RecordSimplification();
+
+ condition = replacement;
+ }
+ }
HInstruction* left = condition->GetLeft();
HInstruction* right = condition->GetRight();
+
+ // Try to fold an HCompare into this HCondition.
+
// We can only replace an HCondition which compares a Compare to 0.
// Both 'dx' and 'jack' generate a compare to 0 when compiling a
// condition with a long, float or double comparison as input.
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index f1a6e3d..c306cf9 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1548,11 +1548,11 @@
__ Beqz(argument, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadWord,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMipsWordSize,
pStringCompareTo).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
__ Bind(slow_path->GetExitLabel());
}
@@ -1707,10 +1707,10 @@
}
__ LoadFromOffset(kLoadWord,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pIndexOf).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
if (slow_path != nullptr) {
@@ -1793,10 +1793,10 @@
__ Beqz(byte_array, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadWord,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromBytes).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Bind(slow_path->GetExitLabel());
@@ -1826,10 +1826,10 @@
// all include a null check on `data` before calling that method.
__ LoadFromOffset(kLoadWord,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromChars).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -1855,10 +1855,10 @@
__ Beqz(string_to_copy, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadWord,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromString).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 5ec5b86..cf973aa 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1356,10 +1356,10 @@
__ Beqzc(argument, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadDoubleword,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pStringCompareTo).Int32Value());
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
__ Bind(slow_path->GetExitLabel());
}
@@ -1506,11 +1506,11 @@
}
__ LoadFromOffset(kLoadDoubleword,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value());
CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
if (slow_path != nullptr) {
@@ -1583,12 +1583,12 @@
__ Beqzc(byte_array, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadDoubleword,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
pAllocStringFromBytes).Int32Value());
CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Bind(slow_path->GetExitLabel());
@@ -1617,12 +1617,12 @@
//
// all include a null check on `data` before calling that method.
__ LoadFromOffset(kLoadDoubleword,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
pAllocStringFromChars).Int32Value());
CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -1648,12 +1648,12 @@
__ Beqzc(string_to_copy, slow_path->GetEntryLabel());
__ LoadFromOffset(kLoadDoubleword,
- TMP,
+ T9,
TR,
QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
pAllocStringFromString).Int32Value());
CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
- __ Jalr(TMP);
+ __ Jalr(T9);
__ Nop();
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 77ded29..98766a3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2181,7 +2181,9 @@
DCHECK(upper_bound_rti.IsSupertypeOf(rti))
<< " upper_bound_rti: " << upper_bound_rti
<< " rti: " << rti;
- DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact());
+ DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact())
+ << " upper_bound_rti: " << upper_bound_rti
+ << " rti: " << rti;
}
}
@@ -2215,6 +2217,10 @@
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
+ if (!is_exact) {
+ DCHECK(!type_handle->CannotBeAssignedFromOtherTypes())
+ << "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed";
+ }
}
return ReferenceTypeInfo(type_handle, is_exact);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ecb690f..cc01455 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -509,6 +509,8 @@
// before cursor.
HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor);
+ ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
+
private:
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited);
@@ -2964,6 +2966,8 @@
virtual IfCondition GetOppositeCondition() const = 0;
bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; }
+ bool IsLtBias() const { return GetBias() == ComparisonBias::kLtBias; }
+
ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); }
@@ -2974,13 +2978,23 @@
bool IsFPConditionTrueIfNaN() const {
DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
IfCondition if_cond = GetCondition();
- return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE);
+ if (if_cond == kCondNE) {
+ return true;
+ } else if (if_cond == kCondEQ) {
+ return false;
+ }
+ return ((if_cond == kCondGT) || (if_cond == kCondGE)) && IsGtBias();
}
bool IsFPConditionFalseIfNaN() const {
DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
IfCondition if_cond = GetCondition();
- return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ);
+ if (if_cond == kCondEQ) {
+ return true;
+ } else if (if_cond == kCondNE) {
+ return false;
+ }
+ return ((if_cond == kCondLT) || (if_cond == kCondLE)) && IsGtBias();
}
protected:
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index cc1a806..7a82063 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -919,7 +919,8 @@
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());
- debug::MethodDebugInfo info;
+ debug::MethodDebugInfo info = debug::MethodDebugInfo();
+ info.trampoline_name = nullptr;
info.dex_file = dex_file;
info.class_def_index = class_def_idx;
info.dex_method_index = method_idx;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index deaa415..75356c8 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -432,11 +432,10 @@
} else if (klass != nullptr) {
ScopedObjectAccess soa(Thread::Current());
ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
- is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes();
+ is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
} else {
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false));
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
}
@@ -518,8 +517,7 @@
HUnresolvedInstanceFieldGet* instr) {
// TODO: Use descriptor to get the actual type.
if (instr->GetFieldType() == Primitive::kPrimNot) {
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false));
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
}
@@ -527,8 +525,7 @@
HUnresolvedStaticFieldGet* instr) {
// TODO: Use descriptor to get the actual type.
if (instr->GetFieldType() == Primitive::kPrimNot) {
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false));
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
}
@@ -724,12 +721,11 @@
if (handle->IsObjectArrayClass()) {
ReferenceTypeInfo::TypeHandle component_handle =
handle_cache->NewHandle(handle->GetComponentType());
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(component_handle, /* is_exact */ false));
+ bool is_exact = component_handle->CannotBeAssignedFromOtherTypes();
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(component_handle, is_exact));
} else {
// We don't know what the parent actually is, so we fallback to object.
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(handle_cache->GetObjectClassHandle(), /* is_exact */ false));
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
}
@@ -811,8 +807,7 @@
if (first_input_index_not_null == input_count) {
// All inputs are NullConstants, set the type to object.
// This may happen in the presence of inlining.
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact */ false));
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
return;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 44e7fc9..ce4f38a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -46,6 +46,7 @@
#include "class_linker.h"
#include "compiler.h"
#include "compiler_callbacks.h"
+#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex/pass_manager.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
@@ -1034,6 +1035,9 @@
key_value_store_->Put(
OatHeader::kDebuggableKey,
compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ key_value_store_->Put(
+ OatHeader::kNativeDebuggableKey,
+ compiler_options_->native_debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
if (compiler_options_->IsExtractOnly()) {
key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue);
} else if (UseProfileGuidedCompilation()) {
@@ -1687,6 +1691,8 @@
std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
+ oat_writer->AddMethodDebugInfos(debug::MakeTrampolineInfos(oat_writer->GetOatHeader()));
+
// We need to mirror the layout of the ELF file in the compressed debug-info.
// Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above.
elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo());
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 0e17fc2..3ed5766 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -32,6 +32,8 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "class_linker-inl.h"
+#include "debug/elf_debug_writer.h"
+#include "debug/method_debug_info.h"
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "disassembler.h"
@@ -106,14 +108,6 @@
output_name_(output_name.empty() ? "symbolized.oat" : output_name) {
}
- typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
- uint32_t,
- const OatFile::OatMethod&,
- const DexFile&,
- uint32_t,
- const DexFile::CodeItem*,
- uint32_t);
-
bool Symbolize() {
const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet();
const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap(
@@ -129,8 +123,6 @@
auto* rodata = builder_->GetRoData();
auto* text = builder_->GetText();
auto* bss = builder_->GetBss();
- auto* strtab = builder_->GetStrTab();
- auto* symtab = builder_->GetSymTab();
rodata->Start();
const uint8_t* rodata_begin = oat_file_->Begin();
@@ -155,69 +147,31 @@
elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize());
builder_->WriteDynamicSection();
- Walk(&art::OatSymbolizer<ElfTypes>::RegisterForDedup);
+ Walk();
+ for (const auto& trampoline : debug::MakeTrampolineInfos(oat_file_->GetOatHeader())) {
+ method_debug_infos_.push_back(trampoline);
+ }
- NormalizeState();
-
- strtab->Start();
- strtab->Write(""); // strtab should start with empty string.
- AddTrampolineSymbols();
- Walk(&art::OatSymbolizer<ElfTypes>::AddSymbol);
- strtab->End();
-
- symtab->Start();
- symtab->Write();
- symtab->End();
+ debug::WriteDebugInfo(builder_.get(),
+ ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_),
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ true /* write_oat_patches */);
builder_->End();
return builder_->Good();
}
- void AddTrampolineSymbol(const char* name, uint32_t code_offset) {
- if (code_offset != 0) {
- uint32_t name_offset = builder_->GetStrTab()->Write(name);
- uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset();
- // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until
- // the end of the section in case of the last symbol.
- builder_->GetSymTab()->Add(
- name_offset,
- builder_->GetText(),
- builder_->GetText()->GetAddress() + symbol_value,
- /* size */ 0,
- STB_GLOBAL,
- STT_FUNC);
- }
- }
-
- void AddTrampolineSymbols() {
- const OatHeader& oat_header = oat_file_->GetOatHeader();
- AddTrampolineSymbol("interpreterToInterpreterBridge",
- oat_header.GetInterpreterToInterpreterBridgeOffset());
- AddTrampolineSymbol("interpreterToCompiledCodeBridge",
- oat_header.GetInterpreterToCompiledCodeBridgeOffset());
- AddTrampolineSymbol("jniDlsymLookup",
- oat_header.GetJniDlsymLookupOffset());
- AddTrampolineSymbol("quickGenericJniTrampoline",
- oat_header.GetQuickGenericJniTrampolineOffset());
- AddTrampolineSymbol("quickImtConflictTrampoline",
- oat_header.GetQuickImtConflictTrampolineOffset());
- AddTrampolineSymbol("quickResolutionTrampoline",
- oat_header.GetQuickResolutionTrampolineOffset());
- AddTrampolineSymbol("quickToInterpreterBridge",
- oat_header.GetQuickToInterpreterBridgeOffset());
- }
-
- void Walk(Callback callback) {
+ void Walk() {
std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
for (size_t i = 0; i < oat_dex_files.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
CHECK(oat_dex_file != nullptr);
- WalkOatDexFile(oat_dex_file, callback);
+ WalkOatDexFile(oat_dex_file);
}
}
- void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+ void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) {
std::string error_msg;
const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
if (dex_file == nullptr) {
@@ -226,13 +180,12 @@
for (size_t class_def_index = 0;
class_def_index < dex_file->NumClassDefs();
class_def_index++) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
OatClassType type = oat_class.GetType();
switch (type) {
case kOatClassAllCompiled:
case kOatClassSomeCompiled:
- WalkOatClass(oat_class, *dex_file, class_def, callback);
+ WalkOatClass(oat_class, *dex_file, class_def_index);
break;
case kOatClassNoneCompiled:
@@ -243,8 +196,10 @@
}
}
- void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
- const DexFile::ClassDef& class_def, Callback callback) {
+ void WalkOatClass(const OatFile::OatClass& oat_class,
+ const DexFile& dex_file,
+ uint32_t class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
const uint8_t* class_data = dex_file.GetClassData(class_def);
if (class_data == nullptr) { // empty class such as a marker interface?
return;
@@ -252,117 +207,62 @@
// Note: even if this is an interface or a native class, we still have to walk it, as there
// might be a static initializer.
ClassDataItemIterator it(dex_file, class_data);
- SkipAllFields(&it);
uint32_t class_method_idx = 0;
- while (it.HasNextDirectMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
- WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
- it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
- class_method_idx++;
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
- WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
- it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
- class_method_idx++;
- it.Next();
+ for (; it.HasNextStaticField(); it.Next()) { /* skip */ }
+ for (; it.HasNextInstanceField(); it.Next()) { /* skip */ }
+ for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) {
+ WalkOatMethod(oat_class.GetOatMethod(class_method_idx++),
+ dex_file,
+ class_def_index,
+ it.GetMemberIndex(),
+ it.GetMethodCodeItem(),
+ it.GetMethodAccessFlags());
}
DCHECK(!it.HasNext());
}
- void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
- const OatFile::OatMethod& oat_method, const DexFile& dex_file,
- uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
- uint32_t method_access_flags, Callback callback) {
+ void WalkOatMethod(const OatFile::OatMethod& oat_method,
+ const DexFile& dex_file,
+ uint32_t class_def_index,
+ uint32_t dex_method_index,
+ const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
if ((method_access_flags & kAccAbstract) != 0) {
// Abstract method, no code.
return;
}
- if (oat_method.GetCodeOffset() == 0) {
+ const OatHeader& oat_header = oat_file_->GetOatHeader();
+ const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
+ if (method_header == nullptr || method_header->GetCodeSize() == 0) {
// No code.
return;
}
- (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
- method_access_flags);
- }
-
- void RegisterForDedup(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED,
- uint32_t class_method_index ATTRIBUTE_UNUSED,
- const OatFile::OatMethod& oat_method,
- const DexFile& dex_file ATTRIBUTE_UNUSED,
- uint32_t dex_method_idx ATTRIBUTE_UNUSED,
- const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
- uint32_t method_access_flags ATTRIBUTE_UNUSED) {
- state_[oat_method.GetCodeOffset()]++;
- }
-
- void NormalizeState() {
- for (auto& x : state_) {
- if (x.second == 1) {
- state_[x.first] = 0;
- }
- }
- }
-
- enum class DedupState { // private
- kNotDeduplicated,
- kDeduplicatedFirst,
- kDeduplicatedOther
- };
- DedupState IsDuplicated(uint32_t offset) {
- if (state_[offset] == 0) {
- return DedupState::kNotDeduplicated;
- }
- if (state_[offset] == 1) {
- return DedupState::kDeduplicatedOther;
- }
- state_[offset] = 1;
- return DedupState::kDeduplicatedFirst;
- }
-
- void AddSymbol(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED,
- uint32_t class_method_index ATTRIBUTE_UNUSED,
- const OatFile::OatMethod& oat_method,
- const DexFile& dex_file,
- uint32_t dex_method_idx,
- const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
- uint32_t method_access_flags ATTRIBUTE_UNUSED) {
- DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
- if (dedup != DedupState::kDeduplicatedOther) {
- std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
-
- if (dedup == DedupState::kDeduplicatedFirst) {
- pretty_name = "[Dedup]" + pretty_name;
- }
-
- int name_offset = builder_->GetStrTab()->Write(pretty_name);
- uint64_t address = oat_method.GetCodeOffset() -
- oat_file_->GetOatHeader().GetExecutableOffset() +
- builder_->GetText()->GetAddress();
- builder_->GetSymTab()->Add(name_offset,
- builder_->GetText(),
- address,
- oat_method.GetQuickCodeSize(),
- STB_GLOBAL,
- STT_FUNC);
- }
+ debug::MethodDebugInfo info = debug::MethodDebugInfo();
+ info.trampoline_name = nullptr;
+ info.dex_file = &dex_file;
+ info.class_def_index = class_def_index;
+ info.dex_method_index = dex_method_index;
+ info.access_flags = method_access_flags;
+ info.code_item = code_item;
+ info.isa = oat_header.GetInstructionSet();
+ info.deduped = !seen_offsets_.insert(oat_method.GetCodeOffset()).second;
+ info.is_native_debuggable = oat_header.IsNativeDebuggable();
+ info.is_optimized = method_header->IsOptimized();
+ info.is_code_address_text_relative = true;
+ info.code_address = oat_method.GetCodeOffset() - oat_header.GetExecutableOffset();
+ info.code_size = method_header->GetCodeSize();
+ info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
+ info.code_info = info.is_optimized ? method_header->GetOptimizedCodeInfoPtr() : nullptr;
+ info.cfi = ArrayRef<uint8_t>();
+ method_debug_infos_.push_back(info);
}
private:
- static void SkipAllFields(ClassDataItemIterator* it) {
- while (it->HasNextStaticField()) {
- it->Next();
- }
- while (it->HasNextInstanceField()) {
- it->Next();
- }
- }
-
const OatFile* oat_file_;
std::unique_ptr<ElfBuilder<ElfTypes> > builder_;
- std::unordered_map<uint32_t, uint32_t> state_;
+ std::vector<debug::MethodDebugInfo> method_debug_infos_;
+ std::unordered_set<uint32_t> seen_offsets_;
const std::string output_name_;
};
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 2ac1052..ed99cba 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -468,6 +468,10 @@
return IsKeyEnabled(OatHeader::kDebuggableKey);
}
+bool OatHeader::IsNativeDebuggable() const {
+ return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
+}
+
bool OatHeader::IsExtractOnly() const {
return KeyHasValue(kCompilationType,
kExtractOnlyValue,
diff --git a/runtime/oat.h b/runtime/oat.h
index 0660e19..1d6c076 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,6 +38,7 @@
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
+ static constexpr const char* kNativeDebuggableKey = "native-debuggable";
static constexpr const char* kCompilationType = "compilation-type";
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPath = "bootclasspath";
@@ -110,6 +111,7 @@
size_t GetHeaderSize() const;
bool IsPic() const;
bool IsDebuggable() const;
+ bool IsNativeDebuggable() const;
bool IsExtractOnly() const;
bool IsProfileGuideCompiled() const;
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 2b7eca2..59c37e4 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -63,16 +63,24 @@
return gc_map_offset_ == 0 && vmap_table_offset_ != 0;
}
- CodeInfo GetOptimizedCodeInfo() const {
+ const void* GetOptimizedCodeInfoPtr() const {
DCHECK(IsOptimized());
const void* data = reinterpret_cast<const void*>(code_ - vmap_table_offset_);
- return CodeInfo(data);
+ return data;
+ }
+
+ CodeInfo GetOptimizedCodeInfo() const {
+ return CodeInfo(GetOptimizedCodeInfoPtr());
}
const uint8_t* GetCode() const {
return code_;
}
+ uint32_t GetCodeSize() const {
+ return code_size_;
+ }
+
const uint8_t* GetNativeGcMap() const {
return (gc_map_offset_ == 0) ? nullptr : code_ - gc_map_offset_;
}
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 8640148..dd4ffe4 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -1601,6 +1601,34 @@
return (short) (value & 0x17fff);
}
+ /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Const42>>,<<Arg>>]
+
+ /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Arg>>,<<Const42>>]
+
+ public static int intReverseCondition(int i) {
+ return (42 > i) ? 13 : 54;
+ }
+
+ /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
+ /// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: <<CMP:i\d+>> Compare [<<Const42>>,<<Result>>]
+
+ /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
+ /// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: <<EQ:z\d+>> Equal [<<Result>>,<<Const42>>]
+
+ public static int intReverseConditionNaN(int i) {
+ return (42 != Math.sqrt(i)) ? 13 : 54;
+ }
+
public static int runSmaliTest(String name, boolean input) {
try {
Class<?> c = Class.forName("SmaliTests");
@@ -1611,7 +1639,7 @@
}
}
- public static void main(String[] args) {
+public static void main(String[] args) {
int arg = 123456;
assertLongEquals(Add0(arg), arg);
@@ -1740,6 +1768,9 @@
assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0);
assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(intReverseCondition(41), 13);
+ assertIntEquals(intReverseConditionNaN(-5), 13);
+
for (String condition : new String[] { "Equal", "NotEqual" }) {
for (String constant : new String[] { "True", "False" }) {
for (String side : new String[] { "Rhs", "Lhs" }) {
diff --git a/test/537-checker-jump-over-jump/src/Main.java b/test/537-checker-jump-over-jump/src/Main.java
index cf9a69d..7a58e8b 100644
--- a/test/537-checker-jump-over-jump/src/Main.java
+++ b/test/537-checker-jump-over-jump/src/Main.java
@@ -24,7 +24,7 @@
//
/// CHECK: If
/// CHECK-NEXT: cmp
- /// CHECK-NEXT: jnl/ge
+ /// CHECK-NEXT: jle/ng
//
/// CHECK-DAG: <<Fibs:l\d+>> StaticFieldGet
/// CHECK-DAG: NullCheck [<<Fibs>>]
diff --git a/test/581-rtp/expected.txt b/test/581-rtp/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/581-rtp/expected.txt
diff --git a/test/581-rtp/info.txt b/test/581-rtp/info.txt
new file mode 100644
index 0000000..b57449a
--- /dev/null
+++ b/test/581-rtp/info.txt
@@ -0,0 +1,2 @@
+Regression test for the reference type propagation pass
+of the optimizing compiler that used to break invariants.
diff --git a/test/581-rtp/src/Main.java b/test/581-rtp/src/Main.java
new file mode 100644
index 0000000..09f6f6c
--- /dev/null
+++ b/test/581-rtp/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * 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 final class Main {
+
+ /// CHECK-START: void Main.main(String[]) builder (after)
+ /// CHECK: StaticFieldGet klass:Main[] exact: true
+ /// CHECK: ArrayGet klass:Main exact:true
+ /// CHECK: BoundType klass:Main exact:true
+ public static void main(String[] args) {
+ Object o = null;
+ Main f = a[0];
+ for (int i = 0; i < 2; ++i) {
+ // We used to crash in the fixed point iteration of
+ // the reference type propagation while handling the instanceof:
+ // we were expecting `o` to get the same exact-ness as the
+ // `HBoundType` but the typing of the `ArrayGet` used to not
+ // propagate the exact-ness.
+ if (o instanceof Main) {
+ field = o;
+ }
+ o = f;
+ }
+ if (field != null) {
+ throw new Error("Expected null");
+ }
+ }
+
+ static Main[] a = new Main[1];
+ static Object field;
+}