summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk1
-rw-r--r--compiler/dex/mir_optimization.cc3
-rw-r--r--compiler/dex/quick/codegen_util.cc52
-rw-r--r--compiler/dex/quick/mir_to_lir.h3
-rw-r--r--compiler/image_writer.cc2
-rw-r--r--compiler/oat_test.cc2
-rw-r--r--compiler/optimizing/boolean_simplifier.cc27
-rw-r--r--compiler/optimizing/boolean_simplifier.h2
-rw-r--r--compiler/optimizing/code_generator_arm.cc78
-rw-r--r--compiler/optimizing/code_generator_x86.cc4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc4
-rw-r--r--compiler/optimizing/nodes.cc32
-rw-r--r--compiler/optimizing/nodes.h28
-rw-r--r--compiler/optimizing/stack_map_stream.h169
-rw-r--r--compiler/optimizing/stack_map_test.cc50
-rw-r--r--compiler/utils/arm/assembler_arm.h4
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc43
-rw-r--r--compiler/utils/arm/assembler_thumb2_test.cc20
-rw-r--r--oatdump/oatdump.cc8
-rw-r--r--runtime/dex_file.h19
-rw-r--r--runtime/dex_file_test.cc9
-rw-r--r--runtime/gc/space/image_space.cc3
-rw-r--r--runtime/oat_file.cc53
-rw-r--r--runtime/oat_file.h29
-rw-r--r--runtime/oat_file_assistant.cc5
-rw-r--r--runtime/oat_file_assistant_test.cc37
-rw-r--r--runtime/oat_file_test.cc59
-rw-r--r--runtime/primitive.h4
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/stack_map.h8
-rw-r--r--test/463-checker-boolean-simplifier/src/Main.java2
31 files changed, 587 insertions, 175 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 948c7566c7..91998fa69c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -167,6 +167,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
runtime/mirror/object_test.cc \
runtime/monitor_pool_test.cc \
runtime/monitor_test.cc \
+ runtime/oat_file_test.cc \
runtime/oat_file_assistant_test.cc \
runtime/parsed_options_test.cc \
runtime/reference_table_test.cc \
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 266b7c3064..c85c3b6f21 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -671,6 +671,9 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
}
int dead_true_def = if_true->ssa_rep->defs[0];
raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0;
+ // Update ending vreg->sreg map for GC maps generation.
+ int def_vreg = SRegToVReg(mir->ssa_rep->defs[0]);
+ bb->data_flow_info->vreg_to_ssa_map_exit[def_vreg] = mir->ssa_rep->defs[0];
// We want to remove ft and tk and link bb directly to ft_ft. First, we need
// to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id)
// since the live_def above comes from ft->first_mir_insn (if_false).
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 4e7919b6d2..bd479bef5b 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -793,33 +793,43 @@ void Mir2Lir::CreateNativeGcMap() {
prev_mir = mir;
}
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ static constexpr bool kLittleEndian = true;
+#else
+ static constexpr bool kLittleEndian = false;
+#endif
+
// Build the GC map.
uint32_t reg_width = static_cast<uint32_t>((max_ref_vreg + 8) / 8);
GcMapBuilder native_gc_map_builder(&native_gc_map_,
safepoints_.size(),
max_native_offset, reg_width);
-#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN)
- ArenaVector<uint8_t> references_buffer(arena_->Adapter());
- references_buffer.resize(reg_width);
-#endif
- for (const auto& entry : safepoints_) {
- uint32_t native_offset = entry.first->offset;
- MIR* mir = entry.second;
- UpdateReferenceVRegs(mir, prev_mir, references);
-#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN)
- // Big-endian or unknown endianness, manually translate the bit vector data.
- const auto* raw_storage = references->GetRawStorage();
- for (size_t i = 0; i != reg_width; ++i) {
- references_buffer[i] = static_cast<uint8_t>(
- raw_storage[i / sizeof(raw_storage[0])] >> (8u * (i % sizeof(raw_storage[0]))));
+ if (kLittleEndian) {
+ for (const auto& entry : safepoints_) {
+ uint32_t native_offset = entry.first->offset;
+ MIR* mir = entry.second;
+ UpdateReferenceVRegs(mir, prev_mir, references);
+ // For little-endian, the bytes comprising the bit vector's raw storage are what we need.
+ native_gc_map_builder.AddEntry(native_offset,
+ reinterpret_cast<const uint8_t*>(references->GetRawStorage()));
+ prev_mir = mir;
+ }
+ } else {
+ ArenaVector<uint8_t> references_buffer(arena_->Adapter());
+ references_buffer.resize(reg_width);
+ for (const auto& entry : safepoints_) {
+ uint32_t native_offset = entry.first->offset;
+ MIR* mir = entry.second;
+ UpdateReferenceVRegs(mir, prev_mir, references);
+ // Big-endian or unknown endianness, manually translate the bit vector data.
+ const auto* raw_storage = references->GetRawStorage();
+ for (size_t i = 0; i != reg_width; ++i) {
+ references_buffer[i] = static_cast<uint8_t>(
+ raw_storage[i / sizeof(raw_storage[0])] >> (8u * (i % sizeof(raw_storage[0]))));
+ }
+ native_gc_map_builder.AddEntry(native_offset, &references_buffer[0]);
+ prev_mir = mir;
}
- native_gc_map_builder.AddEntry(native_offset, &references_buffer[0]);
-#else
- // For little-endian, the bytes comprising the bit vector's raw storage are what we need.
- native_gc_map_builder.AddEntry(native_offset,
- reinterpret_cast<const uint8_t*>(references->GetRawStorage()));
-#endif
- prev_mir = mir;
}
}
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 236bad7451..cca4e5a30a 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1465,9 +1465,6 @@ class Mir2Lir {
virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src);
virtual void GenMonitorExit(int opt_flags, RegLocation rl_src);
- // Temp workaround
- void Workaround7250540(RegLocation rl_dest, RegStorage zero_reg);
-
virtual LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) = 0;
// Queries for backend support for vectors
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d238b2cb05..c1555aa523 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -133,7 +133,7 @@ bool ImageWriter::Write(const std::string& image_filename,
return false;
}
std::string error_msg;
- oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg);
if (oat_file_ == nullptr) {
PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 46aed60479..c426625ae1 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -122,7 +122,7 @@ TEST_F(OatTest, WriteRead) {
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
- nullptr, false, &error_msg));
+ nullptr, false, nullptr, &error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index ecf9fa28a7..0ecc0d7433 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -18,15 +18,6 @@
namespace art {
-static bool EndsWithAnIf(HBasicBlock* block) {
- return block->GetLastInstruction()->IsIf();
-}
-
-static bool HasSinglePhi(HBasicBlock* block) {
- return !block->GetPhis().IsEmpty()
- && block->GetFirstPhi()->GetNext() == nullptr;
-}
-
// Returns true if 'block1' and 'block2' are empty, merge into the same single
// successor and the successor can only be reached from them.
static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
@@ -39,15 +30,15 @@ static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
// Returns true if the outcome of the branching matches the boolean value of
// the branching condition.
static bool PreservesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 1
- && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 0;
+ return input_true->IsIntConstant() && input_true->AsIntConstant()->IsOne()
+ && input_false->IsIntConstant() && input_false->AsIntConstant()->IsZero();
}
// Returns true if the outcome of the branching is exactly opposite of the
// boolean value of the branching condition.
static bool NegatesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 0
- && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 1;
+ return input_true->IsIntConstant() && input_true->AsIntConstant()->IsZero()
+ && input_false->IsIntConstant() && input_false->AsIntConstant()->IsOne();
}
// Returns an instruction with the opposite boolean value from 'cond'.
@@ -72,11 +63,11 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) {
return new (allocator) HLessThan(lhs, rhs);
}
} else if (cond->IsIntConstant()) {
- int32_t value = cond->AsIntConstant()->GetValue();
- if (value == 0) {
+ HIntConstant* int_const = cond->AsIntConstant();
+ if (int_const->IsZero()) {
return graph->GetIntConstant1();
} else {
- DCHECK_EQ(value, 1);
+ DCHECK(int_const->IsOne());
return graph->GetIntConstant0();
}
}
@@ -91,7 +82,7 @@ void HBooleanSimplifier::Run() {
// order does not matter.
for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
- if (!EndsWithAnIf(block)) continue;
+ if (!block->EndsWithIf()) continue;
// Find elements of the pattern.
HIf* if_instruction = block->GetLastInstruction()->AsIf();
@@ -101,7 +92,7 @@ void HBooleanSimplifier::Run() {
continue;
}
HBasicBlock* merge_block = true_block->GetSuccessors().Get(0);
- if (!HasSinglePhi(merge_block)) {
+ if (!merge_block->HasSinglePhi()) {
continue;
}
HPhi* phi = merge_block->GetFirstPhi()->AsPhi();
diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h
index 9fa9c5acdb..a88733e1af 100644
--- a/compiler/optimizing/boolean_simplifier.h
+++ b/compiler/optimizing/boolean_simplifier.h
@@ -15,7 +15,7 @@
*/
// This optimization recognizes a common pattern where a boolean value is
-// either casted to an integer or negated by selecting from zero/one integer
+// either cast to an integer or negated by selecting from zero/one integer
// constants with an If statement. Because boolean values are internally
// represented as zero/one, we can safely replace the pattern with a suitable
// condition instruction.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index d783903349..97c470b730 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1388,8 +1388,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
- // Java language does not allow treating boolean as an integral type but our
- // bit representation makes it safe.
+ // The Java language does not allow treating boolean as an integral type but
+ // our bit representation makes it safe.
switch (result_type) {
case Primitive::kPrimByte:
@@ -2326,10 +2326,8 @@ void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction)
void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
- LocationSummary::CallKind call_kind = op->GetResultType() == Primitive::kPrimLong
- ? LocationSummary::kCall
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(op, call_kind);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
switch (op->GetResultType()) {
case Primitive::kPrimInt: {
@@ -2339,12 +2337,10 @@ void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
break;
}
case Primitive::kPrimLong: {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::RegisterPairLocation(
- calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
- // The runtime helper puts the output in R0,R1.
- locations->SetOut(Location::RegisterPairLocation(R0, R1));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
break;
}
default:
@@ -2392,24 +2388,56 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
break;
}
case Primitive::kPrimLong: {
- // TODO: Inline the assembly instead of calling the runtime.
- InvokeRuntimeCallingConvention calling_convention;
- DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegister<Register>());
- DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
+ Register o_h = out.AsRegisterPairHigh<Register>();
+ Register o_l = out.AsRegisterPairLow<Register>();
+
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+
+ Register high = first.AsRegisterPairHigh<Register>();
+ Register low = first.AsRegisterPairLow<Register>();
+
+ Register second_reg = second.AsRegister<Register>();
- int32_t entry_point_offset;
if (op->IsShl()) {
- entry_point_offset = QUICK_ENTRY_POINT(pShlLong);
+ // Shift the high part
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsl(o_h, high, second_reg);
+ // Shift the low part and `or` what overflew on the high part
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsr(temp, low, temp);
+ __ orr(o_h, o_h, ShifterOperand(temp));
+ // If the shift is > 32 bits, override the high part
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Lsl(o_h, low, temp, false, PL);
+ // Shift the low part
+ __ Lsl(o_l, low, second_reg);
} else if (op->IsShr()) {
- entry_point_offset = QUICK_ENTRY_POINT(pShrLong);
+ // Shift the low part
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsr(o_l, low, second_reg);
+ // Shift the high part and `or` what underflew on the low part
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsl(temp, high, temp);
+ __ orr(o_l, o_l, ShifterOperand(temp));
+ // If the shift is > 32 bits, override the low part
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Asr(o_l, high, temp, false, PL);
+ // Shift the high part
+ __ Asr(o_h, high, second_reg);
} else {
- entry_point_offset = QUICK_ENTRY_POINT(pUshrLong);
+ // same as Shr except we use `Lsr`s and not `Asr`s
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsr(o_l, low, second_reg);
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsl(temp, high, temp);
+ __ orr(o_l, o_l, ShifterOperand(temp));
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Lsr(o_l, high, temp, false, PL);
+ __ Lsr(o_h, high, second_reg);
}
- __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
- __ blx(LR);
break;
}
default:
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0a7d3fece3..4414a65efa 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1370,8 +1370,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
- // Java language does not allow treating boolean as an integral type but our
- // bit representation makes it safe.
+ // The Java language does not allow treating boolean as an integral type but
+ // our bit representation makes it safe.
switch (result_type) {
case Primitive::kPrimByte:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index bff8fc99ac..c1f601e6d4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1410,8 +1410,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
- // Java language does not allow treating boolean as an integral type but our
- // bit representation makes it safe.
+ // The Java language does not allow treating boolean as an integral type but
+ // our bit representation makes it safe.
switch (result_type) {
case Primitive::kPrimByte:
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 6009cb50cd..4f6565d315 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -832,6 +832,14 @@ bool HBasicBlock::IsSingleGoto() const {
&& (loop_info == nullptr || !loop_info->IsBackEdge(*this));
}
+bool HBasicBlock::EndsWithIf() const {
+ return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf();
+}
+
+bool HBasicBlock::HasSinglePhi() const {
+ return !GetPhis().IsEmpty() && GetFirstPhi()->GetNext() == nullptr;
+}
+
void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
for (HInstruction* current = first_instruction_;
current != nullptr;
@@ -1086,13 +1094,15 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
}
void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block) {
- // Make sure this is a diamond control-flow path, find the two branches.
+ // Find the two branches of an If.
DCHECK_EQ(start_block->GetSuccessors().Size(), 2u);
- DCHECK_EQ(end_block->GetPredecessors().Size(), 2u);
HBasicBlock* left_branch = start_block->GetSuccessors().Get(0);
HBasicBlock* right_branch = start_block->GetSuccessors().Get(1);
+
+ // Make sure this is a diamond control-flow path.
DCHECK_EQ(left_branch->GetSuccessors().Get(0), end_block);
DCHECK_EQ(right_branch->GetSuccessors().Get(0), end_block);
+ DCHECK_EQ(end_block->GetPredecessors().Size(), 2u);
DCHECK_EQ(start_block, end_block->GetDominator());
// Disconnect the branches and merge the two blocks. This will move
@@ -1114,16 +1124,12 @@ void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block
reverse_post_order_.Delete(right_branch);
reverse_post_order_.Delete(end_block);
- // Update loop information.
- HLoopInformation* loop_info = start_block->GetLoopInformation();
- if (kIsDebugBuild) {
- if (loop_info != nullptr) {
- DCHECK_EQ(loop_info, left_branch->GetLoopInformation());
- DCHECK_EQ(loop_info, right_branch->GetLoopInformation());
- DCHECK_EQ(loop_info, end_block->GetLoopInformation());
- }
- }
- while (loop_info != nullptr) {
+ // Update loops which contain the code.
+ for (HLoopInformationOutwardIterator it(*start_block); !it.Done(); it.Advance()) {
+ HLoopInformation* loop_info = it.Current();
+ DCHECK(loop_info->Contains(*left_branch));
+ DCHECK(loop_info->Contains(*right_branch));
+ DCHECK(loop_info->Contains(*end_block));
loop_info->Remove(left_branch);
loop_info->Remove(right_branch);
loop_info->Remove(end_block);
@@ -1131,8 +1137,6 @@ void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block
loop_info->RemoveBackEdge(end_block);
loop_info->AddBackEdge(start_block);
}
- // Move to parent loop if nested.
- loop_info = loop_info->GetHeader()->GetDominator()->GetLoopInformation();
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a38ee45eec..08b16d99b6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -602,6 +602,9 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
bool IsCatchBlock() const { return is_catch_block_; }
void SetIsCatchBlock() { is_catch_block_ = true; }
+ bool EndsWithIf() const;
+ bool HasSinglePhi() const;
+
private:
HGraph* graph_;
GrowableArray<HBasicBlock*> predecessors_;
@@ -624,6 +627,31 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
};
+// Iterates over the LoopInformation of all loops which contain 'block'
+// from the innermost to the outermost.
+class HLoopInformationOutwardIterator : public ValueObject {
+ public:
+ explicit HLoopInformationOutwardIterator(const HBasicBlock& block)
+ : current_(block.GetLoopInformation()) {}
+
+ bool Done() const { return current_ == nullptr; }
+
+ void Advance() {
+ DCHECK(!Done());
+ current_ = current_->GetHeader()->GetDominator()->GetLoopInformation();
+ }
+
+ HLoopInformation* Current() const {
+ DCHECK(!Done());
+ return current_;
+ }
+
+ private:
+ HLoopInformation* current_;
+
+ DISALLOW_COPY_AND_ASSIGN(HLoopInformationOutwardIterator);
+};
+
#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
M(Add, BinaryOperation) \
M(And, BinaryOperation) \
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 9914ef49c3..5818a37a46 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
#define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
-#include "base/bit_vector.h"
+#include "base/arena_containers.h"
+#include "base/bit_vector-inl.h"
#include "base/value_object.h"
#include "memory_region.h"
#include "nodes.h"
@@ -40,7 +41,8 @@ class StackMapStream : public ValueObject {
stack_mask_max_(-1),
dex_pc_max_(0),
native_pc_offset_max_(0),
- number_of_stack_maps_with_inline_info_(0) {}
+ number_of_stack_maps_with_inline_info_(0),
+ dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {}
// Compute bytes needed to encode a mask with the given maximum element.
static uint32_t StackMaskEncodingSize(int max_element) {
@@ -59,6 +61,7 @@ class StackMapStream : public ValueObject {
size_t dex_register_locations_start_index;
size_t inline_infos_start_index;
BitVector* live_dex_registers_mask;
+ uint32_t dex_register_map_hash;
};
struct InlineInfoEntry {
@@ -80,6 +83,7 @@ class StackMapStream : public ValueObject {
entry.inlining_depth = inlining_depth;
entry.dex_register_locations_start_index = dex_register_locations_.Size();
entry.inline_infos_start_index = inline_infos_.Size();
+ entry.dex_register_map_hash = 0;
if (num_dex_registers != 0) {
entry.live_dex_registers_mask =
new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true);
@@ -105,7 +109,7 @@ class StackMapStream : public ValueObject {
inline_infos_.Add(entry);
}
- size_t ComputeNeededSize() const {
+ size_t ComputeNeededSize() {
size_t size = CodeInfo::kFixedSize
+ ComputeStackMapsSize()
+ ComputeDexRegisterMapsSize()
@@ -118,7 +122,7 @@ class StackMapStream : public ValueObject {
return StackMaskEncodingSize(stack_mask_max_);
}
- size_t ComputeStackMapsSize() const {
+ size_t ComputeStackMapsSize() {
return stack_maps_.Size() * StackMap::ComputeStackMapSize(
ComputeStackMaskSize(),
ComputeInlineInfoSize(),
@@ -146,10 +150,13 @@ class StackMapStream : public ValueObject {
}
// Compute the size of all the Dex register maps.
- size_t ComputeDexRegisterMapsSize() const {
+ size_t ComputeDexRegisterMapsSize() {
size_t size = 0;
for (size_t i = 0; i < stack_maps_.Size(); ++i) {
- size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+ if (FindEntryWithTheSameDexMap(i) == kNoSameDexMapFound) {
+ // Entries with the same dex map will have the same offset.
+ size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+ }
}
return size;
}
@@ -161,11 +168,11 @@ class StackMapStream : public ValueObject {
+ (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
}
- size_t ComputeDexRegisterMapsStart() const {
+ size_t ComputeDexRegisterMapsStart() {
return CodeInfo::kFixedSize + ComputeStackMapsSize();
}
- size_t ComputeInlineInfoStart() const {
+ size_t ComputeInlineInfoStart() {
return ComputeDexRegisterMapsStart() + ComputeDexRegisterMapsSize();
}
@@ -206,38 +213,47 @@ class StackMapStream : public ValueObject {
stack_map.SetStackMask(code_info, *entry.sp_mask);
}
- if (entry.num_dex_registers != 0) {
- // Set the Dex register map.
- MemoryRegion register_region =
- dex_register_locations_region.Subregion(
- next_dex_register_map_offset,
- ComputeDexRegisterMapSize(entry));
- next_dex_register_map_offset += register_region.size();
- DexRegisterMap dex_register_map(register_region);
- stack_map.SetDexRegisterMapOffset(
+ if (entry.num_dex_registers == 0) {
+ // No dex map available.
+ stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
+ } else {
+ // Search for an entry with the same dex map.
+ size_t entry_with_same_map = FindEntryWithTheSameDexMap(i);
+ if (entry_with_same_map != kNoSameDexMapFound) {
+ // If we have a hit reuse the offset.
+ stack_map.SetDexRegisterMapOffset(code_info,
+ code_info.GetStackMapAt(entry_with_same_map).GetDexRegisterMapOffset(code_info));
+ } else {
+ // New dex registers maps should be added to the stack map.
+ MemoryRegion register_region =
+ dex_register_locations_region.Subregion(
+ next_dex_register_map_offset,
+ ComputeDexRegisterMapSize(entry));
+ next_dex_register_map_offset += register_region.size();
+ DexRegisterMap dex_register_map(register_region);
+ stack_map.SetDexRegisterMapOffset(
code_info, register_region.start() - dex_register_locations_region.start());
- // Offset in `dex_register_map` where to store the next register entry.
- size_t offset = DexRegisterMap::kFixedSize;
- dex_register_map.SetLiveBitMask(offset,
- entry.num_dex_registers,
- *entry.live_dex_registers_mask);
- offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
- for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
- dex_register_number < entry.num_dex_registers;
- ++dex_register_number) {
- if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
- DexRegisterLocation dex_register_location = dex_register_locations_.Get(
- entry.dex_register_locations_start_index + index_in_dex_register_locations);
- dex_register_map.SetRegisterInfo(offset, dex_register_location);
- offset += DexRegisterMap::EntrySize(dex_register_location);
- ++index_in_dex_register_locations;
+ // Offset in `dex_register_map` where to store the next register entry.
+ size_t offset = DexRegisterMap::kFixedSize;
+ dex_register_map.SetLiveBitMask(offset,
+ entry.num_dex_registers,
+ *entry.live_dex_registers_mask);
+ offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
+ for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
+ dex_register_number < entry.num_dex_registers;
+ ++dex_register_number) {
+ if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
+ DexRegisterLocation dex_register_location = dex_register_locations_.Get(
+ entry.dex_register_locations_start_index + index_in_dex_register_locations);
+ dex_register_map.SetRegisterInfo(offset, dex_register_location);
+ offset += DexRegisterMap::EntrySize(dex_register_location);
+ ++index_in_dex_register_locations;
+ }
}
+ // Ensure we reached the end of the Dex registers region.
+ DCHECK_EQ(offset, register_region.size());
}
- // Ensure we reached the end of the Dex registers region.
- DCHECK_EQ(offset, register_region.size());
- } else {
- stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
}
// Set the inlining info.
@@ -271,11 +287,86 @@ class StackMapStream : public ValueObject {
DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
<< DexRegisterLocation::PrettyDescriptor(kind);
dex_register_locations_.Add(DexRegisterLocation(kind, value));
- stack_maps_.Get(stack_maps_.Size() - 1).live_dex_registers_mask->SetBit(dex_register);
+ StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
+ entry.live_dex_registers_mask->SetBit(dex_register);
+ entry.dex_register_map_hash += (1 << dex_register);
+ entry.dex_register_map_hash += static_cast<uint32_t>(value);
+ entry.dex_register_map_hash += static_cast<uint32_t>(kind);
+ stack_maps_.Put(stack_maps_.Size() - 1, entry);
}
}
private:
+ // Returns the index of an entry with the same dex register map
+ // or kNoSameDexMapFound if no such entry exists.
+ size_t FindEntryWithTheSameDexMap(size_t entry_index) {
+ StackMapEntry entry = stack_maps_.Get(entry_index);
+ auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.dex_register_map_hash);
+ if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
+ // We don't have a perfect hash functions so we need a list to collect all stack maps
+ // which might have the same dex register map.
+ GrowableArray<uint32_t> stack_map_indices(allocator_, 1);
+ stack_map_indices.Add(entry_index);
+ dex_map_hash_to_stack_map_indices_.Put(entry.dex_register_map_hash, stack_map_indices);
+ return kNoSameDexMapFound;
+ }
+
+ // TODO: We don't need to add ourselves to the map if we can guarantee that
+ // FindEntryWithTheSameDexMap is called just once per stack map entry.
+ // A good way to do this is to cache the offset in the stack map entry. This
+ // is easier to do if we add markers when the stack map constructions begins
+ // and when it ends.
+
+ // We might have collisions, so we need to check whether or not we should
+ // add the entry to the map. `needs_to_be_added` keeps track of this.
+ bool needs_to_be_added = true;
+ size_t result = kNoSameDexMapFound;
+ for (size_t i = 0; i < entries_it->second.Size(); i++) {
+ size_t test_entry_index = entries_it->second.Get(i);
+ if (test_entry_index == entry_index) {
+ needs_to_be_added = false;
+ } else if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), entry)) {
+ result = test_entry_index;
+ needs_to_be_added = false;
+ break;
+ }
+ }
+ if (needs_to_be_added) {
+ entries_it->second.Add(entry_index);
+ }
+ return result;
+ }
+
+ bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
+ if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
+ return true;
+ }
+ if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
+ return false;
+ }
+ if (a.num_dex_registers != b.num_dex_registers) {
+ return false;
+ }
+
+ int index_in_dex_register_locations = 0;
+ for (uint32_t i = 0; i < a.num_dex_registers; i++) {
+ if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) {
+ return false;
+ }
+ if (a.live_dex_registers_mask->IsBitSet(i)) {
+ DexRegisterLocation a_loc = dex_register_locations_.Get(
+ a.dex_register_locations_start_index + index_in_dex_register_locations);
+ DexRegisterLocation b_loc = dex_register_locations_.Get(
+ b.dex_register_locations_start_index + index_in_dex_register_locations);
+ if (a_loc != b_loc) {
+ return false;
+ }
+ ++index_in_dex_register_locations;
+ }
+ }
+ return true;
+ }
+
ArenaAllocator* allocator_;
GrowableArray<StackMapEntry> stack_maps_;
GrowableArray<DexRegisterLocation> dex_register_locations_;
@@ -285,6 +376,10 @@ class StackMapStream : public ValueObject {
uint32_t native_pc_offset_max_;
size_t number_of_stack_maps_with_inline_info_;
+ ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
+
+ static constexpr uint32_t kNoSameDexMapFound = -1;
+
ART_FRIEND_TEST(StackMapTest, Test1);
ART_FRIEND_TEST(StackMapTest, Test2);
ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters);
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e7075c0aef..e5a9790254 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -231,4 +231,54 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) {
ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding);
}
+TEST(StackMapTest, TestShareDexRegisterMap) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ StackMapStream stream(&arena);
+
+ ArenaBitVector sp_mask(&arena, 0, false);
+ uint32_t number_of_dex_registers = 2;
+ // First stack map.
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+ // Second stack map, which should share the same dex register map.
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+ // Third stack map (doesn't share the dex register map).
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+
+ size_t size = stream.ComputeNeededSize();
+ void* memory = arena.Alloc(size, kArenaAllocMisc);
+ MemoryRegion region(memory, size);
+ stream.FillIn(region);
+
+ CodeInfo ci(region);
+ // Verify first stack map.
+ StackMap sm0 = ci.GetStackMapAt(0);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers));
+
+ // Verify second stack map.
+ StackMap sm1 = ci.GetStackMapAt(1);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers));
+
+ // Verify third stack map.
+ StackMap sm2 = ci.GetStackMapAt(2);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
+ ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers));
+
+ // Verify dex register map offsets.
+ ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci));
+ ASSERT_NE(sm0.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+ ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 8730f52eca..dd0dba2df4 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -100,6 +100,10 @@ class ShifterOperand {
return rm_;
}
+ Register GetSecondRegister() const {
+ return rs_;
+ }
+
enum Type {
kUnknown = -1,
kRegister,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 6d0571e263..a894319c99 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -825,10 +825,12 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
if (so.IsImmediate()) {
// Check special cases.
if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
- if (opcode == SUB) {
- thumb_opcode = 5U /* 0b0101 */;
- } else {
- thumb_opcode = 0;
+ if (!set_cc) {
+ if (opcode == SUB) {
+ thumb_opcode = 5U;
+ } else if (opcode == ADD) {
+ thumb_opcode = 0U;
+ }
}
uint32_t imm = so.GetImmediate();
@@ -836,13 +838,14 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
uint32_t imm8 = imm & 0xff;
- encoding = B31 | B30 | B29 | B28 | B25 |
- thumb_opcode << 21 |
- rn << 16 |
- rd << 8 |
- i << 26 |
- imm3 << 12 |
- imm8;
+ encoding = B31 | B30 | B29 | B28 |
+ (set_cc ? B20 : B25) |
+ thumb_opcode << 21 |
+ rn << 16 |
+ rd << 8 |
+ i << 26 |
+ imm3 << 12 |
+ imm8;
} else {
// Modified immediate.
uint32_t imm = ModifiedImmediate(so.encodingThumb());
@@ -852,19 +855,19 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
}
encoding = B31 | B30 | B29 | B28 |
thumb_opcode << 21 |
- (set_cc ? 1 : 0) << 20 |
+ (set_cc ? B20 : 0) |
rn << 16 |
rd << 8 |
imm;
}
} else if (so.IsRegister()) {
- // Register (possibly shifted)
- encoding = B31 | B30 | B29 | B27 | B25 |
- thumb_opcode << 21 |
- (set_cc ? 1 : 0) << 20 |
- rn << 16 |
- rd << 8 |
- so.encodingThumb();
+ // Register (possibly shifted)
+ encoding = B31 | B30 | B29 | B27 | B25 |
+ thumb_opcode << 21 |
+ (set_cc ? B20 : 0) |
+ rn << 16 |
+ rd << 8 |
+ so.encodingThumb();
}
Emit32(encoding);
}
@@ -921,6 +924,8 @@ void Thumb2Assembler::Emit16BitDataProcessing(Condition cond,
use_immediate = true;
immediate = so.GetImmediate();
} else {
+ CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister))
+ << "No register-shifted register instruction available in thumb";
// Adjust rn and rd: only two registers will be emitted.
switch (opcode) {
case AND:
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index ebea9d4262..813996b0db 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -227,4 +227,24 @@ TEST_F(AssemblerThumb2Test, eor) {
DriverStr(expected, "abs");
}
+TEST_F(AssemblerThumb2Test, sub) {
+ __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+ const char* expected =
+ "subs r1, r0, #42\n"
+ "subw r1, r0, #42\n";
+ DriverStr(expected, "sub");
+}
+
+TEST_F(AssemblerThumb2Test, add) {
+ __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+ const char* expected =
+ "adds r1, r0, #42\n"
+ "addw r1, r0, #42\n";
+ DriverStr(expected, "add");
+}
+
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 14bcd4b799..daca971de0 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1453,7 +1453,9 @@ class ImageDumper {
std::string error_msg;
const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location,
+ nullptr, nullptr, false, nullptr,
+ &error_msg);
if (oat_file == nullptr) {
os << "NOT FOUND: " << error_msg << "\n";
return false;
@@ -2193,7 +2195,7 @@ static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions*
std::ostream* os) {
std::string error_msg;
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- &error_msg);
+ nullptr, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2209,7 +2211,7 @@ static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions*
static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
std::string error_msg;
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- &error_msg);
+ nullptr, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index c8ede48b7d..da395734d3 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -421,15 +421,26 @@ class DexFile {
}
}
- std::string GetBaseLocation() const {
- size_t pos = location_.rfind(kMultiDexSeparator);
+ static std::string GetBaseLocation(const std::string& location) {
+ return GetBaseLocation(location.c_str());
+ }
+
+ // Returns the ':classes*.dex' part of the dex location. Returns an empty
+ // string if there is no multidex suffix for the given location.
+ // The kMultiDexSeparator is included in the returned suffix.
+ static std::string GetMultiDexSuffix(const std::string& location) {
+ size_t pos = location.rfind(kMultiDexSeparator);
if (pos == std::string::npos) {
- return location_;
+ return "";
} else {
- return location_.substr(0, pos);
+ return location.substr(pos);
}
}
+ std::string GetBaseLocation() const {
+ return GetBaseLocation(location_);
+ }
+
// For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
// For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
uint32_t GetLocationChecksum() const {
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 7f5a181d40..09ef3eef77 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -377,4 +377,13 @@ TEST_F(DexFileTest, GetDexCanonicalLocation) {
ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
}
+TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes2.dex"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes8.dex"));
+ EXPECT_EQ("", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar"));
+ EXPECT_EQ(":classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes2.dex"));
+ EXPECT_EQ(":classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes8.dex"));
+}
+
} // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 14f770da63..1fb3252ea9 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -788,7 +788,8 @@ OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg)
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
image_header.GetOatFileBegin(),
- !Runtime::Current()->IsAotCompiler(), error_msg);
+ !Runtime::Current()->IsAotCompiler(),
+ nullptr, error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
oat_filename.c_str(), GetName(), error_msg->c_str());
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 356e3d2509..69cb22d07a 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -17,10 +17,11 @@
#include "oat_file.h"
#include <dlfcn.h>
-#include <sstream>
#include <string.h>
#include <unistd.h>
+#include <sstream>
+
#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -38,12 +39,33 @@
namespace art {
+std::string OatFile::ResolveRelativeEncodedDexLocation(
+ const char* abs_dex_location, const std::string& rel_dex_location) {
+ if (abs_dex_location != nullptr && rel_dex_location[0] != '/') {
+ // Strip :classes<N>.dex used for secondary multidex files.
+ std::string base = DexFile::GetBaseLocation(rel_dex_location);
+ std::string multidex_suffix = DexFile::GetMultiDexSuffix(rel_dex_location);
+
+ // Check if the base is a suffix of the provided abs_dex_location.
+ std::string target_suffix = "/" + base;
+ std::string abs_location(abs_dex_location);
+ if (abs_location.size() > target_suffix.size()) {
+ size_t pos = abs_location.size() - target_suffix.size();
+ if (abs_location.compare(pos, std::string::npos, target_suffix) == 0) {
+ return abs_location + multidex_suffix;
+ }
+ }
+ }
+ return rel_dex_location;
+}
+
void OatFile::CheckLocation(const std::string& location) {
CHECK(!location.empty());
}
OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
const std::string& location,
+ const char* abs_dex_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
oat_file->elf_file_.reset(elf_file);
@@ -53,7 +75,7 @@ OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
oat_file->begin_ = elf_file->Begin() + offset;
oat_file->end_ = elf_file->Begin() + size + offset;
// Ignore the optional .bss section when opening non-executable.
- return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
+ return oat_file->Setup(abs_dex_location, error_msg) ? oat_file.release() : nullptr;
}
OatFile* OatFile::Open(const std::string& filename,
@@ -61,6 +83,7 @@ OatFile* OatFile::Open(const std::string& filename,
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
CHECK(!filename.empty()) << location;
CheckLocation(location);
@@ -80,7 +103,7 @@ OatFile* OatFile::Open(const std::string& filename,
return nullptr;
}
ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
- error_msg));
+ abs_dex_location, error_msg));
// It would be nice to unlink here. But we might have opened the file created by the
// ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -88,14 +111,18 @@ OatFile* OatFile::Open(const std::string& filename,
return ret.release();
}
-OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenWritable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, true, false, abs_dex_location, error_msg);
}
-OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenReadable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, false, false, abs_dex_location, error_msg);
}
OatFile* OatFile::OpenElfFile(File* file,
@@ -104,10 +131,11 @@ OatFile* OatFile::OpenElfFile(File* file,
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
- error_msg);
+ abs_dex_location, error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
@@ -131,6 +159,7 @@ OatFile::~OatFile() {
bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
bool writable, bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
// TODO: rename requested_base to oat_data_begin
elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
@@ -180,10 +209,10 @@ bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file
bss_end_ += sizeof(uint32_t);
}
- return Setup(error_msg);
+ return Setup(abs_dex_location, error_msg);
}
-bool OatFile::Setup(std::string* error_msg) {
+bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
*error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
@@ -230,7 +259,9 @@ bool OatFile::Setup(std::string* error_msg) {
return false;
}
- std::string dex_file_location(dex_file_location_data, dex_file_location_size);
+ std::string dex_file_location = ResolveRelativeEncodedDexLocation(
+ abs_dex_location,
+ std::string(dex_file_location_data, dex_file_location_size));
uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
oat += sizeof(dex_file_checksum);
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 564185cc75..51952f318c 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -43,14 +43,18 @@ class OatFile {
// Opens an oat file contained within the given elf file. This is always opened as
// non-executable at the moment.
static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
+ const char* abs_dex_location,
std::string* error_msg);
// Open an oat file. Returns NULL on failure. Requested base can
// optionally be used to request where the file should be loaded.
+ // See the ResolveRelativeEncodedDexLocation for a description of how the
+ // abs_dex_location argument is used.
static OatFile* Open(const std::string& filename,
const std::string& location,
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
// Open an oat file from an already opened File.
@@ -58,9 +62,13 @@ class OatFile {
// where relocations may be required. Currently used from
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
- static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+ static OatFile* OpenWritable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg);
// Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
- static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
+ static OatFile* OpenReadable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg);
~OatFile();
@@ -279,6 +287,18 @@ class OatFile {
const uint8_t* BssBegin() const;
const uint8_t* BssEnd() const;
+ // Returns the absolute dex location for the encoded relative dex location.
+ //
+ // If not nullptr, abs_dex_location is used to resolve the absolute dex
+ // location of relative dex locations encoded in the oat file.
+ // For example, given absolute location "/data/app/foo/base.apk", encoded
+ // dex locations "base.apk", "base.apk:classes2.dex", etc. would be resolved
+ // to "/data/app/foo/base.apk", "/data/app/foo/base.apk:classes2.dex", etc.
+ // Relative encoded dex locations that don't match the given abs_dex_location
+ // are left unchanged.
+ static std::string ResolveRelativeEncodedDexLocation(
+ const char* abs_dex_location, const std::string& rel_dex_location);
+
private:
static void CheckLocation(const std::string& location);
@@ -288,14 +308,17 @@ class OatFile {
uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
explicit OatFile(const std::string& filename, bool executable);
bool ElfFileOpen(File* file, uint8_t* requested_base,
uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable, bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
- bool Setup(std::string* error_msg);
+
+ bool Setup(const char* abs_dex_location, std::string* error_msg);
// The oat file name.
//
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f87fa4f8f4..9a17b01c02 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -850,7 +850,7 @@ const OatFile* OatFileAssistant::GetOdexFile() {
std::string error_msg;
cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
odex_file_name.c_str(), nullptr, nullptr, load_executable_,
- &error_msg));
+ dex_location_, &error_msg));
if (cached_odex_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
<< odex_file_name << ": " << error_msg;
@@ -875,7 +875,8 @@ const OatFile* OatFileAssistant::GetOatFile() {
const std::string& oat_file_name = *OatFileName();
std::string error_msg;
cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
- oat_file_name.c_str(), nullptr, nullptr, load_executable_, &error_msg));
+ oat_file_name.c_str(), nullptr, nullptr, load_executable_,
+ dex_location_, &error_msg));
if (cached_oat_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing oat file "
<< oat_file_name << ": " << error_msg;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index be8652cec5..41dc2d7206 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -326,12 +326,43 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
GenerateOatForTest(dex_location.c_str());
// Verify we can load both dex files.
- OatFileAssistant executable_oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- std::unique_ptr<OatFile> oat_file = executable_oat_file_assistant.GetBestOatFile();
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
+// encoded dex locations.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+ std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
+ std::string oat_location = GetISADir() + "/RelativeEncodedDexLocation.oat";
+
+ // Create the dex file
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Create the oat file with relative encoded dex location.
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
+ args.push_back("--oat-file=" + oat_location);
+
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify we can load both dex files.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ oat_location.c_str(),
+ kRuntimeISA, true);
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
EXPECT_TRUE(oat_file->IsExecutable());
std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = executable_oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
}
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
new file mode 100644
index 0000000000..f2213e9879
--- /dev/null
+++ b/runtime/oat_file_test.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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 "oat_file.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ nullptr, "/data/app/foo/base.apk"));
+
+ EXPECT_EQ(std::string("/system/framework/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "/system/framework/base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "foo/base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk:classes2.dex"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk:classes2.dex"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk:classes11.dex"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk:classes11.dex"));
+
+ EXPECT_EQ(std::string("base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/sludge.apk", "base.apk"));
+
+ EXPECT_EQ(std::string("o/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "o/base.apk"));
+}
+
+} // namespace art
diff --git a/runtime/primitive.h b/runtime/primitive.h
index d11f1e955e..32bfdaf089 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -153,8 +153,8 @@ class Primitive {
}
static bool IsIntegralType(Type type) {
- // Java language does not allow treating boolean as an integral type but our
- // bit representation makes it safe.
+ // The Java language does not allow treating boolean as an integral type but
+ // our bit representation makes it safe.
switch (type) {
case kPrimBoolean:
case kPrimByte:
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2dacfe2bdf..9ca00b1195 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -688,7 +688,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location,
return false;
}
std::unique_ptr<OatFile> oat_file(OatFile::OpenWithElfFile(elf_file.release(), oat_location,
- &error_msg));
+ nullptr, &error_msg));
if (oat_file.get() == nullptr) {
LOG(INFO) << "Unable to use '" << oat_filename << "' because " << error_msg;
return false;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 629fc9a347..6ec7cc8525 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -212,6 +212,14 @@ class DexRegisterLocation {
// Get the actual kind of the location.
Kind GetInternalKind() const { return kind_; }
+ bool operator==(DexRegisterLocation other) const {
+ return kind_ == other.kind_ && value_ == other.value_;
+ }
+
+ bool operator!=(DexRegisterLocation other) const {
+ return !(*this == other);
+ }
+
private:
Kind kind_;
int32_t value_;
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 25f58b4b43..efe0d3f729 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -116,7 +116,7 @@ public class Main {
// CHECK-DAG: Return [ [[Cond]] ]
public static boolean LessThan(int x, int y) {
- return x < y;
+ return (x < y) ? true : false;
}
/*