diff options
75 files changed, 1781 insertions, 581 deletions
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 26ab281741..7f2e1931d0 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -780,9 +780,9 @@ class ElfBuilder FINAL { EF_MIPS_PIC | EF_MIPS_CPIC | EF_MIPS_ABI_O32 | - features->AsMipsInstructionSetFeatures()->IsR6() - ? EF_MIPS_ARCH_32R6 - : EF_MIPS_ARCH_32R2); + (features->AsMipsInstructionSetFeatures()->IsR6() + ? EF_MIPS_ARCH_32R6 + : EF_MIPS_ARCH_32R2)); break; } case kMips64: { diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 6d1f94491e..672018b355 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -444,7 +444,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(132 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(133 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c179342d6e..cdc7df11b6 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -2195,6 +2195,7 @@ bool OatWriter::OpenDexFiles( oat_dex_file.dex_file_location_checksum_, /* oat_dex_file */ nullptr, verify, + verify, &error_msg)); if (dex_files.back() == nullptr) { LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 6e851bf1ba..12aa15207c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -146,6 +146,13 @@ uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { : mirror::Array::LengthOffset().Uint32Value(); } +uint32_t CodeGenerator::GetArrayDataOffset(HArrayGet* array_get) { + DCHECK(array_get->GetType() == Primitive::kPrimChar || !array_get->IsStringCharAt()); + return array_get->IsStringCharAt() + ? mirror::String::ValueOffset().Uint32Value() + : mirror::Array::DataOffset(Primitive::ComponentSize(array_get->GetType())).Uint32Value(); +} + bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const { DCHECK_EQ((*block_order_)[current_block_index_], current); return GetNextBlockToEmit() == FirstNonEmptyBlock(next); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 81c1a7fe16..9364be35ff 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -345,6 +345,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // accessing the String's `count` field in String intrinsics. static uint32_t GetArrayLengthOffset(HArrayLength* array_length); + // Helper that returns the offset of the array's data. + // Note: Besides the normal arrays, we also use the HArrayGet for + // accessing the String's `value` field in String intrinsics. + static uint32_t GetArrayDataOffset(HArrayGet* array_get); + void EmitParallelMoves(Location from1, Location to1, Primitive::Type type1, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index f5befa65c1..b9466ba212 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -176,8 +176,11 @@ class BoundsCheckSlowPathARM : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - arm_codegen->InvokeRuntime( - QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + arm_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -4289,11 +4292,11 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { Register obj = obj_loc.AsRegister<Register>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4307,7 +4310,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4321,7 +4323,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4335,7 +4336,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4349,7 +4349,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4366,7 +4365,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -4401,7 +4399,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; @@ -4414,7 +4411,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); SRegister out = out_loc.AsFpuRegister<SRegister>(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; @@ -4427,7 +4423,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index f748898c13..4692a4a876 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -237,8 +237,11 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { codegen->EmitParallelMoves( locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt, locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - arm64_codegen->InvokeRuntime( - QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + arm64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -2054,8 +2057,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { Register obj = InputRegisterAt(instruction, 0); LocationSummary* locations = instruction->GetLocations(); Location index = locations->InAt(1); - uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(); Location out = locations->Out(); + uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index b2cebc0dbd..738180670f 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -166,11 +166,15 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + mips_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this, IsDirectEntrypoint(kQuickThrowArrayBounds)); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -1635,11 +1639,11 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).AsRegister<Register>(); Location index = locations->InAt(1); - Primitive::Type type = instruction->GetType(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1653,7 +1657,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1667,7 +1670,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1682,7 +1684,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1699,7 +1700,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimInt: case Primitive::kPrimNot: { DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1714,7 +1714,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); Register out = locations->Out().AsRegisterPairLow<Register>(); if (index.IsConstant()) { size_t offset = @@ -1729,7 +1728,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); FRegister out = locations->Out().AsFpuRegister<FRegister>(); if (index.IsConstant()) { size_t offset = @@ -1744,7 +1742,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); FRegister out = locations->Out().AsFpuRegister<FRegister>(); if (index.IsConstant()) { size_t offset = diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1a20f5c0fe..558735110a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -127,10 +127,14 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + mips64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -918,13 +922,13 @@ void CodeGeneratorMIPS64::SetupBlockedRegisters() const { // TODO: review; anything else? - // TODO: remove once all the issues with register saving/restoring are sorted out. - for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { - blocked_core_registers_[kCoreCalleeSaves[i]] = true; - } - - for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { - blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; + if (GetGraph()->IsDebuggable()) { + // Stubs do not save callee-save floating point registers. If the graph + // is debuggable, we need to deal with these registers differently. For + // now, just block them. + for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { + blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; + } } } @@ -1289,11 +1293,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); Location index = locations->InAt(1); - Primitive::Type type = instruction->GetType(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1307,7 +1311,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1321,7 +1324,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1336,7 +1338,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1353,7 +1354,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimInt: case Primitive::kPrimNot: { DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); LoadOperandType load_type = (type == Primitive::kPrimNot) ? kLoadUnsignedWord : kLoadWord; if (index.IsConstant()) { @@ -1369,7 +1369,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1384,7 +1383,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1399,7 +1397,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); if (index.IsConstant()) { size_t offset = diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a816d5f0e8..52868f4b2e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -148,10 +148,14 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + x86_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -5060,11 +5064,11 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { Register obj = obj_loc.AsRegister<Register>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movzxb(out, Address(obj, @@ -5076,7 +5080,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movsxb(out, Address(obj, @@ -5088,7 +5091,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movsxw(out, Address(obj, @@ -5100,7 +5102,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movzxw(out, Address(obj, @@ -5112,7 +5113,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movl(out, Address(obj, @@ -5127,7 +5127,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -5161,7 +5160,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); DCHECK_NE(obj, out_loc.AsRegisterPairLow<Register>()); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; @@ -5179,7 +5177,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movss(out, Address(obj, @@ -5191,7 +5188,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movsd(out, Address(obj, diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index ccfb181e14..9a3e8d266b 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -204,10 +204,14 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + x86_64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -4555,11 +4559,11 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { CpuRegister obj = obj_loc.AsRegister<CpuRegister>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movzxb(out, Address(obj, @@ -4571,7 +4575,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movsxb(out, Address(obj, @@ -4583,7 +4586,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movsxw(out, Address(obj, @@ -4595,7 +4597,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movzxw(out, Address(obj, @@ -4607,7 +4608,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movl(out, Address(obj, @@ -4622,7 +4622,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -4656,7 +4655,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movq(out, Address(obj, @@ -4668,7 +4666,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movss(out, Address(obj, @@ -4680,7 +4677,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movsd(out, Address(obj, diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 7d11948f4e..4af8d1985b 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -403,6 +403,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << array_length->IsStringLength() << std::noboolalpha; } + void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE { + StartAttributeStream("is_string_char_at") << std::boolalpha + << bounds_check->IsStringCharAt() << std::noboolalpha; + } + + void VisitArrayGet(HArrayGet* array_get) OVERRIDE { + StartAttributeStream("is_string_char_at") << std::boolalpha + << array_get->IsStringCharAt() << std::noboolalpha; + } + void VisitArraySet(HArraySet* array_set) OVERRIDE { StartAttributeStream("value_can_be_null") << std::boolalpha << array_set->GetValueCanBeNull() << std::noboolalpha; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 0a5cf80e9d..52426d73c6 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -670,7 +670,7 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, // an unsigned entity, for example, as in the following loop that uses the full range: // for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX // (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in: - // for (int i = 12; i < U; i++) // TC = 0 when U < 12 + // for (int i = 12; i < U; i++) // TC = 0 when U <= 12 // If this cannot be determined at compile-time, the TC is only valid within the // loop-body proper, not the loop-header unless enforced with an explicit taken-test. // (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in: diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index d5e80b4759..be4ea200a9 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -756,7 +756,15 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* metho invoke_instruction->ReplaceWith(return_replacement); } invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); - FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp); + FixUpReturnReferenceType(method, return_replacement); + if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { + // Actual return value has a more specific type than the method's declared + // return type. Run RTP again on the outer graph to propagate it. + ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } return true; } @@ -1159,6 +1167,15 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } + // We have replaced formal arguments with actual arguments. If actual types + // are more specific than the declared ones, run RTP again on the inner graph. + if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { + ReferenceTypePropagation(callee_graph, + dex_compilation_unit.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } + size_t number_of_instructions_budget = kMaximumNumberOfHInstructions; size_t number_of_inlined_instructions = RunOptimizations(callee_graph, code_item, dex_compilation_unit); @@ -1332,13 +1349,87 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, return number_of_inlined_instructions; } -void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) { +static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti, + bool declared_can_be_null, + HInstruction* actual_obj) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (declared_can_be_null && !actual_obj->CanBeNull()) { + return true; + } + + ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo(); + return (actual_rti.IsExact() && !declared_rti.IsExact()) || + declared_rti.IsStrictSupertypeOf(actual_rti); +} + +ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) { + return ReferenceTypePropagation::IsAdmissible(klass) + ? ReferenceTypeInfo::Create(handles_->NewHandle(klass)) + : graph_->GetInexactObjectRti(); +} + +bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) { + // If this is an instance call, test whether the type of the `this` argument + // is more specific than the class which declares the method. + if (!resolved_method->IsStatic()) { + if (IsReferenceTypeRefinement(GetClassRTI(resolved_method->GetDeclaringClass()), + /* declared_can_be_null */ false, + invoke_instruction->InputAt(0u))) { + return true; + } + } + + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + + // Iterate over the list of parameter types and test whether any of the + // actual inputs has a more specific reference type than the type declared in + // the signature. + const DexFile::TypeList* param_list = resolved_method->GetParameterTypeList(); + for (size_t param_idx = 0, + input_idx = resolved_method->IsStatic() ? 0 : 1, + e = (param_list == nullptr ? 0 : param_list->Size()); + param_idx < e; + ++param_idx, ++input_idx) { + HInstruction* input = invoke_instruction->InputAt(input_idx); + if (input->GetType() == Primitive::kPrimNot) { + mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + param_list->GetTypeItem(param_idx).type_idx_, + pointer_size); + if (IsReferenceTypeRefinement(GetClassRTI(param_cls), + /* declared_can_be_null */ true, + input)) { + return true; + } + } + } + + return false; +} + +bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction, + HInstruction* return_replacement) { // Check the integrity of reference types and run another type propagation if needed. if (return_replacement != nullptr) { if (return_replacement->GetType() == Primitive::kPrimNot) { + // Test if the return type is a refinement of the declared return type. + if (IsReferenceTypeRefinement(invoke_instruction->GetReferenceTypeInfo(), + /* declared_can_be_null */ true, + return_replacement)) { + return true; + } + } else if (return_replacement->IsInstanceOf()) { + // Inlining InstanceOf into an If may put a tighter bound on reference types. + return true; + } + } + + return false; +} + +void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, + HInstruction* return_replacement) { + if (return_replacement != nullptr) { + if (return_replacement->GetType() == Primitive::kPrimNot) { if (!return_replacement->GetReferenceTypeInfo().IsValid()) { // Make sure that we have a valid type for the return. We may get an invalid one when // we inline invokes with multiple branches and create a Phi for the result. @@ -1347,36 +1438,7 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, DCHECK(return_replacement->IsPhi()); size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); - if (cls != nullptr && !cls->IsErroneous()) { - ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls); - return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); - } else { - // Return inexact object type on failures. - return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti()); - } - } - - if (do_rtp) { - // If the return type is a refinement of the declared type run the type propagation again. - ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); - ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); - if (invoke_rti.IsStrictSupertypeOf(return_rti) - || (return_rti.IsExact() && !invoke_rti.IsExact()) - || !return_replacement->CanBeNull()) { - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); - } - } - } else if (return_replacement->IsInstanceOf()) { - if (do_rtp) { - // Inlining InstanceOf into an If may put a tighter bound on reference types. - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); + return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 7cf1424b6d..02d3a5f499 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -124,10 +124,18 @@ class HInliner : public HOptimization { uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); - void FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) + void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is + // admissible (see ReferenceTypePropagation::IsAdmissible for details). + // Otherwise returns inexact Object RTI. + ReferenceTypeInfo GetClassRTI(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + + bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) + SHARED_REQUIRES(Locks::mutator_lock_); + + bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement) SHARED_REQUIRES(Locks::mutator_lock_); // Add a type guard on the given `receiver`. This will add to the graph: diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index eb1d1560db..62d637081d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -101,6 +101,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); + void SimplifyStringCharAt(HInvoke* invoke); void SimplifyStringIsEmptyOrLength(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); @@ -1685,13 +1686,32 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 } +void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { + HInstruction* str = invoke->InputAt(0); + HInstruction* index = invoke->InputAt(1); + uint32_t dex_pc = invoke->GetDexPc(); + ArenaAllocator* arena = GetGraph()->GetArena(); + // We treat String as an array to allow DCE and BCE to seamlessly work on strings, + // so create the HArrayLength, HBoundsCheck and HArrayGet. + HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true); + invoke->GetBlock()->InsertInstructionBefore(length, invoke); + HBoundsCheck* bounds_check = + new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex()); + invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); + HArrayGet* array_get = + new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get); + bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment()); + GetGraph()->SetHasBoundsChecks(true); +} + void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) { HInstruction* str = invoke->InputAt(0); uint32_t dex_pc = invoke->GetDexPc(); // We treat String as an array to allow DCE and BCE to seamlessly work on strings, // so create the HArrayLength. - HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc); - length->MarkAsStringLength(); + HArrayLength* length = + new (GetGraph()->GetArena()) HArrayLength(str, dex_pc, /* is_string_length */ true); HInstruction* replacement; if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) { // For String.isEmpty(), create the `HEqual` representing the `length == 0`. @@ -1752,6 +1772,9 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kDoubleDoubleToLongBits: SimplifyFP2Int(instruction); break; + case Intrinsics::kStringCharAt: + SimplifyStringCharAt(instruction); + break; case Intrinsics::kStringIsEmpty: case Intrinsics::kStringLength: SimplifyStringIsEmptyOrLength(instruction); diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index e4a711ec83..983d31d168 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -19,6 +19,7 @@ #include "common_arm64.h" #include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace arm64 { @@ -30,7 +31,7 @@ using helpers::ShifterOperandSupportsExtension; void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access, HInstruction* array, HInstruction* index, - int access_size) { + size_t data_offset) { if (kEmitCompilerReadBarrier) { // The read barrier instrumentation does not support the // HArm64IntermediateAddress instruction yet. @@ -55,8 +56,7 @@ void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstructio // Proceed to extract the base address computation. ArenaAllocator* arena = GetGraph()->GetArena(); - HIntConstant* offset = - GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value()); + HIntConstant* offset = GetGraph()->GetIntConstant(data_offset); HArm64IntermediateAddress* address = new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc); address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); @@ -189,17 +189,20 @@ void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { } void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { + size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); TryExtractArrayAccessAddress(instruction, instruction->GetArray(), instruction->GetIndex(), - Primitive::ComponentSize(instruction->GetType())); + data_offset); } void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { + size_t access_size = Primitive::ComponentSize(instruction->GetComponentType()); + size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value(); TryExtractArrayAccessAddress(instruction, instruction->GetArray(), instruction->GetIndex(), - Primitive::ComponentSize(instruction->GetComponentType())); + data_offset); } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index da269980e8..4735f85ab0 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -38,7 +38,7 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { void TryExtractArrayAccessAddress(HInstruction* access, HInstruction* array, HInstruction* index, - int access_size); + size_t data_offset); bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 83a512738b..3429a8fdbb 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -239,6 +239,7 @@ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ UNREACHABLE_INTRINSIC(Arch, LongCompare) \ UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, StringCharAt) \ UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \ UNREACHABLE_INTRINSIC(Arch, StringLength) \ UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 7d1c2ebe0b..93950d58b5 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -935,55 +935,6 @@ void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { - ArmAssembler* assembler = GetAssembler(); - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array - const MemberOffset value_offset = mirror::String::ValueOffset(); - // Location of count - const MemberOffset count_offset = mirror::String::CountOffset(); - - Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer. - Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character. - Register out = locations->Out().AsRegister<Register>(); // Result character. - - Register temp = locations->GetTemp(0).AsRegister<Register>(); - Register array_temp = locations->GetTemp(1).AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); - codegen_->AddSlowPath(slow_path); - - __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length. - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ cmp(idx, ShifterOperand(temp)); - __ b(slow_path->GetEntryLabel(), CS); - - __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value. - - // Load the value. - __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx]. - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) { // The inputs plus one temp. LocationSummary* locations = new (arena_) LocationSummary(invoke, diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index c8d6ddc8f1..4da0843a76 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1122,56 +1122,6 @@ void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - // In case we need to go in the slow path, we can't have the output be the same - // as the input: the current liveness analysis considers the input to be live - // at the point of the call. - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { - vixl::MacroAssembler* masm = GetVIXLAssembler(); - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array - const MemberOffset value_offset = mirror::String::ValueOffset(); - // Location of count - const MemberOffset count_offset = mirror::String::CountOffset(); - - Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer. - Register idx = WRegisterFrom(locations->InAt(1)); // Index of character. - Register out = WRegisterFrom(locations->Out()); // Result character. - - UseScratchRegisterScope temps(masm); - Register temp = temps.AcquireW(); - Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling. - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); - codegen_->AddSlowPath(slow_path); - - __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length. - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ Cmp(idx, temp); - __ B(hs, slow_path->GetEntryLabel()); - - __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value. - - // Load the value. - __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx]. - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, invoke->InputAt(1)->CanBeNull() diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 140f56a870..d4f44d63e2 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1872,54 +1872,6 @@ void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -// char java.lang.String.charAt(int index) -void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - // The inputs will be considered live at the last instruction and restored. This would overwrite - // the output with kNoOutputOverlap. - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - MipsAssembler* assembler = GetAssembler(); - - // Location of reference to data array - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - Register obj = locations->InAt(0).AsRegister<Register>(); - Register idx = locations->InAt(1).AsRegister<Register>(); - Register out = locations->Out().AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, - // though, I think it's not worth the cost. - // TODO: For simplicity, the index parameter is requested in a - // register, so different from Quick we will not optimize the - // code for constants (which would save a register). - - SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); - codegen_->AddSlowPath(slow_path); - - // Load the string size - __ Lw(TMP, obj, count_offset); - codegen_->MaybeRecordImplicitNullCheck(invoke); - // Revert to slow path if idx is too large, or negative - __ Bgeu(idx, TMP, slow_path->GetEntryLabel()); - - // out = obj[2*idx]. - __ Sll(TMP, idx, 1); // idx * 2 - __ Addu(TMP, TMP, obj); // Address of char at location idx - __ Lhu(out, TMP, value_offset); // Load char at location idx - - __ Bind(slow_path->GetExitLabel()); -} - // int java.lang.String.compareTo(String anotherString) void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 6c4e64e4b1..9243f4c93f 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1371,52 +1371,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -// char java.lang.String.charAt(int index) -void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicCodeGeneratorMIPS64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - Mips64Assembler* assembler = GetAssembler(); - - // Location of reference to data array - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); - GpuRegister idx = locations->InAt(1).AsRegister<GpuRegister>(); - GpuRegister out = locations->Out().AsRegister<GpuRegister>(); - - // TODO: Maybe we can support range check elimination. Overall, - // though, I think it's not worth the cost. - // TODO: For simplicity, the index parameter is requested in a - // register, so different from Quick we will not optimize the - // code for constants (which would save a register). - - SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); - codegen_->AddSlowPath(slow_path); - - // Load the string size - __ Lw(TMP, obj, count_offset); - codegen_->MaybeRecordImplicitNullCheck(invoke); - // Revert to slow path if idx is too large, or negative - __ Bgeuc(idx, TMP, slow_path->GetEntryLabel()); - - // out = obj[2*idx]. - __ Sll(TMP, idx, 1); // idx * 2 - __ Daddu(TMP, TMP, obj); // Address of char at location idx - __ Lhu(out, TMP, value_offset); // Load char at location idx - - __ Bind(slow_path->GetExitLabel()); -} - // int java.lang.String.compareTo(String anotherString) void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 05377f984b..4988398c92 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1030,48 +1030,6 @@ void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickNextAfter); } -void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) { - // The inputs plus one temp. - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array. - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count. - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - Register obj = locations->InAt(0).AsRegister<Register>(); - Register idx = locations->InAt(1).AsRegister<Register>(); - Register out = locations->Out().AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); - codegen_->AddSlowPath(slow_path); - - X86Assembler* assembler = GetAssembler(); - - __ cmpl(idx, Address(obj, count_offset)); - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ j(kAboveEqual, slow_path->GetEntryLabel()); - - // out = out[2*idx]. - __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) { // We need at least two of the positions or length to be an integer constant, // or else we won't have enough free registers. diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 67c2f3a866..593c8f319b 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -891,49 +891,6 @@ void IntrinsicCodeGeneratorX86_64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickNextAfter); } -void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) { - // The inputs plus one temp. - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array. - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count. - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); - CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>(); - CpuRegister out = locations->Out().AsRegister<CpuRegister>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); - codegen_->AddSlowPath(slow_path); - - X86_64Assembler* assembler = GetAssembler(); - - __ cmpl(idx, Address(obj, count_offset)); - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ j(kAboveEqual, slow_path->GetEntryLabel()); - - // out = out[2*idx]. - __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { // Check to see if we have known failures that will cause us to have to bail out // to the runtime, and just generate the runtime call directly. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 74a4acae3f..29df7c8ab8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/stl_util.h" #include "dex/compiler_enums.h" +#include "dex_file.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -171,6 +172,10 @@ class ReferenceTypeInfo : ValueObject { static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + static ReferenceTypeInfo Create(TypeHandle type_handle) SHARED_REQUIRES(Locks::mutator_lock_) { + return Create(type_handle, type_handle->CannotBeAssignedFromOtherTypes()); + } + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -1930,6 +1935,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { environment_ = environment; } + void InsertRawEnvironment(HEnvironment* environment) { + DCHECK(environment_ != nullptr); + DCHECK_EQ(environment->GetHolder(), this); + DCHECK(environment->GetParent() == nullptr); + environment->parent_ = environment_; + environment_ = environment; + } + void RemoveEnvironment(); // Set the environment of this instruction, copying it from `environment`. While @@ -5089,8 +5102,13 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { class HArrayGet FINAL : public HExpression<2> { public: - HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc) + HArrayGet(HInstruction* array, + HInstruction* index, + Primitive::Type type, + uint32_t dex_pc, + bool is_string_char_at = false) : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) { + SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at); SetRawInputAt(0, array); SetRawInputAt(1, index); } @@ -5124,12 +5142,24 @@ class HArrayGet FINAL : public HExpression<2> { return result; } + bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); } + HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } DECLARE_INSTRUCTION(ArrayGet); private: + // We treat a String as an array, creating the HArrayGet from String.charAt() + // intrinsic in the instruction simplifier. We can always determine whether + // a particular HArrayGet is actually a String.charAt() by looking at the type + // of the input but that requires holding the mutator lock, so we prefer to use + // a flag, so that code generators don't need to do the locking. + static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; + static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; @@ -5235,8 +5265,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> { class HArrayLength FINAL : public HExpression<1> { public: - HArrayLength(HInstruction* array, uint32_t dex_pc) + HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false) : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { + SetPackedFlag<kFlagIsStringLength>(is_string_length); // Note that arrays do not change length, so the instruction does not // depend on any write. SetRawInputAt(0, array); @@ -5250,7 +5281,6 @@ class HArrayLength FINAL : public HExpression<1> { return obj == InputAt(0); } - void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); } bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); } DECLARE_INSTRUCTION(ArrayLength); @@ -5273,8 +5303,12 @@ class HBoundsCheck FINAL : public HExpression<2> { public: // `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException` // constructor. - HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc) - : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { + HBoundsCheck(HInstruction* index, + HInstruction* length, + uint32_t dex_pc, + uint32_t string_char_at_method_index = DexFile::kDexNoIndex) + : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc), + string_char_at_method_index_(string_char_at_method_index) { DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType())); SetRawInputAt(0, index); SetRawInputAt(1, length); @@ -5289,11 +5323,23 @@ class HBoundsCheck FINAL : public HExpression<2> { bool CanThrow() const OVERRIDE { return true; } + bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; } + uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; } + HInstruction* GetIndex() const { return InputAt(0); } DECLARE_INSTRUCTION(BoundsCheck); private: + // We treat a String as an array, creating the HBoundsCheck from String.charAt() + // intrinsic in the instruction simplifier. We want to include the String.charAt() + // in the stack trace if we actually throw the StringIndexOutOfBoundsException, + // so we need to create an HEnvironment which will be translated to an InlineInfo + // indicating the extra stack frame. Since we add this HEnvironment quite late, + // in the PrepareForRegisterAllocation pass, we need to remember the method index + // from the invoke as we don't want to look again at the dex bytecode. + uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array. + DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index c941c0c086..696b8c6859 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -40,6 +40,22 @@ void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) { void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); + if (check->IsStringCharAt()) { + // Add a fake environment for String.charAt() inline info as we want + // the exception to appear as being thrown from there. + const DexFile& dex_file = check->GetEnvironment()->GetDexFile(); + DCHECK_STREQ(PrettyMethod(check->GetStringCharAtMethodIndex(), dex_file).c_str(), + "char java.lang.String.charAt(int)"); + ArenaAllocator* arena = GetGraph()->GetArena(); + HEnvironment* environment = new (arena) HEnvironment(arena, + /* number_of_vregs */ 0u, + dex_file, + check->GetStringCharAtMethodIndex(), + /* dex_pc */ DexFile::kDexNoIndex, + kVirtual, + check); + check->InsertRawEnvironment(environment); + } } void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) { diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 3e6adcb172..3dfd7282cd 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -46,13 +46,6 @@ static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollec return *cache; } -// Returns true if klass is admissible to the propagation: non-null and resolved. -// For an array type, we also check if the component type is admissible. -static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { - return klass != nullptr && klass->IsResolved() && - (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); -} - ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 2106be6b53..edd83bf5de 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -42,6 +42,14 @@ class ReferenceTypePropagation : public HOptimization { void Run() OVERRIDE; + // Returns true if klass is admissible to the propagation: non-null and resolved. + // For an array type, we also check if the component type is admissible. + static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { + return klass != nullptr && + klass->IsResolved() && + (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); + } + static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation"; private: diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 11a254ef63..fc8af6462a 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -228,7 +228,7 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const { void StackMapStream::ComputeInlineInfoEncoding() { uint32_t method_index_max = 0; - uint32_t dex_pc_max = 0; + uint32_t dex_pc_max = DexFile::kDexNoIndex; uint32_t invoke_type_max = 0; uint32_t inline_info_index = 0; @@ -236,7 +236,10 @@ void StackMapStream::ComputeInlineInfoEncoding() { for (size_t j = 0; j < entry.inlining_depth; ++j) { InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; method_index_max = std::max(method_index_max, inline_entry.method_index); - dex_pc_max = std::max(dex_pc_max, inline_entry.dex_pc); + if (inline_entry.dex_pc != DexFile::kDexNoIndex && + (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { + dex_pc_max = inline_entry.dex_pc; + } invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type)); } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 41f72f508b..53a9795d52 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -108,7 +108,7 @@ class StackMapStream : public ValueObject { }; struct InlineInfoEntry { - uint32_t dex_pc; + uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. uint32_t method_index; InvokeType invoke_type; uint32_t num_dex_registers; diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index fb37804649..6921780a85 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -227,9 +227,18 @@ class TestDexFileBuilder { // Write the complete header again, just simpler that way. std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); + static constexpr bool kVerify = false; + static constexpr bool kVerifyChecksum = false; std::string error_msg; std::unique_ptr<const DexFile> dex_file(DexFile::Open( - &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg)); + &dex_file_data_[0], + dex_file_data_.size(), + dex_location, + 0u, + nullptr, + kVerify, + kVerifyChecksum, + &error_msg)); CHECK(dex_file != nullptr) << error_msg; return dex_file; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2dce2f1d47..24a4d586a2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1985,8 +1985,10 @@ class Dex2Oat FINAL { if (location == OatFile::kSpecialSharedLibrary) { break; } + static constexpr bool kVerifyChecksum = true; std::string error_msg; - if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) { + if (!DexFile::Open( + location.c_str(), location.c_str(), kVerifyChecksum, &error_msg, opened_dex_files)) { // If we fail to open the dex file because it's been stripped, try to open the dex file // from its corresponding oat file. OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 9e06a7c31b..48b773e90b 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1635,11 +1635,10 @@ int processFile(const char* fileName) { // If the file is not a .dex file, the function tries .zip/.jar/.apk files, // all of which are Zip archives with "classes.dex" inside. - // - // TODO(ajcbik): implement gOptions.ignoreBadChecksum + const bool kVerifyChecksum = !gOptions.ignoreBadChecksum; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { + if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) { // Display returned error message to user. Note that this error behavior // differs from the error messages shown by the original Dalvik dexdump. fputs(error_msg.c_str(), stderr); diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index 32e9d521c1..f716ba8be7 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -17,8 +17,8 @@ * * This is a re-implementation of the original dexdump utility that was * based on Dalvik functions in libdex into a new dexdump that is now - * based on Art functions in libart instead. The output is identical to - * the original for correct DEX files. Error messages may differ, however. + * based on Art functions in libart instead. The output is very similar to + * to the original for correct DEX files. Error messages may differ, however. * Also, ODEX files are no longer supported. */ diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index d20c16919a..6f19df55fd 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -180,9 +180,10 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { static int processFile(const char* fileName) { // If the file is not a .dex file, the function tries .zip/.jar/.apk files, // all of which are Zip archives with "classes.dex" inside. + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { + if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) { fputs(error_msg.c_str(), stderr); fputc('\n', stderr); return -1; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7239a479db..bb35f8dc30 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -35,7 +35,7 @@ #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex_file-inl.h" -#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "disassembler.h" #include "elf_builder.h" #include "gc/space/image_space.h" @@ -578,6 +578,7 @@ class OatDumper { // Print embedded dex file data range. const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin(); const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer(); + std::set<uint32_t> string_ids; uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - oat_file_begin); os << StringPrintf("dex-file: 0x%08x..0x%08x\n", dex_offset, @@ -623,7 +624,7 @@ class OatDumper { << " (" << oat_class.GetType() << ")\n"; // TODO: include bitmap here if type is kOatClassSomeCompiled? if (options_.list_classes_) continue; - if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis)) { + if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis, string_ids)) { success = false; } if (stop_analysis) { @@ -631,7 +632,7 @@ class OatDumper { return success; } } - + os << "Number of unique strings loaded from dex code: " << string_ids.size() << "\n"; os << std::flush; return success; } @@ -725,7 +726,8 @@ class OatDumper { bool DumpOatClass(VariableIndentationOutputStream* vios, const OatFile::OatClass& oat_class, const DexFile& dex_file, - const DexFile::ClassDef& class_def, bool* stop_analysis) { + const DexFile::ClassDef& class_def, bool* stop_analysis, + std::set<uint32_t>& string_ids) { bool success = true; bool addr_found = false; const uint8_t* class_data = dex_file.GetClassData(class_def); @@ -739,7 +741,7 @@ class OatDumper { while (it.HasNextDirectMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { + it.GetRawMemberAccessFlags(), &addr_found, string_ids)) { success = false; } if (addr_found) { @@ -752,7 +754,7 @@ class OatDumper { while (it.HasNextVirtualMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { + it.GetRawMemberAccessFlags(), &addr_found, string_ids)) { success = false; } if (addr_found) { @@ -777,9 +779,35 @@ class OatDumper { uint32_t class_method_index, const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, bool* addr_found) { + uint32_t method_access_flags, bool* addr_found, + std::set<uint32_t>& string_ids) { bool success = true; + if (code_item != nullptr) { + const uint16_t* code_ptr = code_item->insns_; + const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_; + + while (code_ptr < code_end) { + const Instruction* inst = Instruction::At(code_ptr); + switch (inst->Opcode()) { + case Instruction::CONST_STRING: { + uint32_t string_index = inst->VRegB_21c(); + string_ids.insert(string_index); + break; + } + case Instruction::CONST_STRING_JUMBO: { + uint32_t string_index = inst->VRegB_31c(); + string_ids.insert(string_index); + break; + } + + default: + break; + } + + code_ptr += inst->SizeInCodeUnits(); + } + } // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); if (method_name.find(options_.method_filter_) == std::string::npos) { diff --git a/profman/profman.cc b/profman/profman.cc index 754e4315d0..d2c9cb2e3f 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -234,6 +234,7 @@ class ProfMan FINAL { MemMap::Init(); // for ZipArchive::OpenFromFd std::vector<const DexFile*> dex_files; assert(dex_locations_.size() == apks_fd_.size()); + static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; @@ -246,6 +247,7 @@ class ProfMan FINAL { } if (DexFile::OpenFromZip(*zip_archive, dex_locations_[i], + kVerifyChecksum, &error_msg, &dex_files_for_location)) { } else { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index dd7063f248..27a41f09ad 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -310,6 +310,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * Called by managed code to create and deliver a StackOverflowError. */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 1fba09bae3..a6490aed33 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -417,6 +417,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * Called by managed code to create and deliver a StackOverflowError. */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 45e33a8500..833ba1b612 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -268,6 +268,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickThrowNullPointer), "Non-direct C stub marked direct."); qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; static_assert(!IsDirectEntrypoint(kQuickThrowStackOverflow), "Non-direct C stub marked direct."); + qpoints->pThrowStringBounds = art_quick_throw_string_bounds; + static_assert(!IsDirectEntrypoint(kQuickThrowStringBounds), "Non-direct C stub marked direct."); // Deoptimization from compiled code. qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 3ee26afc4f..bb89674caf 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -529,6 +529,18 @@ ENTRY art_quick_throw_array_bounds END art_quick_throw_array_bounds /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). + */ + .extern artThrowStringBoundsFromCode +ENTRY art_quick_throw_string_bounds + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + la $t9, artThrowStringBoundsFromCode + jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*) + move $a2, rSELF # pass Thread::Current +END art_quick_throw_string_bounds + + /* * Called by managed code to create and deliver a StackOverflowError. */ .extern artThrowStackOverflowFromCode diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8f1a35a693..78ac748e32 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -617,6 +617,19 @@ ENTRY art_quick_throw_array_bounds END art_quick_throw_array_bounds /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). + */ + .extern artThrowStringBoundsFromCode +ENTRY art_quick_throw_string_bounds +.Lart_quick_throw_string_bounds_gp_set: + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + dla $t9, artThrowStringBoundsFromCode + jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*) + move $a2, rSELF # pass Thread::Current +END art_quick_throw_string_bounds + + /* * Called by managed code to create and deliver a StackOverflowError. */ .extern artThrowStackOverflowFromCode diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 0b6ddc1af1..b3dd4545f4 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -311,6 +311,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain * the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 8064ed696f..205307ce67 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -336,6 +336,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 480644a5b2..2d702f6af7 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -127,32 +127,32 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) -// Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 166 * __SIZEOF_POINTER__) -ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, - art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tlsPtr_.thread_local_objects. +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) +ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, + art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) +#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. -#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) +#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_CURRENT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET, art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_alt_ibase. -#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) +#define THREAD_ALT_IBASE_OFFSET (THREAD_DEFAULT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET, art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.rosalloc_runs. -#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) +#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_ALT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET, art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top. diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 5bdb36cafc..3509d9aef9 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -284,7 +284,8 @@ std::unique_ptr<const DexFile> CommonRuntimeTestImpl::LoadExpectSingleDexFile( std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; MemMap::Init(); - if (!DexFile::Open(location, location, &error_msg, &dex_files)) { + static constexpr bool kVerifyChecksum = true; + if (!DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)) { LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; UNREACHABLE(); } else { @@ -480,9 +481,11 @@ std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) { std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFiles( const char* name) { std::string filename = GetTestDexFileName(name); + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); + bool success = DexFile::Open( + filename.c_str(), filename.c_str(), kVerifyChecksum, &error_msg, &dex_files); CHECK(success) << "Failed to open '" << filename << "': " << error_msg; for (auto& dex_file : dex_files) { CHECK_EQ(PROT_READ, dex_file->GetPermissions()); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 75cce424e9..4f705f2056 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -654,6 +654,13 @@ void ThrowStackOverflowError(Thread* self) { } } +// StringIndexOutOfBoundsException + +void ThrowStringIndexOutOfBoundsException(int index, int length) { + ThrowException("Ljava/lang/StringIndexOutOfBoundsException;", nullptr, + StringPrintf("length=%d; index=%d", length, index).c_str()); +} + // VerifyError void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) { diff --git a/runtime/common_throws.h b/runtime/common_throws.h index c3a1f09db3..7a335859e5 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -211,6 +211,11 @@ void ThrowRuntimeException(const char* fmt, ...) void ThrowStackOverflowError(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; +// StringIndexOutOfBoundsException + +void ThrowStringIndexOutOfBoundsException(int index, int length) + SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; + // VerifyError void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 05c95e069e..5a203afd1a 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -109,7 +109,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* } if (IsDexMagic(magic)) { std::unique_ptr<const DexFile> dex_file( - DexFile::OpenFile(fd.release(), filename, false, error_msg)); + DexFile::OpenFile(fd.release(), filename, false, false, error_msg)); if (dex_file.get() == nullptr) { return false; } @@ -120,7 +120,10 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* return false; } -bool DexFile::Open(const char* filename, const char* location, std::string* error_msg, +bool DexFile::Open(const char* filename, + const char* location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace(std::string("Open dex file ") + location); DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; @@ -131,10 +134,13 @@ bool DexFile::Open(const char* filename, const char* location, std::string* erro return false; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd.release(), location, error_msg, dex_files); + return DexFile::OpenZip(fd.release(), location, verify_checksum, error_msg, dex_files); } if (IsDexMagic(magic)) { - std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true, + std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), + location, + /* verify */ true, + verify_checksum, error_msg)); if (dex_file.get() != nullptr) { dex_files->push_back(std::move(dex_file)); @@ -207,6 +213,7 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, uint32_t location_checksum, const OatDexFile* oat_dex_file, bool verify, + bool verify_checksum, std::string* error_msg) { ScopedTrace trace(std::string("Open dex file from RAM ") + location); std::unique_ptr<const DexFile> dex_file = OpenMemory(base, @@ -220,6 +227,7 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, dex_file->Begin(), dex_file->Size(), location.c_str(), + verify_checksum, error_msg)) { return nullptr; } @@ -227,7 +235,10 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, return dex_file; } -std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify, +std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, + const char* location, + bool verify, + bool verify_checksum, std::string* error_msg) { ScopedTrace trace(std::string("Open dex file ") + location); CHECK(location != nullptr); @@ -276,7 +287,9 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, b } if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), - location, error_msg)) { + location, + verify_checksum, + error_msg)) { return nullptr; } @@ -285,7 +298,10 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, b const char* DexFile::kClassesDex = "classes.dex"; -bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg, +bool DexFile::OpenZip(int fd, + const std::string& location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace("Dex file open Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr"; @@ -294,7 +310,7 @@ bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_ms DCHECK(!error_msg->empty()); return false; } - return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files); + return DexFile::OpenFromZip(*zip_archive, location, verify_checksum, error_msg, dex_files); } std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location, @@ -310,8 +326,11 @@ std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location, error_msg); } -std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name, - const std::string& location, std::string* error_msg, +std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify_checksum, + std::string* error_msg, ZipOpenErrorCode* error_code) { ScopedTrace trace("Dex file open from Zip Archive " + std::string(location)); CHECK(!location.empty()); @@ -342,7 +361,9 @@ std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, cons } CHECK(dex_file->IsReadOnly()) << location; if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), - location.c_str(), error_msg)) { + location.c_str(), + verify_checksum, + error_msg)) { *error_code = ZipOpenErrorCode::kVerifyError; return nullptr; } @@ -356,14 +377,16 @@ std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, cons // seems an excessive number. static constexpr size_t kWarnOnManyDexFilesThreshold = 100; -bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location, +bool DexFile::OpenFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify_checksum, std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace("Dex file open from Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr"; ZipOpenErrorCode error_code; - std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg, - &error_code)); + std::unique_ptr<const DexFile> dex_file( + Open(zip_archive, kClassesDex, location, verify_checksum, error_msg, &error_code)); if (dex_file.get() == nullptr) { return false; } else { @@ -378,8 +401,8 @@ bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& loca for (size_t i = 1; ; ++i) { std::string name = GetMultiDexClassesDexName(i); std::string fake_location = GetMultiDexLocation(i, location.c_str()); - std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location, - error_msg, &error_code)); + std::unique_ptr<const DexFile> next_dex_file( + Open(zip_archive, name.c_str(), fake_location, verify_checksum, error_msg, &error_code)); if (next_dex_file.get() == nullptr) { if (error_code != ZipOpenErrorCode::kEntryNotFound) { LOG(WARNING) << error_msg; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 638821bfb7..759986e183 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -416,7 +416,10 @@ class DexFile { static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); // Opens .dex files found in the container, guessing the container format based on file extension. - static bool Open(const char* filename, const char* location, std::string* error_msg, + static bool Open(const char* filename, + const char* location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry. @@ -429,10 +432,13 @@ class DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, bool verify, + bool verify_checksum, std::string* error_msg); // Open all classesXXX.dex files from a zip archive. - static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + static bool OpenFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify_checksum, std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); @@ -1177,11 +1183,17 @@ class DexFile { private: // Opens a .dex file - static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location, - bool verify, std::string* error_msg); + static std::unique_ptr<const DexFile> OpenFile(int fd, + const char* location, + bool verify, + bool verify_checksum, + std::string* error_msg); // Opens dex files from within a .jar, .zip, or .apk file - static bool OpenZip(int fd, const std::string& location, std::string* error_msg, + static bool OpenZip(int fd, + const std::string& location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); enum class ZipOpenErrorCode { // private @@ -1195,8 +1207,11 @@ class DexFile { // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null // return. - static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name, - const std::string& location, std::string* error_msg, + static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify_checksum, + std::string* error_msg, ZipOpenErrorCode* error_code); // Opens a .dex file at the given address backed by a MemMap diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 796701d86b..4f8e6f1fc0 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -154,9 +154,10 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> tmp; - bool success = DexFile::Open(location, location, &error_msg, &tmp); + bool success = DexFile::Open(location, location, kVerifyChecksum, &error_msg, &tmp); CHECK(success) << error_msg; EXPECT_EQ(1U, tmp.size()); std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 1d243490ab..5132efc03c 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -128,9 +128,14 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const error_stmt; \ } -bool DexFileVerifier::Verify(const DexFile* dex_file, const uint8_t* begin, size_t size, - const char* location, std::string* error_msg) { - std::unique_ptr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location)); +bool DexFileVerifier::Verify(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum, + std::string* error_msg) { + std::unique_ptr<DexFileVerifier> verifier( + new DexFileVerifier(dex_file, begin, size, location, verify_checksum)); if (!verifier->Verify()) { *error_msg = verifier->FailureReason(); return false; @@ -273,8 +278,13 @@ bool DexFileVerifier::CheckHeader() { const uint8_t* non_sum_ptr = reinterpret_cast<const uint8_t*>(header_) + non_sum; adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum); if (adler_checksum != header_->checksum_) { - ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); - return false; + if (verify_checksum_) { + ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); + return false; + } else { + LOG(WARNING) << StringPrintf( + "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); + } } // Check the contents of the header. diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 90409db44b..133e4326bc 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -26,17 +26,31 @@ namespace art { class DexFileVerifier { public: - static bool Verify(const DexFile* dex_file, const uint8_t* begin, size_t size, - const char* location, std::string* error_msg); + static bool Verify(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum, + std::string* error_msg); const std::string& FailureReason() const { return failure_reason_; } private: - DexFileVerifier(const DexFile* dex_file, const uint8_t* begin, size_t size, const char* location) - : dex_file_(dex_file), begin_(begin), size_(size), location_(location), - header_(&dex_file->GetHeader()), ptr_(nullptr), previous_item_(nullptr) { + DexFileVerifier(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum) + : dex_file_(dex_file), + begin_(begin), + size_(size), + location_(location), + verify_checksum_(verify_checksum), + header_(&dex_file->GetHeader()), + ptr_(nullptr), + previous_item_(nullptr) { } bool Verify(); @@ -176,6 +190,7 @@ class DexFileVerifier { const uint8_t* const begin_; const size_t size_; const char* const location_; + const bool verify_checksum_; const DexFile::Header* const header_; struct OffsetTypeMapEmptyFn { diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 4e53914374..71c0ad9295 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -122,6 +122,10 @@ static void FixUpChecksum(uint8_t* dex_file) { class DexFileVerifierTest : public CommonRuntimeTest { protected: + DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) { + return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr); + } + void VerifyModification(const char* dex_file_base64_content, const char* location, std::function<void(DexFile*)> f, @@ -130,16 +134,17 @@ class DexFileVerifierTest : public CommonRuntimeTest { std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length); CHECK(dex_bytes != nullptr); // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file( - new DexFile(dex_bytes.get(), length, "tmp", 0, nullptr, nullptr)); + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); f(dex_file.get()); FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin())); + static constexpr bool kVerifyChecksum = true; std::string error_msg; bool success = DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), location, + kVerifyChecksum, &error_msg); if (expected_error == nullptr) { EXPECT_TRUE(success) << error_msg; @@ -175,7 +180,7 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); std::vector<std::unique_ptr<const DexFile>> tmp; - bool success = DexFile::Open(location, location, error_msg, &tmp); + bool success = DexFile::Open(location, location, true, error_msg, &tmp); CHECK(success) << error_msg; EXPECT_EQ(1U, tmp.size()); std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); @@ -1697,4 +1702,45 @@ TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) { " implemented interface with type idx: '0'"); } +TEST_F(DexFileVerifierTest, Checksum) { + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(kGoodTestDex, &length); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + + // Good checksum: all pass. + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good checksum, no verify", + /*verify_checksum*/ false, + &error_msg)); + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good checksum, verify", + /*verify_checksum*/ true, + &error_msg)); + + // Bad checksum: !verify_checksum passes verify_checksum fails. + DexFile::Header* header = reinterpret_cast<DexFile::Header*>( + const_cast<uint8_t*>(dex_file->Begin())); + header->checksum_ = 0; + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad checksum, no verify", + /*verify_checksum*/ false, + &error_msg)); + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad checksum, verify", + /*verify_checksum*/ true, + &error_msg)); + EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg; +} + } // namespace art diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index f3a0d2f3ef..771e14396e 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -121,5 +121,6 @@ extern "C" void art_quick_throw_div_zero(); extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); +extern "C" void art_quick_throw_string_bounds(int32_t index, int32_t limit); #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_ diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 5dafa8b599..f98de95fcb 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -114,6 +114,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method; qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception; qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; + qpoints->pThrowStringBounds = art_quick_throw_string_bounds; // Deoptimize qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 79d1c1377b..30b639eaec 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -140,6 +140,7 @@ V(ThrowNoSuchMethod, void, int32_t) \ V(ThrowNullPointer, void, void) \ V(ThrowStackOverflow, void, void*) \ + V(ThrowStringBounds, void, int32_t, int32_t) \ V(Deoptimize, void, void) \ \ V(A64Load, int64_t, volatile const int64_t *) \ diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 5256feae2b..2778e32ece 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -29,7 +29,7 @@ extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an exception. +// Called by generated code to throw an exception. extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { /* @@ -48,7 +48,7 @@ extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* excepti self->QuickDeliverException(); } -// Called by generated call to throw a NPE exception. +// Called by generated code to throw a NPE exception. extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -58,7 +58,7 @@ extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an arithmetic divide by zero exception. +// Called by generated code to throw an arithmetic divide by zero exception. extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -66,7 +66,7 @@ extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an array index out of bounds exception. +// Called by generated code to throw an array index out of bounds exception. extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -74,6 +74,14 @@ extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thr self->QuickDeliverException(); } +// Called by generated code to throw a string index out of bounds exception. +extern "C" NO_RETURN void artThrowStringBoundsFromCode(int index, int length, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + ThrowStringIndexOutOfBoundsException(index, length); + self->QuickDeliverException(); +} + extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 91deea0161..7a624b211c 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -121,10 +121,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { // Skip across the entrypoints structures. - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); @@ -287,7 +287,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pDeoptimize, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pThrowStringBounds, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStringBounds, pDeoptimize, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeoptimize, pA64Load, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*)); diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index f9d06c480e..96f2098ab0 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -20,6 +20,7 @@ #include "array.h" #include "base/bit_utils.h" #include "class.h" +#include "common_throws.h" #include "gc/heap-inl.h" #include "globals.h" #include "intern_table.h" @@ -134,9 +135,7 @@ inline String* String::Intern() { inline uint16_t String::CharAt(int32_t index) { int32_t count = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_)); if (UNLIKELY((index < 0) || (index >= count))) { - Thread* self = Thread::Current(); - self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", - "length=%i; index=%i", count, index); + ThrowStringIndexOutOfBoundsException(index, count); return 0; } return GetValue()[index]; diff --git a/runtime/oat.h b/runtime/oat.h index 52d4c4209e..6243660494 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '8', '1', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '8', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 62c723e76f..61dc287927 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1167,12 +1167,15 @@ size_t OatFile::OatDexFile::FileSize() const { std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { ScopedTrace trace(__PRETTY_FUNCTION__); + static constexpr bool kVerify = false; + static constexpr bool kVerifyChecksum = false; return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_, dex_file_location_checksum_, this, - false /* verify */, + kVerify, + kVerifyChecksum, error_msg); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index c79a9a67b3..e3cc77f2c0 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -56,12 +56,11 @@ class OatFileAssistantTest : public CommonRuntimeTest { odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); - // Verify the environment is as we expect uint32_t checksum; std::string error_msg; - ASSERT_TRUE(OS::FileExists(GetImageFile().c_str())) - << "Expected pre-compiled boot image to be at: " << GetImageFile(); + ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) + << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str())) << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) @@ -73,20 +72,42 @@ class OatFileAssistantTest : public CommonRuntimeTest { // GetMultiDexSrc2 should have the same primary dex checksum as // GetMultiDexSrc1, but a different secondary dex checksum. + static constexpr bool kVerifyChecksum = true; std::vector<std::unique_ptr<const DexFile>> multi1; ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(), - GetMultiDexSrc1().c_str(), &error_msg, &multi1)) << error_msg; + GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg; ASSERT_GT(multi1.size(), 1u); std::vector<std::unique_ptr<const DexFile>> multi2; ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(), - GetMultiDexSrc2().c_str(), &error_msg, &multi2)) << error_msg; + GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg; ASSERT_GT(multi2.size(), 1u); ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum()); ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum()); } + // Pre-Relocate the image to a known non-zero offset so we don't have to + // deal with the runtime randomly relocating the image by 0 and messing up + // the expected results of the tests. + bool PreRelocateImage(std::string* error_msg) { + std::string image; + if (!GetCachedImageFile(&image, error_msg)) { + return false; + } + + std::string patchoat = GetAndroidRoot(); + patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; + + std::vector<std::string> argv; + argv.push_back(patchoat); + argv.push_back("--input-image-location=" + GetImageLocation()); + argv.push_back("--output-image-file=" + image); + argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); + argv.push_back("--base-offset-delta=0x00008000"); + return Exec(argv, error_msg); + } + virtual void SetUpRuntimeOptions(RuntimeOptions* options) { // options->push_back(std::make_pair("-verbose:oat", nullptr)); @@ -99,6 +120,9 @@ class OatFileAssistantTest : public CommonRuntimeTest { } virtual void PreRuntimeCreate() { + std::string error_msg; + ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg; + UnreserveImageSpace(); } @@ -144,11 +168,16 @@ class OatFileAssistantTest : public CommonRuntimeTest { return GetImageDirectory() + "/core.art"; } - std::string GetImageFile() { + std::string GetSystemImageFile() { return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA) + "/core.art"; } + bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) { + std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true); + return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg); + } + std::string GetDexSrc1() { return GetTestDexFileName("Main"); } @@ -189,34 +218,31 @@ class OatFileAssistantTest : public CommonRuntimeTest { // The generated odex file will be un-relocated. void GenerateOdexForTest(const std::string& dex_location, const std::string& odex_location, - CompilerFilter::Filter filter) { - // To generate an un-relocated odex file, we first compile a relocated - // version of the file, then manually call patchoat to make it look as if - // it is unrelocated. - std::string relocated_odex_location = odex_location + ".relocated"; + CompilerFilter::Filter filter, + bool pic = false, + bool with_patch_info = true) { + // Temporarily redirect the dalvik cache so dex2oat doesn't find the + // relocated image file. + std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; + setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + relocated_odex_location); + args.push_back("--oat-file=" + odex_location); args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--runtime-arg"); + args.push_back("-Xnorelocate"); - // We need to use the quick compiler to generate non-PIC code, because - // the optimizing compiler always generates PIC. - args.push_back("--compiler-backend=Quick"); - args.push_back("--include-patch-information"); + if (pic) { + args.push_back("--compile-pic"); + } + + if (with_patch_info) { + args.push_back("--include-patch-information"); + } std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Use patchoat to unrelocate the relocated odex file. - Runtime* runtime = Runtime::Current(); - std::vector<std::string> argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); - argv.push_back("--input-oat-file=" + relocated_odex_location); - argv.push_back("--output-oat-file=" + odex_location); - argv.push_back("--base-offset-delta=0x00008000"); - std::string command_line(Join(argv, ' ')); - ASSERT_TRUE(Exec(argv, &error_msg)) << error_msg; + setenv("ANDROID_DATA", android_data_.c_str(), 1); // Verify the odex file was generated as expected and really is // unrelocated. @@ -229,13 +255,13 @@ class OatFileAssistantTest : public CommonRuntimeTest { dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_TRUE(odex_file->HasPatchInfo()); + EXPECT_EQ(pic, odex_file->IsPic()); + EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo()); EXPECT_EQ(filter, odex_file->GetCompilerFilter()); if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { const std::vector<gc::space::ImageSpace*> image_spaces = - runtime->GetHeap()->GetBootImageSpaces(); + Runtime::Current()->GetHeap()->GetBootImageSpaces(); ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); const OatHeader& oat_header = odex_file->GetOatHeader(); @@ -250,71 +276,15 @@ class OatFileAssistantTest : public CommonRuntimeTest { void GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--compile-pic"); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_TRUE(odex_file->IsPic()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, true, false); } // Generate a non-PIC odex file without patch information for the purposes // of test. The generated odex file will be un-relocated. - // TODO: This won't work correctly if we depend on the boot image being - // randomly relocated by a non-zero amount. We should have a better solution - // for avoiding that flakiness and duplicating code to generate odex and oat - // files for test. void GenerateNoPatchOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_FALSE(odex_file->HasPatchInfo()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, false, false); } private: @@ -326,11 +296,10 @@ class OatFileAssistantTest : public CommonRuntimeTest { MemMap::Init(); // Ensure a chunk of memory is reserved for the image space. - uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA; - uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA - // Include the main space that has to come right after the - // image in case of the GSS collector. - + 384 * MB; + // The reservation_end includes room for the main space that has to come + // right after the image in case of the GSS collector. + uintptr_t reservation_start = ART_BASE_ADDRESS; + uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB; std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true)); ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index fbae1daf43..b7e604036e 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -696,7 +696,9 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (dex_files.empty()) { if (oat_file_assistant.HasOriginalDexFiles()) { if (Runtime::Current()->IsDexFileFallbackEnabled()) { - if (!DexFile::Open(dex_location, dex_location, /*out*/ &error_msg, &dex_files)) { + static constexpr bool kVerifyChecksum = true; + if (!DexFile::Open( + dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) { LOG(WARNING) << error_msg; error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) + " because: " + error_msg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index caf554550e..40e1b1363a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -883,12 +883,13 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, for (size_t i = 0; i < dex_filenames.size(); i++) { const char* dex_filename = dex_filenames[i].c_str(); const char* dex_location = dex_locations[i].c_str(); + static constexpr bool kVerifyChecksum = true; std::string error_msg; if (!OS::FileExists(dex_filename)) { LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) { + if (!DexFile::Open(dex_filename, dex_location, kVerifyChecksum, &error_msg, dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 7c50f97d39..4647d67699 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -19,6 +19,7 @@ #include "base/bit_vector.h" #include "base/bit_utils.h" +#include "dex_file.h" #include "memory_region.h" #include "leb128.h" @@ -892,7 +893,11 @@ class InlineInfoEncoding { total_bit_size_ += MinimumBitsToStore(method_index_max); dex_pc_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + // Note: We're not encoding the dex pc if there is none. That's the case + // for an intrinsified native method, such as String.charAt(). + if (dex_pc_max != DexFile::kDexNoIndex) { + total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + } invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); total_bit_size_ += MinimumBitsToStore(invoke_type_max); diff --git a/runtime/thread.h b/runtime/thread.h index 7ae9be5d1a..9b6a20ec59 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1349,8 +1349,8 @@ class Thread { instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), - last_no_thread_suspension_cause(nullptr), thread_local_objects(0), - thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), + last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr), + thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr), @@ -1465,12 +1465,12 @@ class Thread { QuickEntryPoints quick_entrypoints; // Thread-local allocation pointer. - size_t thread_local_objects; uint8_t* thread_local_start; // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; + size_t thread_local_objects; // Mterp jump table bases. void* mterp_current_ibase; diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 08b6cec35f..98a09386ee 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -30,6 +30,11 @@ class Super implements Interface { public void $noinline$f() { throw new RuntimeException(); } + + public int $inline$h(boolean cond) { + Super obj = (cond ? this : null); + return obj.hashCode(); + } } class SubclassA extends Super { @@ -620,6 +625,45 @@ public class Main { o.mainField = 0; } + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: InvokeVirtual [<<Arg>>,{{z\d+}}] method_name:Super.$inline$h + + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: <<Null:l\d+>> NullConstant + /// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA + /// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:Super.hashCode + + public void testThisArgumentMoreSpecific(boolean cond) { + // Inlining method from Super will build it with `this` typed as Super. + // Running RTP will sharpen it to SubclassA. + SubclassA obj = new SubclassA(); + ((Super) obj).$inline$h(cond); + } + + public static int $inline$hashCode(Super obj) { + return obj.hashCode(); + } + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + /// CHECK-DAG: InvokeStaticOrDirect [<<Arg>>] method_name:Main.$inline$hashCode + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + /// CHECK-DAG: <<NCArg:l\d+>> NullCheck [<<Arg>>] klass:SubclassA + /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:Super.hashCode + + public void testExplicitArgumentMoreSpecific(SubclassA obj) { + // Inlining a method will build it with reference types from its signature, + // here the callee graph is built with Super as the type of its only argument. + // Running RTP after its ParameterValue instructions are replaced with actual + // arguments will type the inner graph more precisely. + $inline$hashCode(obj); + } + /// CHECK-START: void Main.testPhiHasOnlyNullInputs(boolean) inliner (before) /// CHECK: <<Int:i\d+>> IntConstant 0 /// CHECK: <<Phi:l\d+>> Phi klass:Main exact:false diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index 24ed2feff7..26475ae55c 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -30,6 +30,18 @@ public class Main { } } + public static void assertCharEquals(char expected, char result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertStringContains(String searchTerm, String result) { + if (result == null || !result.contains(searchTerm)) { + throw new Error("Search term: " + searchTerm + ", not found in: " + result); + } + } + public static void main(String[] args) { stringEqualsSame(); stringArgumentNotNull("Foo"); @@ -41,6 +53,57 @@ public class Main { assertBooleanEquals(true, $opt$noinline$isStringEmpty("")); assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc")); assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789")); + + assertCharEquals('a', $opt$noinline$stringCharAt("a", 0)); + assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0)); + assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1)); + assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2)); + assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7)); + + try { + $opt$noinline$stringCharAt("abc", -1); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + try { + $opt$noinline$stringCharAt("abc", 3); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + try { + $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + + assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7)); + assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10)); + + assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc")); + assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3)); + try { + $opt$noinline$stringSumLeadingChars("abcdef", 7); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringSumLeadingChars", + sioob.getStackTrace()[1].toString()); + } + assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef")); + try { + $opt$noinline$stringSum4LeadingChars("abc"); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringSum4LeadingChars", + sioob.getStackTrace()[1].toString()); + } } /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before) @@ -81,6 +144,144 @@ public class Main { return s.isEmpty(); } + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: <<String:l\d+>> ParameterValue + /// CHECK-DAG: <<Pos:i\d+>> ParameterValue + /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true + /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true + /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + static public char $opt$noinline$stringCharAt(String s, int pos) { + if (doThrow) { throw new Error(); } + return s.charAt(pos); + } + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: <<String:l\d+>> ParameterValue + /// CHECK-DAG: <<Pos:i\d+>> ParameterValue + /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true + /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true + /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + static public char $opt$noinline$stringCharAtCatch(String s, int pos) { + if (doThrow) { throw new Error(); } + try { + return s.charAt(pos); + } catch (StringIndexOutOfBoundsException ignored) { + return '\0'; + } + } + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringLength + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringLength + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-NOT: ArrayLength is_string_length:true + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after) + /// CHECK-NOT: BoundsCheck + + static public int $opt$noinline$stringSumChars(String s) { + if (doThrow) { throw new Error(); } + int sum = 0; + int len = s.length(); + for (int i = 0; i < len; ++i) { + sum += s.charAt(i); + } + return sum; + } + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) + /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) + /// CHECK-NOT: BoundsCheck is_string_char_at:true + + static public int $opt$noinline$stringSumLeadingChars(String s, int n) { + if (doThrow) { throw new Error(); } + int sum = 0; + for (int i = 0; i < n; ++i) { + sum += s.charAt(i); + } + return sum; + } + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) + /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) + /// CHECK-NOT: BoundsCheck is_string_char_at:true + + static public int $opt$noinline$stringSum4LeadingChars(String s) { + if (doThrow) { throw new Error(); } + int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3); + return sum; + } + /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before) /// CHECK: InvokeStaticOrDirect diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index ee651b5494..f118a7625a 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -209,9 +209,10 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) -# Disable 137-cfi (b/27391690). +# Disable 149-suspend-all-stress, its output is flaky (b/28988206). # Disable 577-profile-foreign-dex (b/27454772). TEST_ART_BROKEN_ALL_TARGET_TESTS := \ + 149-suspend-all-stress \ 577-profile-foreign-dex \ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/dexdump/all.dex b/test/dexdump/all.dex Binary files differnew file mode 100644 index 0000000000..caf678da4a --- /dev/null +++ b/test/dexdump/all.dex diff --git a/test/dexdump/all.lst b/test/dexdump/all.lst new file mode 100644 index 0000000000..17ab9cacdd --- /dev/null +++ b/test/dexdump/all.lst @@ -0,0 +1,21 @@ +#all.dex +0x0000043c 8 A <init> ()V (none) -1 +0x00000454 58 A arrays ()V (none) -1 +0x000004a0 130 A binary_ops ()V (none) -1 +0x00000534 66 A binary_ops_2addr ()V (none) -1 +0x00000588 34 A binary_ops_lit16 ()V (none) -1 +0x000005bc 46 A binary_ops_lit8 ()V (none) -1 +0x000005fc 22 A compares ()V (none) -1 +0x00000624 50 A conditionals ()V (none) -1 +0x00000668 56 A constants ()V (none) -1 +0x000006b0 108 A misc ()V (none) -1 +0x0000072c 46 A moves ()V (none) -1 +0x0000076c 32 A packed_switch ()V (none) -1 +0x0000079c 2 A return32 ()I (none) -1 +0x000007b0 2 A return64 ()I (none) -1 +0x000007c4 2 A return_object ()Ljava/lang/Object; (none) -1 +0x000007d8 44 A sparse_switch ()V (none) -1 +0x00000814 58 A static_fields ()V (none) -1 +0x00000860 44 A unary_ops ()V (none) -1 +0x0000089c 58 A instance_fields ()V (none) -1 +0x000008e8 30 A invokes ()V (none) -1 diff --git a/test/dexdump/all.txt b/test/dexdump/all.txt new file mode 100644 index 0000000000..af4fb4c12c --- /dev/null +++ b/test/dexdump/all.txt @@ -0,0 +1,622 @@ +Processing 'all.dex'... +Opened 'all.dex', DEX version '035' +DEX file header: +magic : 'dex\n035\0' +checksum : d5134208 +signature : 7af6...100f +file_size : 2572 +header_size : 112 +link_size : 0 +link_off : 0 (0x000000) +string_ids_size : 46 +string_ids_off : 112 (0x000070) +type_ids_size : 10 +type_ids_off : 296 (0x000128) +proto_ids_size : 3 +proto_ids_off : 336 (0x000150) +field_ids_size : 14 +field_ids_off : 372 (0x000174) +method_ids_size : 21 +method_ids_off : 484 (0x0001e4) +class_defs_size : 1 +class_defs_off : 652 (0x00028c) +data_size : 1888 +data_off : 684 (0x0002ac) + +Class #0 header: +class_idx : 4 +access_flags : 1 (0x0001) +superclass_idx : 5 +interfaces_off : 0 (0x000000) +source_file_idx : -1 +annotations_off : 0 (0x000000) +class_data_off : 2310 (0x000906) +static_fields_size : 7 +instance_fields_size: 7 +direct_methods_size : 18 +virtual_methods_size: 2 + +Class #0 - + Class descriptor : 'LA;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + #0 : (in LA;) + name : 'sB' + type : 'B' + access : 0x000a (PRIVATE STATIC) + #1 : (in LA;) + name : 'sC' + type : 'C' + access : 0x000a (PRIVATE STATIC) + #2 : (in LA;) + name : 'sI' + type : 'I' + access : 0x000a (PRIVATE STATIC) + #3 : (in LA;) + name : 'sJ' + type : 'J' + access : 0x000a (PRIVATE STATIC) + #4 : (in LA;) + name : 'sO' + type : 'LA;' + access : 0x000a (PRIVATE STATIC) + #5 : (in LA;) + name : 'sS' + type : 'S' + access : 0x000a (PRIVATE STATIC) + #6 : (in LA;) + name : 'sZ' + type : 'Z' + access : 0x000a (PRIVATE STATIC) + Instance fields - + #0 : (in LA;) + name : 'mB' + type : 'B' + access : 0x0002 (PRIVATE) + #1 : (in LA;) + name : 'mC' + type : 'C' + access : 0x0002 (PRIVATE) + #2 : (in LA;) + name : 'mI' + type : 'I' + access : 0x0002 (PRIVATE) + #3 : (in LA;) + name : 'mJ' + type : 'J' + access : 0x0002 (PRIVATE) + #4 : (in LA;) + name : 'mO' + type : 'LA;' + access : 0x0002 (PRIVATE) + #5 : (in LA;) + name : 'mS' + type : 'S' + access : 0x0002 (PRIVATE) + #6 : (in LA;) + name : 'mZ' + type : 'Z' + access : 0x0002 (PRIVATE) + Direct methods - + #0 : (in LA;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +00042c: |[00042c] A.<init>:()V +00043c: 7010 1400 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0014 +000442: 0e00 |0003: return-void + catches : (none) + positions : + locals : + + #1 : (in LA;) + name : 'arrays' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 29 16-bit code units +000444: |[000444] A.arrays:()V +000454: 4400 0102 |0000: aget v0, v1, v2 +000458: 4500 0102 |0002: aget-wide v0, v1, v2 +00045c: 4600 0102 |0004: aget-object v0, v1, v2 +000460: 4700 0102 |0006: aget-boolean v0, v1, v2 +000464: 4800 0102 |0008: aget-byte v0, v1, v2 +000468: 4900 0102 |000a: aget-char v0, v1, v2 +00046c: 4a00 0102 |000c: aget-short v0, v1, v2 +000470: 4b00 0102 |000e: aput v0, v1, v2 +000474: 4c00 0102 |0010: aput-wide v0, v1, v2 +000478: 4d00 0102 |0012: aput-object v0, v1, v2 +00047c: 4e00 0102 |0014: aput-boolean v0, v1, v2 +000480: 4f00 0102 |0016: aput-byte v0, v1, v2 +000484: 5000 0102 |0018: aput-char v0, v1, v2 +000488: 5100 0102 |001a: aput-short v0, v1, v2 +00048c: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #2 : (in LA;) + name : 'binary_ops' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 65 16-bit code units +000490: |[000490] A.binary_ops:()V +0004a0: 9000 0102 |0000: add-int v0, v1, v2 +0004a4: 9100 0102 |0002: sub-int v0, v1, v2 +0004a8: 9200 0102 |0004: mul-int v0, v1, v2 +0004ac: 9300 0102 |0006: div-int v0, v1, v2 +0004b0: 9400 0102 |0008: rem-int v0, v1, v2 +0004b4: 9500 0102 |000a: and-int v0, v1, v2 +0004b8: 9600 0102 |000c: or-int v0, v1, v2 +0004bc: 9700 0102 |000e: xor-int v0, v1, v2 +0004c0: 9800 0102 |0010: shl-int v0, v1, v2 +0004c4: 9900 0102 |0012: shr-int v0, v1, v2 +0004c8: 9a00 0102 |0014: ushr-int v0, v1, v2 +0004cc: 9b00 0102 |0016: add-long v0, v1, v2 +0004d0: 9c00 0102 |0018: sub-long v0, v1, v2 +0004d4: 9d00 0102 |001a: mul-long v0, v1, v2 +0004d8: 9e00 0102 |001c: div-long v0, v1, v2 +0004dc: 9f00 0102 |001e: rem-long v0, v1, v2 +0004e0: a000 0102 |0020: and-long v0, v1, v2 +0004e4: a100 0102 |0022: or-long v0, v1, v2 +0004e8: a200 0102 |0024: xor-long v0, v1, v2 +0004ec: a300 0102 |0026: shl-long v0, v1, v2 +0004f0: a400 0102 |0028: shr-long v0, v1, v2 +0004f4: a500 0102 |002a: ushr-long v0, v1, v2 +0004f8: a600 0102 |002c: add-float v0, v1, v2 +0004fc: a700 0102 |002e: sub-float v0, v1, v2 +000500: a800 0102 |0030: mul-float v0, v1, v2 +000504: a900 0102 |0032: div-float v0, v1, v2 +000508: aa00 0102 |0034: rem-float v0, v1, v2 +00050c: ab00 0102 |0036: add-double v0, v1, v2 +000510: ac00 0102 |0038: sub-double v0, v1, v2 +000514: ad00 0102 |003a: mul-double v0, v1, v2 +000518: ae00 0102 |003c: div-double v0, v1, v2 +00051c: af00 0102 |003e: rem-double v0, v1, v2 +000520: 0e00 |0040: return-void + catches : (none) + positions : + locals : + + #3 : (in LA;) + name : 'binary_ops_2addr' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 33 16-bit code units +000524: |[000524] A.binary_ops_2addr:()V +000534: b010 |0000: add-int/2addr v0, v1 +000536: b110 |0001: sub-int/2addr v0, v1 +000538: b210 |0002: mul-int/2addr v0, v1 +00053a: b310 |0003: div-int/2addr v0, v1 +00053c: b410 |0004: rem-int/2addr v0, v1 +00053e: b510 |0005: and-int/2addr v0, v1 +000540: b610 |0006: or-int/2addr v0, v1 +000542: b710 |0007: xor-int/2addr v0, v1 +000544: b810 |0008: shl-int/2addr v0, v1 +000546: b910 |0009: shr-int/2addr v0, v1 +000548: ba10 |000a: ushr-int/2addr v0, v1 +00054a: bb10 |000b: add-long/2addr v0, v1 +00054c: bc10 |000c: sub-long/2addr v0, v1 +00054e: bd10 |000d: mul-long/2addr v0, v1 +000550: be10 |000e: div-long/2addr v0, v1 +000552: bf10 |000f: rem-long/2addr v0, v1 +000554: c010 |0010: and-long/2addr v0, v1 +000556: c110 |0011: or-long/2addr v0, v1 +000558: c210 |0012: xor-long/2addr v0, v1 +00055a: c310 |0013: shl-long/2addr v0, v1 +00055c: c410 |0014: shr-long/2addr v0, v1 +00055e: c510 |0015: ushr-long/2addr v0, v1 +000560: c610 |0016: add-float/2addr v0, v1 +000562: c710 |0017: sub-float/2addr v0, v1 +000564: c810 |0018: mul-float/2addr v0, v1 +000566: c910 |0019: div-float/2addr v0, v1 +000568: ca10 |001a: rem-float/2addr v0, v1 +00056a: cb10 |001b: add-double/2addr v0, v1 +00056c: cc10 |001c: sub-double/2addr v0, v1 +00056e: cd10 |001d: mul-double/2addr v0, v1 +000570: ce10 |001e: div-double/2addr v0, v1 +000572: cf10 |001f: rem-double/2addr v0, v1 +000574: 0e00 |0020: return-void + catches : (none) + positions : + locals : + + #4 : (in LA;) + name : 'binary_ops_lit16' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 17 16-bit code units +000578: |[000578] A.binary_ops_lit16:()V +000588: d010 3412 |0000: add-int/lit16 v0, v1, #int 4660 // #1234 +00058c: d110 3412 |0002: rsub-int v0, v1, #int 4660 // #1234 +000590: d210 3412 |0004: mul-int/lit16 v0, v1, #int 4660 // #1234 +000594: d310 3412 |0006: div-int/lit16 v0, v1, #int 4660 // #1234 +000598: d410 3412 |0008: rem-int/lit16 v0, v1, #int 4660 // #1234 +00059c: d510 3412 |000a: and-int/lit16 v0, v1, #int 4660 // #1234 +0005a0: d610 3412 |000c: or-int/lit16 v0, v1, #int 4660 // #1234 +0005a4: d710 3412 |000e: xor-int/lit16 v0, v1, #int 4660 // #1234 +0005a8: 0e00 |0010: return-void + catches : (none) + positions : + locals : + + #5 : (in LA;) + name : 'binary_ops_lit8' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 23 16-bit code units +0005ac: |[0005ac] A.binary_ops_lit8:()V +0005bc: d800 0112 |0000: add-int/lit8 v0, v1, #int 18 // #12 +0005c0: d900 0112 |0002: rsub-int/lit8 v0, v1, #int 18 // #12 +0005c4: da00 0112 |0004: mul-int/lit8 v0, v1, #int 18 // #12 +0005c8: db00 0112 |0006: div-int/lit8 v0, v1, #int 18 // #12 +0005cc: dc00 0112 |0008: rem-int/lit8 v0, v1, #int 18 // #12 +0005d0: dd00 0112 |000a: and-int/lit8 v0, v1, #int 18 // #12 +0005d4: de00 0112 |000c: or-int/lit8 v0, v1, #int 18 // #12 +0005d8: df00 0112 |000e: xor-int/lit8 v0, v1, #int 18 // #12 +0005dc: e000 0112 |0010: shl-int/lit8 v0, v1, #int 18 // #12 +0005e0: e100 0112 |0012: shr-int/lit8 v0, v1, #int 18 // #12 +0005e4: e200 0112 |0014: ushr-int/lit8 v0, v1, #int 18 // #12 +0005e8: 0e00 |0016: return-void + catches : (none) + positions : + locals : + + #6 : (in LA;) + name : 'compares' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 11 16-bit code units +0005ec: |[0005ec] A.compares:()V +0005fc: 2d00 0102 |0000: cmpl-float v0, v1, v2 +000600: 2e00 0102 |0002: cmpg-float v0, v1, v2 +000604: 2f00 0102 |0004: cmpl-double v0, v1, v2 +000608: 3000 0102 |0006: cmpg-double v0, v1, v2 +00060c: 3100 0102 |0008: cmp-long v0, v1, v2 +000610: 0e00 |000a: return-void + catches : (none) + positions : + locals : + + #7 : (in LA;) + name : 'conditionals' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 25 16-bit code units +000614: |[000614] A.conditionals:()V +000624: 3210 1800 |0000: if-eq v0, v1, 0018 // +0018 +000628: 3310 1600 |0002: if-ne v0, v1, 0018 // +0016 +00062c: 3410 1400 |0004: if-lt v0, v1, 0018 // +0014 +000630: 3510 1200 |0006: if-ge v0, v1, 0018 // +0012 +000634: 3610 1000 |0008: if-gt v0, v1, 0018 // +0010 +000638: 3710 0e00 |000a: if-le v0, v1, 0018 // +000e +00063c: 3800 0c00 |000c: if-eqz v0, 0018 // +000c +000640: 3900 0a00 |000e: if-nez v0, 0018 // +000a +000644: 3a00 0800 |0010: if-ltz v0, 0018 // +0008 +000648: 3b00 0600 |0012: if-gez v0, 0018 // +0006 +00064c: 3c00 0400 |0014: if-gtz v0, 0018 // +0004 +000650: 3d00 0200 |0016: if-lez v0, 0018 // +0002 +000654: 0e00 |0018: return-void + catches : (none) + positions : + locals : + + #8 : (in LA;) + name : 'constants' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 28 16-bit code units +000658: |[000658] A.constants:()V +000668: 1210 |0000: const/4 v0, #int 1 // #1 +00066a: 1300 3412 |0001: const/16 v0, #int 4660 // #1234 +00066e: 1400 7856 3412 |0003: const v0, #float 5.69046e-28 // #12345678 +000674: 1500 3412 |0006: const/high16 v0, #int 305397760 // #1234 +000678: 1600 3412 |0008: const-wide/16 v0, #int 4660 // #1234 +00067c: 1700 7856 3412 |000a: const-wide/32 v0, #float 5.69046e-28 // #12345678 +000682: 1800 efcd ab90 7856 3412 |000d: const-wide v0, #double 5.62635e-221 // #1234567890abcdef +00068c: 1900 3412 |0012: const-wide/high16 v0, #long 1311673391471656960 // #1234 +000690: 1a00 2c00 |0014: const-string v0, "string" // string@002c +000694: 1b00 2c00 0000 |0016: const-string/jumbo v0, "string" // string@0000002c +00069a: 1c00 0500 |0019: const-class v0, Ljava/lang/Object; // type@0005 +00069e: 0e00 |001b: return-void + catches : (none) + positions : + locals : + + #9 : (in LA;) + name : 'misc' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 5 + ins : 0 + outs : 0 + insns size : 54 16-bit code units +0006a0: |[0006a0] A.misc:()V +0006b0: 0000 |0000: nop // spacer +0006b2: 1d00 |0001: monitor-enter v0 +0006b4: 1e00 |0002: monitor-exit v0 +0006b6: 1f00 0500 |0003: check-cast v0, Ljava/lang/Object; // type@0005 +0006ba: 2010 0500 |0005: instance-of v0, v1, Ljava/lang/Object; // type@0005 +0006be: 2110 |0007: array-length v0, v1 +0006c0: 2200 0500 |0008: new-instance v0, Ljava/lang/Object; // type@0005 +0006c4: 2310 0500 |000a: new-array v0, v1, Ljava/lang/Object; // type@0005 +0006c8: 2454 0900 1032 |000c: filled-new-array {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009 +0006ce: 2505 0900 0000 |000f: filled-new-array/range {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009 +0006d4: 2600 0c00 0000 |0012: fill-array-data v0, 0000001e // +0000000c +0006da: 2700 |0015: throw v0 +0006dc: 2806 |0016: goto 001c // +0006 +0006de: 2900 0500 |0017: goto/16 001c // +0005 +0006e2: 2a00 0300 0000 |0019: goto/32 #00000003 +0006e8: 0e00 |001c: return-void +0006ea: 0000 |001d: nop // spacer +0006ec: 0003 0400 0a00 0000 0100 0000 0200 ... |001e: array-data (24 units) + catches : (none) + positions : + locals : + + #10 : (in LA;) + name : 'moves' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 23 16-bit code units +00071c: |[00071c] A.moves:()V +00072c: 0110 |0000: move v0, v1 +00072e: 0200 0100 |0001: move/from16 v0, v1 +000732: 0300 0000 0100 |0003: move/16 v0, v1 +000738: 0410 |0006: move-wide v0, v1 +00073a: 0500 0100 |0007: move-wide/from16 v0, v1 +00073e: 0600 0000 0100 |0009: move-wide/16 v0, v1 +000744: 0710 |000c: move-object v0, v1 +000746: 0800 0100 |000d: move-object/from16 v0, v1 +00074a: 0900 0000 0100 |000f: move-object/16 v0, v1 +000750: 0a00 |0012: move-result v0 +000752: 0b00 |0013: move-result-wide v0 +000754: 0c00 |0014: move-result-object v0 +000756: 0d00 |0015: move-exception v0 +000758: 0e00 |0016: return-void + catches : (none) + positions : + locals : + + #11 : (in LA;) + name : 'packed_switch' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 16 16-bit code units +00075c: |[00075c] A.packed_switch:()V +00076c: 2b00 0800 0000 |0000: packed-switch v0, 00000008 // +00000008 +000772: 0e00 |0003: return-void +000774: 28ff |0004: goto 0003 // -0001 +000776: 28fe |0005: goto 0003 // -0002 +000778: 28fd |0006: goto 0003 // -0003 +00077a: 0000 |0007: nop // spacer +00077c: 0001 0200 feff ff7f 0500 0000 0600 ... |0008: packed-switch-data (8 units) + catches : (none) + positions : + locals : + + #12 : (in LA;) + name : 'return32' + type : '()I' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +00078c: |[00078c] A.return32:()I +00079c: 0f00 |0000: return v0 + catches : (none) + positions : + locals : + + #13 : (in LA;) + name : 'return64' + type : '()I' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +0007a0: |[0007a0] A.return64:()I +0007b0: 1000 |0000: return-wide v0 + catches : (none) + positions : + locals : + + #14 : (in LA;) + name : 'return_object' + type : '()Ljava/lang/Object;' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +0007b4: |[0007b4] A.return_object:()Ljava/lang/Object; +0007c4: 1100 |0000: return-object v0 + catches : (none) + positions : + locals : + + #15 : (in LA;) + name : 'sparse_switch' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 22 16-bit code units +0007c8: |[0007c8] A.sparse_switch:()V +0007d8: 2c00 0400 0000 |0000: sparse-switch v0, 00000004 // +00000004 +0007de: 0e00 |0003: return-void +0007e0: 0002 0400 1111 0000 2222 0000 3333 ... |0004: sparse-switch-data (18 units) + catches : (none) + positions : + locals : + + #16 : (in LA;) + name : 'static_fields' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 29 16-bit code units +000804: |[000804] A.static_fields:()V +000814: 6000 0900 |0000: sget v0, LA;.sI:I // field@0009 +000818: 6100 0a00 |0002: sget-wide v0, LA;.sJ:J // field@000a +00081c: 6200 0b00 |0004: sget-object v0, LA;.sO:LA; // field@000b +000820: 6300 0d00 |0006: sget-boolean v0, LA;.sZ:Z // field@000d +000824: 6400 0700 |0008: sget-byte v0, LA;.sB:B // field@0007 +000828: 6500 0800 |000a: sget-char v0, LA;.sC:C // field@0008 +00082c: 6600 0c00 |000c: sget-short v0, LA;.sS:S // field@000c +000830: 6700 0900 |000e: sput v0, LA;.sI:I // field@0009 +000834: 6800 0a00 |0010: sput-wide v0, LA;.sJ:J // field@000a +000838: 6900 0b00 |0012: sput-object v0, LA;.sO:LA; // field@000b +00083c: 6a00 0d00 |0014: sput-boolean v0, LA;.sZ:Z // field@000d +000840: 6b00 0700 |0016: sput-byte v0, LA;.sB:B // field@0007 +000844: 6c00 0800 |0018: sput-char v0, LA;.sC:C // field@0008 +000848: 6d00 0500 |001a: sput-short v0, LA;.mS:S // field@0005 +00084c: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #17 : (in LA;) + name : 'unary_ops' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 22 16-bit code units +000850: |[000850] A.unary_ops:()V +000860: 7b10 |0000: neg-int v0, v1 +000862: 7c10 |0001: not-int v0, v1 +000864: 7d10 |0002: neg-long v0, v1 +000866: 7e10 |0003: not-long v0, v1 +000868: 7f10 |0004: neg-float v0, v1 +00086a: 8010 |0005: neg-double v0, v1 +00086c: 8110 |0006: int-to-long v0, v1 +00086e: 8210 |0007: int-to-float v0, v1 +000870: 8310 |0008: int-to-double v0, v1 +000872: 8410 |0009: long-to-int v0, v1 +000874: 8510 |000a: long-to-float v0, v1 +000876: 8610 |000b: long-to-double v0, v1 +000878: 8710 |000c: float-to-int v0, v1 +00087a: 8810 |000d: float-to-long v0, v1 +00087c: 8910 |000e: float-to-double v0, v1 +00087e: 8a10 |000f: double-to-int v0, v1 +000880: 8b10 |0010: double-to-long v0, v1 +000882: 8c10 |0011: double-to-float v0, v1 +000884: 8d10 |0012: int-to-byte v0, v1 +000886: 8e10 |0013: int-to-char v0, v1 +000888: 8f10 |0014: int-to-short v0, v1 +00088a: 0e00 |0015: return-void + catches : (none) + positions : + locals : + + Virtual methods - + #0 : (in LA;) + name : 'instance_fields' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 2 + ins : 1 + outs : 0 + insns size : 29 16-bit code units +00088c: |[00088c] A.instance_fields:()V +00089c: 5210 0900 |0000: iget v0, v1, LA;.sI:I // field@0009 +0008a0: 5310 0a00 |0002: iget-wide v0, v1, LA;.sJ:J // field@000a +0008a4: 5410 0b00 |0004: iget-object v0, v1, LA;.sO:LA; // field@000b +0008a8: 5510 0d00 |0006: iget-boolean v0, v1, LA;.sZ:Z // field@000d +0008ac: 5610 0700 |0008: iget-byte v0, v1, LA;.sB:B // field@0007 +0008b0: 5710 0800 |000a: iget-char v0, v1, LA;.sC:C // field@0008 +0008b4: 5810 0c00 |000c: iget-short v0, v1, LA;.sS:S // field@000c +0008b8: 5910 0900 |000e: iput v0, v1, LA;.sI:I // field@0009 +0008bc: 5a10 0a00 |0010: iput-wide v0, v1, LA;.sJ:J // field@000a +0008c0: 5b10 0b00 |0012: iput-object v0, v1, LA;.sO:LA; // field@000b +0008c4: 5c10 0d00 |0014: iput-boolean v0, v1, LA;.sZ:Z // field@000d +0008c8: 5d10 0700 |0016: iput-byte v0, v1, LA;.sB:B // field@0007 +0008cc: 5e10 0800 |0018: iput-char v0, v1, LA;.sC:C // field@0008 +0008d0: 5f10 0c00 |001a: iput-short v0, v1, LA;.sS:S // field@000c +0008d4: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #1 : (in LA;) + name : 'invokes' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 5 + ins : 1 + outs : 1 + insns size : 15 16-bit code units +0008d8: |[0008d8] A.invokes:()V +0008e8: 6e54 0a00 1032 |0000: invoke-virtual {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008ee: 6f54 0a00 1032 |0003: invoke-super {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008f4: 7054 0a00 1032 |0006: invoke-direct {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008fa: 7154 0a00 1032 |0009: invoke-static {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +000900: 7254 0a00 1032 |000c: invoke-interface {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a + catches : (none) + positions : + locals : + + source_file_idx : -1 (unknown) + diff --git a/test/dexdump/all.xml b/test/dexdump/all.xml new file mode 100644 index 0000000000..b623ecb2c1 --- /dev/null +++ b/test/dexdump/all.xml @@ -0,0 +1,211 @@ +<api> +<package name="" +> +<class name="A" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<constructor name="A" + type="A" + static="false" + final="false" + visibility="public" +> +</constructor> +<method name="arrays" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_2addr" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_lit16" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_lit8" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="compares" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="conditionals" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="constants" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="misc" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="moves" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="packed_switch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return32" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return64" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return_object" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="sparse_switch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="static_fields" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="unary_ops" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="instance_fields" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + visibility="public" +> +</method> +<method name="invokes" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + visibility="public" +> +</method> +</class> +</package> +</api> |