diff options
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; } /* |