Merge "Revert "Add verification of knownfailures in the testrunner""
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index d09f290..f924a85 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -21,7 +21,7 @@
ART_CPPLINT_FLAGS := --quiet --root=$(ANDROID_BUILD_TOP)
ART_CPPLINT_INGORED := \
runtime/elf.h \
- runtime/openjdkjvmti/jvmti.h
+ runtime/openjdkjvmti/include/jvmti.h
# This:
# 1) Gets a list of all .h & .cc files in the art directory.
diff --git a/build/art.go b/build/art.go
index 053968d..e7f7e21 100644
--- a/build/art.go
+++ b/build/art.go
@@ -70,10 +70,10 @@
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
}
- if envTrue(ctx, "ART_USE_OLD_ARM_BACKEND") {
- // Used to enable the old, pre-VIXL ARM code generator.
- cflags = append(cflags, "-DART_USE_OLD_ARM_BACKEND=1")
- asflags = append(asflags, "-DART_USE_OLD_ARM_BACKEND=1")
+ if envTrue(ctx, "ART_USE_VIXL_ARM_BACKEND") {
+ // Used to enable the new VIXL-based ARM code generator.
+ cflags = append(cflags, "-DART_USE_VIXL_ARM_BACKEND=1")
+ asflags = append(asflags, "-DART_USE_VIXL_ARM_BACKEND=1")
}
return cflags, asflags
diff --git a/compiler/Android.bp b/compiler/Android.bp
index c59e36b..d57f301 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -351,6 +351,7 @@
"optimizing/pretty_printer_test.cc",
"optimizing/reference_type_propagation_test.cc",
"optimizing/side_effects_test.cc",
+ "optimizing/ssa_liveness_analysis_test.cc",
"optimizing/ssa_test.cc",
"optimizing/stack_map_test.cc",
"optimizing/suspend_check_test.cc",
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index 5360103..85d5784 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -185,7 +185,7 @@
}
if (quickened_info_ptr_ != quickened_info_end_) {
- LOG(ERROR) << "Failed to use all values in quickening info."
+ LOG(FATAL) << "Failed to use all values in quickening info."
<< " Actual: " << std::hex << quickened_info_ptr_
<< " Expected: " << quickened_info_end_;
return false;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 057e3c9..9950987 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -480,7 +480,9 @@
DCHECK(!it.HasNext());
}
}
- DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info";
+ if (quickening_info_ptr != quickening_info_end) {
+ LOG(FATAL) << "Failed to use all quickening info";
+ }
}
void CompilerDriver::CompileAll(jobject class_loader,
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1e5c43d..cbde587 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -355,6 +355,10 @@
return current_dex_to_dex_methods_;
}
+ const ProfileCompilationInfo* GetProfileCompilationInfo() const {
+ return profile_compilation_info_;
+ }
+
private:
// Can `referrer_class` access the resolved `member`?
// Dispatch call to mirror::Class::CanAccessResolvedField or
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 562f97b..35aa1ee 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -133,9 +133,10 @@
<< " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) << " "
<< dex.GetMethodName(dex.GetMethodId(i));
}
- EXPECT_EQ(dex.NumFieldIds(), dex_cache->NumResolvedFields());
+ EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
+ || dex.NumFieldIds() == dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
- ArtField* field = cl->GetResolvedField(i, dex_cache);
+ ArtField* field = dex_cache->GetResolvedField(i, cl->GetImagePointerSize());
EXPECT_TRUE(field != nullptr) << "field_idx=" << i
<< " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
<< " " << dex.GetFieldName(dex.GetFieldId(i));
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 65d82ed..aa73456 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -968,11 +968,12 @@
<< Class::PrettyClass(declaring_class) << " not in class linker table";
}
}
- ArtField** resolved_fields = dex_cache->GetResolvedFields();
+ mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
- ArtField* field = mirror::DexCache::GetElementPtrSize(resolved_fields, i, target_ptr_size_);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, i, target_ptr_size_);
+ ArtField* field = pair.object;
if (field != nullptr && !KeepClass(field->GetDeclaringClass().Ptr())) {
- dex_cache->SetResolvedField(i, nullptr, target_ptr_size_);
+ dex_cache->ClearResolvedField(pair.index, target_ptr_size_);
}
}
// Clean the dex field. It might have been populated during the initialization phase, but
@@ -1577,10 +1578,8 @@
}
// Calculate the size of the class table.
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
- DCHECK_EQ(image_info.class_table_->NumZygoteClasses(class_loader), 0u);
- if (image_info.class_table_->NumNonZygoteClasses(class_loader) != 0u) {
+ DCHECK_EQ(image_info.class_table_->NumReferencedZygoteClasses(), 0u);
+ if (image_info.class_table_->NumReferencedNonZygoteClasses() != 0u) {
image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr);
}
}
@@ -1596,7 +1595,7 @@
break;
}
case kBinDexCacheArray:
- bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment());
+ bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment(target_ptr_size_));
break;
case kBinImTable:
case kBinIMTConflictTable: {
@@ -1925,9 +1924,8 @@
// above comment for intern tables.
ClassTable temp_class_table;
temp_class_table.ReadFromMemory(class_table_memory_ptr);
- ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
- CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
- table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
+ CHECK_EQ(temp_class_table.NumReferencedZygoteClasses(),
+ table->NumReferencedNonZygoteClasses() + table->NumReferencedZygoteClasses());
UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown));
temp_class_table.VisitRoots(visitor);
}
@@ -2236,16 +2234,17 @@
mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
}
}
- ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
+ mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields();
if (orig_fields != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
NativeLocationInImage(orig_fields),
PointerSize::k64);
- ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
+ mirror::FieldDexCacheType* copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
- ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
- ArtField* copy = NativeLocationInImage(orig);
- mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
+ mirror::FieldDexCachePair orig =
+ mirror::DexCache::GetNativePairPtrSize(orig_fields, i, target_ptr_size_);
+ mirror::FieldDexCachePair copy(NativeLocationInImage(orig.object), orig.index);
+ mirror::DexCache::SetNativePairPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
mirror::MethodTypeDexCacheType* orig_method_types = orig_dex_cache->GetResolvedMethodTypes();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 43f606a..afcdf5e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -2270,28 +2270,11 @@
/* verify */ true,
/* verify_checksum */ true,
&error_msg);
- } else if (oat_dex_file->source_.IsRawFile()) {
+ } else {
+ CHECK(oat_dex_file->source_.IsRawFile())
+ << static_cast<size_t>(oat_dex_file->source_.GetType());
File* raw_file = oat_dex_file->source_.GetRawFile();
dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg);
- } else {
- // The source data is a vdex file.
- CHECK(oat_dex_file->source_.IsRawData())
- << static_cast<size_t>(oat_dex_file->source_.GetType());
- const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData();
- // Note: The raw data has already been checked to contain the header
- // and all the data that the header specifies as the file size.
- DCHECK(raw_dex_file != nullptr);
- DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation()));
- const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
- // Since the source may have had its layout changed, don't verify the checksum.
- dex_file = DexFile::Open(raw_dex_file,
- header->file_size_,
- location,
- oat_dex_file->dex_file_location_checksum_,
- nullptr,
- /* verify */ true,
- /* verify_checksum */ false,
- &error_msg);
}
if (dex_file == nullptr) {
LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index bab626f..e34f116 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5304,18 +5304,29 @@
return true;
}
Opcode neg_opcode = kNoOperand;
+ uint32_t neg_value = 0;
switch (opcode) {
- case AND: neg_opcode = BIC; value = ~value; break;
- case ORR: neg_opcode = ORN; value = ~value; break;
- case ADD: neg_opcode = SUB; value = -value; break;
- case ADC: neg_opcode = SBC; value = ~value; break;
- case SUB: neg_opcode = ADD; value = -value; break;
- case SBC: neg_opcode = ADC; value = ~value; break;
- case MOV: neg_opcode = MVN; value = ~value; break;
+ case AND: neg_opcode = BIC; neg_value = ~value; break;
+ case ORR: neg_opcode = ORN; neg_value = ~value; break;
+ case ADD: neg_opcode = SUB; neg_value = -value; break;
+ case ADC: neg_opcode = SBC; neg_value = ~value; break;
+ case SUB: neg_opcode = ADD; neg_value = -value; break;
+ case SBC: neg_opcode = ADC; neg_value = ~value; break;
+ case MOV: neg_opcode = MVN; neg_value = ~value; break;
default:
return false;
}
- return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, value, set_cc, &so);
+
+ if (assembler->ShifterOperandCanHold(kNoRegister,
+ kNoRegister,
+ neg_opcode,
+ neg_value,
+ set_cc,
+ &so)) {
+ return true;
+ }
+
+ return opcode == AND && IsPowerOfTwo(value + 1);
}
void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
@@ -6217,21 +6228,59 @@
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+
+ HInstruction* index = instruction->InputAt(0);
+ HInstruction* length = instruction->InputAt(1);
+ // If both index and length are constants we can statically check the bounds. But if at least one
+ // of them is not encodable ArmEncodableConstantOrRegister will create
+ // Location::RequiresRegister() which is not desired to happen. Instead we create constant
+ // locations.
+ bool both_const = index->IsConstant() && length->IsConstant();
+ locations->SetInAt(0, both_const
+ ? Location::ConstantLocation(index->AsConstant())
+ : ArmEncodableConstantOrRegister(index, CMP));
+ locations->SetInAt(1, both_const
+ ? Location::ConstantLocation(length->AsConstant())
+ : ArmEncodableConstantOrRegister(length, CMP));
}
void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
- SlowPathCodeARM* slow_path =
- new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
- codegen_->AddSlowPath(slow_path);
+ Location index_loc = locations->InAt(0);
+ Location length_loc = locations->InAt(1);
- Register index = locations->InAt(0).AsRegister<Register>();
- Register length = locations->InAt(1).AsRegister<Register>();
+ if (length_loc.IsConstant()) {
+ int32_t length = helpers::Int32ConstantFrom(length_loc);
+ if (index_loc.IsConstant()) {
+ // BCE will remove the bounds check if we are guaranteed to pass.
+ int32_t index = helpers::Int32ConstantFrom(index_loc);
+ if (index < 0 || index >= length) {
+ SlowPathCodeARM* slow_path =
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
+ codegen_->AddSlowPath(slow_path);
+ __ b(slow_path->GetEntryLabel());
+ } else {
+ // Some optimization after BCE may have generated this, and we should not
+ // generate a bounds check if it is a valid range.
+ }
+ return;
+ }
- __ cmp(index, ShifterOperand(length));
- __ b(slow_path->GetEntryLabel(), HS);
+ SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
+ __ cmp(index_loc.AsRegister<Register>(), ShifterOperand(length));
+ codegen_->AddSlowPath(slow_path);
+ __ b(slow_path->GetEntryLabel(), HS);
+ } else {
+ SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
+ if (index_loc.IsConstant()) {
+ int32_t index = helpers::Int32ConstantFrom(index_loc);
+ __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index));
+ } else {
+ __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index_loc.AsRegister<Register>()));
+ }
+ codegen_->AddSlowPath(slow_path);
+ __ b(slow_path->GetEntryLabel(), LS);
+ }
}
void CodeGeneratorARM::MarkGCCard(Register temp,
@@ -7571,9 +7620,11 @@
ShifterOperand so;
if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) {
__ and_(out, first, so);
- } else {
- DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so));
+ } else if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)) {
__ bic(out, first, ShifterOperand(~value));
+ } else {
+ DCHECK(IsPowerOfTwo(value + 1));
+ __ ubfx(out, first, 0, WhichPowerOf2(value + 1));
}
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 97b61ed..28cc942 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2395,7 +2395,7 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
default:
@@ -2565,7 +2565,7 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->GetOffset(), instruction));
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
void InstructionCodeGeneratorARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index a1f30cd..f5ada52 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -5315,18 +5315,24 @@
return true;
}
Opcode neg_opcode = kNoOperand;
+ uint32_t neg_value = 0;
switch (opcode) {
- case AND: neg_opcode = BIC; value = ~value; break;
- case ORR: neg_opcode = ORN; value = ~value; break;
- case ADD: neg_opcode = SUB; value = -value; break;
- case ADC: neg_opcode = SBC; value = ~value; break;
- case SUB: neg_opcode = ADD; value = -value; break;
- case SBC: neg_opcode = ADC; value = ~value; break;
- case MOV: neg_opcode = MVN; value = ~value; break;
+ case AND: neg_opcode = BIC; neg_value = ~value; break;
+ case ORR: neg_opcode = ORN; neg_value = ~value; break;
+ case ADD: neg_opcode = SUB; neg_value = -value; break;
+ case ADC: neg_opcode = SBC; neg_value = ~value; break;
+ case SUB: neg_opcode = ADD; neg_value = -value; break;
+ case SBC: neg_opcode = ADC; neg_value = ~value; break;
+ case MOV: neg_opcode = MVN; neg_value = ~value; break;
default:
return false;
}
- return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
+
+ if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, set_cc)) {
+ return true;
+ }
+
+ return opcode == AND && IsPowerOfTwo(value + 1);
}
void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
@@ -6264,20 +6270,56 @@
caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+
+ HInstruction* index = instruction->InputAt(0);
+ HInstruction* length = instruction->InputAt(1);
+ // If both index and length are constants we can statically check the bounds. But if at least one
+ // of them is not encodable ArmEncodableConstantOrRegister will create
+ // Location::RequiresRegister() which is not desired to happen. Instead we create constant
+ // locations.
+ bool both_const = index->IsConstant() && length->IsConstant();
+ locations->SetInAt(0, both_const
+ ? Location::ConstantLocation(index->AsConstant())
+ : ArmEncodableConstantOrRegister(index, CMP));
+ locations->SetInAt(1, both_const
+ ? Location::ConstantLocation(length->AsConstant())
+ : ArmEncodableConstantOrRegister(length, CMP));
}
void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
- SlowPathCodeARMVIXL* slow_path =
- new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
- codegen_->AddSlowPath(slow_path);
+ LocationSummary* locations = instruction->GetLocations();
+ Location index_loc = locations->InAt(0);
+ Location length_loc = locations->InAt(1);
- vixl32::Register index = InputRegisterAt(instruction, 0);
- vixl32::Register length = InputRegisterAt(instruction, 1);
+ if (length_loc.IsConstant()) {
+ int32_t length = Int32ConstantFrom(length_loc);
+ if (index_loc.IsConstant()) {
+ // BCE will remove the bounds check if we are guaranteed to pass.
+ int32_t index = Int32ConstantFrom(index_loc);
+ if (index < 0 || index >= length) {
+ SlowPathCodeARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
+ codegen_->AddSlowPath(slow_path);
+ __ B(slow_path->GetEntryLabel());
+ } else {
+ // Some optimization after BCE may have generated this, and we should not
+ // generate a bounds check if it is a valid range.
+ }
+ return;
+ }
- __ Cmp(index, length);
- __ B(hs, slow_path->GetEntryLabel());
+ SlowPathCodeARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
+ __ Cmp(RegisterFrom(index_loc), length);
+ codegen_->AddSlowPath(slow_path);
+ __ B(hs, slow_path->GetEntryLabel());
+ } else {
+ SlowPathCodeARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
+ __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
+ codegen_->AddSlowPath(slow_path);
+ __ B(ls, slow_path->GetEntryLabel());
+ }
}
void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
@@ -7631,10 +7673,12 @@
return;
}
if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
- __ And(out, first, value);
+ __ And(out, first, value);
+ } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
+ __ Bic(out, first, ~value);
} else {
- DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
- __ Bic(out, first, ~value);
+ DCHECK(IsPowerOfTwo(value + 1));
+ __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
}
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 781027a..ef01a47 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -35,11 +35,11 @@
#include "aarch32/macro-assembler-aarch32.h"
#pragma GCC diagnostic pop
-// Default to use the VIXL-based backend on ARM.
-#ifdef ART_USE_OLD_ARM_BACKEND
-static constexpr bool kArmUseVIXL32 = false;
-#else
+// True if VIXL32 should be used for codegen on ARM.
+#ifdef ART_USE_VIXL_ARM_BACKEND
static constexpr bool kArmUseVIXL32 = true;
+#else
+static constexpr bool kArmUseVIXL32 = false;
#endif
namespace art {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 02c3ad6..4814b22 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1681,6 +1681,25 @@
}
}
+Location LocationsBuilderMIPS64::RegisterOrZeroConstant(HInstruction* instruction) {
+ return (instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern())
+ ? Location::ConstantLocation(instruction->AsConstant())
+ : Location::RequiresRegister();
+}
+
+Location LocationsBuilderMIPS64::FpuRegisterOrConstantForStore(HInstruction* instruction) {
+ // We can store 0.0 directly (from the ZERO register) without loading it into an FPU register.
+ // We can store a non-zero float or double constant without first loading it into the FPU,
+ // but we should only prefer this if the constant has a single use.
+ if (instruction->IsConstant() &&
+ (instruction->AsConstant()->IsZeroBitPattern() ||
+ instruction->GetUses().HasExactlyOneElement())) {
+ return Location::ConstantLocation(instruction->AsConstant());
+ // Otherwise fall through and require an FPU register for the constant.
+ }
+ return Location::RequiresFpuRegister();
+}
+
void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) {
bool needs_runtime_call = instruction->NeedsTypeCheck();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
@@ -1695,9 +1714,9 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
+ locations->SetInAt(2, FpuRegisterOrConstantForStore(instruction->InputAt(2)));
} else {
- locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(2, RegisterOrZeroConstant(instruction->InputAt(2)));
}
}
}
@@ -1706,24 +1725,29 @@
LocationSummary* locations = instruction->GetLocations();
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
Location index = locations->InAt(1);
+ Location value_location = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
bool needs_runtime_call = locations->WillCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
auto null_checker = GetImplicitNullChecker(instruction, codegen_);
+ GpuRegister base_reg = index.IsConstant() ? obj : TMP;
switch (value_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
- GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>();
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
- __ StoreToOffset(kStoreByte, value, obj, offset, null_checker);
+ data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1;
} else {
- __ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
- __ StoreToOffset(kStoreByte, value, TMP, data_offset, null_checker);
+ __ Daddu(base_reg, obj, index.AsRegister<GpuRegister>());
+ }
+ if (value_location.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreByte, value, base_reg, data_offset, TMP, null_checker);
+ } else {
+ GpuRegister value = value_location.AsRegister<GpuRegister>();
+ __ StoreToOffset(kStoreByte, value, base_reg, data_offset, null_checker);
}
break;
}
@@ -1731,15 +1755,18 @@
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
- GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>();
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- __ StoreToOffset(kStoreHalfword, value, obj, offset, null_checker);
+ data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
} else {
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
- __ Daddu(TMP, obj, TMP);
- __ StoreToOffset(kStoreHalfword, value, TMP, data_offset, null_checker);
+ __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_2);
+ __ Daddu(base_reg, obj, base_reg);
+ }
+ if (value_location.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreHalfword, value, base_reg, data_offset, TMP, null_checker);
+ } else {
+ GpuRegister value = value_location.AsRegister<GpuRegister>();
+ __ StoreToOffset(kStoreHalfword, value, base_reg, data_offset, null_checker);
}
break;
}
@@ -1748,54 +1775,57 @@
case Primitive::kPrimNot: {
if (!needs_runtime_call) {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- GpuRegister base_reg;
- GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>();
if (index.IsConstant()) {
data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
- base_reg = obj;
} else {
DCHECK(index.IsRegister()) << index;
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
- __ Daddu(TMP, obj, TMP);
- base_reg = TMP;
+ __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
+ __ Daddu(base_reg, obj, base_reg);
}
- if (kPoisonHeapReferences && needs_write_barrier) {
- // Note that in the case where `value` is a null reference,
- // we do not enter this block, as a null reference does not
- // need poisoning.
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- // Use Sw() instead of StoreToOffset() in order to be able to
- // hold the poisoned reference in AT and thus avoid allocating
- // yet another temporary register.
- if (index.IsConstant()) {
- if (!IsInt<16>(static_cast<int32_t>(data_offset))) {
- int16_t low16 = Low16Bits(data_offset);
- // For consistency with StoreToOffset() and such treat data_offset as int32_t.
- uint64_t high48 = static_cast<uint64_t>(static_cast<int32_t>(data_offset)) - low16;
- int16_t upper16 = High16Bits(high48);
- // Allow the full [-2GB,+2GB) range in case `low16` is negative and needs a
- // compensatory 64KB added, which may push `high48` above 2GB and require
- // the dahi instruction.
- int16_t higher16 = High32Bits(high48) + ((upper16 < 0) ? 1 : 0);
- __ Daui(TMP, obj, upper16);
- if (higher16 != 0) {
- __ Dahi(TMP, higher16);
- }
- base_reg = TMP;
- data_offset = low16;
- }
- } else {
- DCHECK(IsInt<16>(static_cast<int32_t>(data_offset)));
- }
- __ PoisonHeapReference(AT, value);
- __ Sw(AT, base_reg, data_offset);
- null_checker();
+ if (value_location.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
+ DCHECK(!needs_write_barrier);
} else {
- __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
- }
- if (needs_write_barrier) {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
+ GpuRegister value = value_location.AsRegister<GpuRegister>();
+ if (kPoisonHeapReferences && needs_write_barrier) {
+ // Note that in the case where `value` is a null reference,
+ // we do not enter this block, as a null reference does not
+ // need poisoning.
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ // Use Sw() instead of StoreToOffset() in order to be able to
+ // hold the poisoned reference in AT and thus avoid allocating
+ // yet another temporary register.
+ if (index.IsConstant()) {
+ if (!IsInt<16>(static_cast<int32_t>(data_offset))) {
+ int16_t low16 = Low16Bits(data_offset);
+ // For consistency with StoreToOffset() and such treat data_offset as int32_t.
+ uint64_t high48 = static_cast<uint64_t>(static_cast<int32_t>(data_offset)) - low16;
+ int16_t upper16 = High16Bits(high48);
+ // Allow the full [-2GB,+2GB) range in case `low16` is negative and needs a
+ // compensatory 64KB added, which may push `high48` above 2GB and require
+ // the dahi instruction.
+ int16_t higher16 = High32Bits(high48) + ((upper16 < 0) ? 1 : 0);
+ __ Daui(TMP, obj, upper16);
+ if (higher16 != 0) {
+ __ Dahi(TMP, higher16);
+ }
+ base_reg = TMP;
+ data_offset = low16;
+ }
+ } else {
+ DCHECK(IsInt<16>(static_cast<int32_t>(data_offset)));
+ }
+ __ PoisonHeapReference(AT, value);
+ __ Sw(AT, base_reg, data_offset);
+ null_checker();
+ } else {
+ __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
+ }
+ if (needs_write_barrier) {
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
+ }
}
} else {
DCHECK_EQ(value_type, Primitive::kPrimNot);
@@ -1809,47 +1839,54 @@
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
- GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>();
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ StoreToOffset(kStoreDoubleword, value, obj, offset, null_checker);
+ data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
} else {
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
- __ Daddu(TMP, obj, TMP);
- __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker);
+ __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_8);
+ __ Daddu(base_reg, obj, base_reg);
+ }
+ if (value_location.IsConstant()) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
+ } else {
+ GpuRegister value = value_location.AsRegister<GpuRegister>();
+ __ StoreToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
}
break;
}
case Primitive::kPrimFloat: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
- FpuRegister value = locations->InAt(2).AsFpuRegister<FpuRegister>();
- DCHECK(locations->InAt(2).IsFpuRegister());
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ StoreFpuToOffset(kStoreWord, value, obj, offset, null_checker);
+ data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
} else {
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
- __ Daddu(TMP, obj, TMP);
- __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset, null_checker);
+ __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
+ __ Daddu(base_reg, obj, base_reg);
+ }
+ if (value_location.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
+ } else {
+ FpuRegister value = value_location.AsFpuRegister<FpuRegister>();
+ __ StoreFpuToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
}
break;
}
case Primitive::kPrimDouble: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
- FpuRegister value = locations->InAt(2).AsFpuRegister<FpuRegister>();
- DCHECK(locations->InAt(2).IsFpuRegister());
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset, null_checker);
+ data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
} else {
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
- __ Daddu(TMP, obj, TMP);
- __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker);
+ __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_8);
+ __ Daddu(base_reg, obj, base_reg);
+ }
+ if (value_location.IsConstant()) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
+ } else {
+ FpuRegister value = value_location.AsFpuRegister<FpuRegister>();
+ __ StoreFpuToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
}
break;
}
@@ -3326,9 +3363,9 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
if (Primitive::IsFloatingPointType(instruction->InputAt(1)->GetType())) {
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, FpuRegisterOrConstantForStore(instruction->InputAt(1)));
} else {
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, RegisterOrZeroConstant(instruction->InputAt(1)));
}
}
@@ -3338,6 +3375,7 @@
Primitive::Type type = field_info.GetFieldType();
LocationSummary* locations = instruction->GetLocations();
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
+ Location value_location = locations->InAt(1);
StoreOperandType store_type = kStoreByte;
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
@@ -3365,29 +3403,34 @@
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
}
- if (!Primitive::IsFloatingPointType(type)) {
- DCHECK(locations->InAt(1).IsRegister());
- GpuRegister src = locations->InAt(1).AsRegister<GpuRegister>();
- if (kPoisonHeapReferences && needs_write_barrier) {
- // Note that in the case where `value` is a null reference,
- // we do not enter this block, as a null reference does not
- // need poisoning.
- DCHECK_EQ(type, Primitive::kPrimNot);
- __ PoisonHeapReference(TMP, src);
- __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
- } else {
- __ StoreToOffset(store_type, src, obj, offset, null_checker);
- }
- } else {
- DCHECK(locations->InAt(1).IsFpuRegister());
- FpuRegister src = locations->InAt(1).AsFpuRegister<FpuRegister>();
- __ StoreFpuToOffset(store_type, src, obj, offset, null_checker);
- }
+ if (value_location.IsConstant()) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
+ __ StoreConstToOffset(store_type, value, obj, offset, TMP, null_checker);
+ } else {
+ if (!Primitive::IsFloatingPointType(type)) {
+ DCHECK(value_location.IsRegister());
+ GpuRegister src = value_location.AsRegister<GpuRegister>();
+ if (kPoisonHeapReferences && needs_write_barrier) {
+ // Note that in the case where `value` is a null reference,
+ // we do not enter this block, as a null reference does not
+ // need poisoning.
+ DCHECK_EQ(type, Primitive::kPrimNot);
+ __ PoisonHeapReference(TMP, src);
+ __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
+ } else {
+ __ StoreToOffset(store_type, src, obj, offset, null_checker);
+ }
+ } else {
+ DCHECK(value_location.IsFpuRegister());
+ FpuRegister src = value_location.AsFpuRegister<FpuRegister>();
+ __ StoreFpuToOffset(store_type, src, obj, offset, null_checker);
+ }
+ }
// TODO: memory barriers?
if (needs_write_barrier) {
- DCHECK(locations->InAt(1).IsRegister());
- GpuRegister src = locations->InAt(1).AsRegister<GpuRegister>();
+ DCHECK(value_location.IsRegister());
+ GpuRegister src = value_location.AsRegister<GpuRegister>();
codegen_->MarkGCCard(obj, src, value_can_be_null);
}
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 3056f7f..6040dc9 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -189,6 +189,8 @@
void HandleShift(HBinaryOperation* operation);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ Location RegisterOrZeroConstant(HInstruction* instruction);
+ Location FpuRegisterOrConstantForStore(HInstruction* instruction);
InvokeDexCallingConventionVisitorMIPS64 parameter_visitor_;
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 0c832a5..cfcb276 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -17,23 +17,23 @@
#include "dex_cache_array_fixups_arm.h"
#include "base/arena_containers.h"
-#ifdef ART_USE_OLD_ARM_BACKEND
-#include "code_generator_arm.h"
-#include "intrinsics_arm.h"
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
#include "code_generator_arm_vixl.h"
#include "intrinsics_arm_vixl.h"
+#else
+#include "code_generator_arm.h"
+#include "intrinsics_arm.h"
#endif
#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
namespace arm {
-#ifdef ART_USE_OLD_ARM_BACKEND
-typedef CodeGeneratorARM CodeGeneratorARMType;
-typedef IntrinsicLocationsBuilderARM IntrinsicLocationsBuilderARMType;
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
typedef IntrinsicLocationsBuilderARMVIXL IntrinsicLocationsBuilderARMType;
+#else
+typedef CodeGeneratorARM CodeGeneratorARMType;
+typedef IntrinsicLocationsBuilderARM IntrinsicLocationsBuilderARMType;
#endif
/**
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 0b96005..583008b 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -249,20 +249,25 @@
ProfilingInfo* const profiling_info_;
};
-static bool IsMonomorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK_GE(InlineCache::kIndividualCacheSize, 2);
- return classes->Get(0) != nullptr && classes->Get(1) == nullptr;
-}
-
-static bool IsMegamorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
- if (classes->Get(i) == nullptr) {
- return false;
+HInliner::InlineCacheType HInliner::GetInlineCacheType(
+ const Handle<mirror::ObjectArray<mirror::Class>>& classes)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t number_of_types = 0;
+ for (; number_of_types < InlineCache::kIndividualCacheSize; ++number_of_types) {
+ if (classes->Get(number_of_types) == nullptr) {
+ break;
}
}
- return true;
+
+ if (number_of_types == 0) {
+ return kInlineCacheUninitialized;
+ } else if (number_of_types == 1) {
+ return kInlineCacheMonomorphic;
+ } else if (number_of_types == InlineCache::kIndividualCacheSize) {
+ return kInlineCacheMegamorphic;
+ } else {
+ return kInlineCachePolymorphic;
+ }
}
static mirror::Class* GetMonomorphicType(Handle<mirror::ObjectArray<mirror::Class>> classes)
@@ -271,18 +276,6 @@
return classes->Get(0);
}
-static bool IsUninitialized(Handle<mirror::ObjectArray<mirror::Class>> classes)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return classes->Get(0) == nullptr;
-}
-
-static bool IsPolymorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK_GE(InlineCache::kIndividualCacheSize, 3);
- return classes->Get(1) != nullptr &&
- classes->Get(InlineCache::kIndividualCacheSize - 1) == nullptr;
-}
-
ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) {
if (!resolved_method->HasSingleImplementation()) {
return nullptr;
@@ -353,67 +346,209 @@
}
return result;
}
-
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
- // Check if we can use an inline cache.
- ArtMethod* caller = graph_->GetArtMethod();
- if (Runtime::Current()->UseJitCompilation()) {
- // Under JIT, we should always know the caller.
- DCHECK(caller != nullptr);
- ScopedProfilingInfoInlineUse spiis(caller, soa.Self());
- ProfilingInfo* profiling_info = spiis.GetProfilingInfo();
- if (profiling_info != nullptr) {
- StackHandleScope<1> hs(soa.Self());
- ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
- Handle<mirror::ObjectArray<mirror::Class>> inline_cache = hs.NewHandle(
- mirror::ObjectArray<mirror::Class>::Alloc(
- soa.Self(),
- class_linker->GetClassRoot(ClassLinker::kClassArrayClass),
- InlineCache::kIndividualCacheSize));
- if (inline_cache == nullptr) {
- // We got an OOME. Just clear the exception, and don't inline.
- DCHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- VLOG(compiler) << "Out of memory in the compiler when trying to inline";
- return false;
+ // Try using inline caches.
+ return TryInlineFromInlineCache(caller_dex_file, invoke_instruction, resolved_method);
+}
+
+static Handle<mirror::ObjectArray<mirror::Class>> AllocateInlineCacheHolder(
+ const DexCompilationUnit& compilation_unit,
+ StackHandleScope<1>* hs)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = compilation_unit.GetClassLinker();
+ Handle<mirror::ObjectArray<mirror::Class>> inline_cache = hs->NewHandle(
+ mirror::ObjectArray<mirror::Class>::Alloc(
+ self,
+ class_linker->GetClassRoot(ClassLinker::kClassArrayClass),
+ InlineCache::kIndividualCacheSize));
+ if (inline_cache == nullptr) {
+ // We got an OOME. Just clear the exception, and don't inline.
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ VLOG(compiler) << "Out of memory in the compiler when trying to inline";
+ }
+ return inline_cache;
+}
+
+bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file,
+ HInvoke* invoke_instruction,
+ ArtMethod* resolved_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::ObjectArray<mirror::Class>> inline_cache;
+ InlineCacheType inline_cache_type = Runtime::Current()->IsAotCompiler()
+ ? GetInlineCacheAOT(caller_dex_file, invoke_instruction, &hs, &inline_cache)
+ : GetInlineCacheJIT(invoke_instruction, &hs, &inline_cache);
+
+ switch (inline_cache_type) {
+ case kInlineCacheNoData:
+ break;
+
+ case kInlineCacheUninitialized:
+ VLOG(compiler) << "Interface or virtual call to "
+ << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+ << " is not hit and not inlined";
+ return false;
+
+ case kInlineCacheMonomorphic:
+ MaybeRecordStat(kMonomorphicCall);
+ if (outermost_graph_->IsCompilingOsr()) {
+ // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
+ // interpreter and it may have seen different receiver types.
+ return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
} else {
- Runtime::Current()->GetJit()->GetCodeCache()->CopyInlineCacheInto(
- *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()),
- inline_cache);
- if (IsUninitialized(inline_cache)) {
- VLOG(compiler) << "Interface or virtual call to "
- << caller_dex_file.PrettyMethod(method_index)
- << " is not hit and not inlined";
- return false;
- } else if (IsMonomorphic(inline_cache)) {
- MaybeRecordStat(kMonomorphicCall);
- if (outermost_graph_->IsCompilingOsr()) {
- // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
- // interpreter and it may have seen different receiver types.
- return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
- } else {
- return TryInlineMonomorphicCall(invoke_instruction, resolved_method, inline_cache);
- }
- } else if (IsPolymorphic(inline_cache)) {
- MaybeRecordStat(kPolymorphicCall);
- return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
- } else {
- DCHECK(IsMegamorphic(inline_cache));
- VLOG(compiler) << "Interface or virtual call to "
- << caller_dex_file.PrettyMethod(method_index)
- << " is megamorphic and not inlined";
- MaybeRecordStat(kMegamorphicCall);
- return false;
- }
+ return TryInlineMonomorphicCall(invoke_instruction, resolved_method, inline_cache);
}
+
+ case kInlineCachePolymorphic:
+ MaybeRecordStat(kPolymorphicCall);
+ return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
+
+ case kInlineCacheMegamorphic:
+ VLOG(compiler) << "Interface or virtual call to "
+ << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+ << " is megamorphic and not inlined";
+ MaybeRecordStat(kMegamorphicCall);
+ return false;
+
+ case kInlineCacheMissingTypes:
+ VLOG(compiler) << "Interface or virtual call to "
+ << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex())
+ << " is missing types and not inlined";
+ return false;
+ }
+ UNREACHABLE();
+}
+
+HInliner::InlineCacheType HInliner::GetInlineCacheJIT(
+ HInvoke* invoke_instruction,
+ StackHandleScope<1>* hs,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->UseJitCompilation());
+
+ ArtMethod* caller = graph_->GetArtMethod();
+ // Under JIT, we should always know the caller.
+ DCHECK(caller != nullptr);
+ ScopedProfilingInfoInlineUse spiis(caller, Thread::Current());
+ ProfilingInfo* profiling_info = spiis.GetProfilingInfo();
+
+ if (profiling_info == nullptr) {
+ return kInlineCacheNoData;
+ }
+
+ *inline_cache = AllocateInlineCacheHolder(caller_compilation_unit_, hs);
+ if (inline_cache->Get() == nullptr) {
+ // We can't extract any data if we failed to allocate;
+ return kInlineCacheNoData;
+ } else {
+ Runtime::Current()->GetJit()->GetCodeCache()->CopyInlineCacheInto(
+ *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()),
+ *inline_cache);
+ return GetInlineCacheType(*inline_cache);
+ }
+}
+
+HInliner::InlineCacheType HInliner::GetInlineCacheAOT(
+ const DexFile& caller_dex_file,
+ HInvoke* invoke_instruction,
+ StackHandleScope<1>* hs,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ const ProfileCompilationInfo* pci = compiler_driver_->GetProfileCompilationInfo();
+ if (pci == nullptr) {
+ return kInlineCacheNoData;
+ }
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo offline_profile;
+ bool found = pci->GetMethod(caller_dex_file.GetLocation(),
+ caller_dex_file.GetLocationChecksum(),
+ caller_compilation_unit_.GetDexMethodIndex(),
+ &offline_profile);
+ if (!found) {
+ return kInlineCacheNoData; // no profile information for this invocation.
+ }
+
+ *inline_cache = AllocateInlineCacheHolder(caller_compilation_unit_, hs);
+ if (inline_cache == nullptr) {
+ // We can't extract any data if we failed to allocate;
+ return kInlineCacheNoData;
+ } else {
+ return ExtractClassesFromOfflineProfile(invoke_instruction,
+ offline_profile,
+ *inline_cache);
+ }
+}
+
+HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile(
+ const HInvoke* invoke_instruction,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& offline_profile,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>> inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const auto it = offline_profile.inline_caches.find(invoke_instruction->GetDexPc());
+ if (it == offline_profile.inline_caches.end()) {
+ return kInlineCacheUninitialized;
+ }
+
+ const ProfileCompilationInfo::DexPcData& dex_pc_data = it->second;
+
+ if (dex_pc_data.is_missing_types) {
+ return kInlineCacheMissingTypes;
+ }
+ if (dex_pc_data.is_megamorphic) {
+ return kInlineCacheMegamorphic;
+ }
+
+ DCHECK_LE(dex_pc_data.classes.size(), InlineCache::kIndividualCacheSize);
+ Thread* self = Thread::Current();
+ // We need to resolve the class relative to the containing dex file.
+ // So first, build a mapping from the index of dex file in the profile to
+ // its dex cache. This will avoid repeating the lookup when walking over
+ // the inline cache types.
+ std::vector<ObjPtr<mirror::DexCache>> dex_profile_index_to_dex_cache(
+ offline_profile.dex_references.size());
+ for (size_t i = 0; i < offline_profile.dex_references.size(); i++) {
+ bool found = false;
+ for (const DexFile* dex_file : compiler_driver_->GetDexFilesForOatFile()) {
+ if (offline_profile.dex_references[i].MatchesDex(dex_file)) {
+ dex_profile_index_to_dex_cache[i] =
+ caller_compilation_unit_.GetClassLinker()->FindDexCache(self, *dex_file);
+ found = true;
+ }
+ }
+ if (!found) {
+ VLOG(compiler) << "Could not find profiled dex file: "
+ << offline_profile.dex_references[i].dex_location;
+ return kInlineCacheMissingTypes;
}
}
- VLOG(compiler) << "Interface or virtual call to "
- << caller_dex_file.PrettyMethod(method_index)
- << " could not be statically determined";
- return false;
+ // Walk over the classes and resolve them. If we cannot find a type we return
+ // kInlineCacheMissingTypes.
+ int ic_index = 0;
+ for (const ProfileCompilationInfo::ClassReference& class_ref : dex_pc_data.classes) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ dex_profile_index_to_dex_cache[class_ref.dex_profile_index];
+ DCHECK(dex_cache != nullptr);
+ ObjPtr<mirror::Class> clazz = ClassLinker::LookupResolvedType(
+ class_ref.type_index,
+ dex_cache,
+ caller_compilation_unit_.GetClassLoader().Get());
+ if (clazz != nullptr) {
+ inline_cache->Set(ic_index++, clazz);
+ } else {
+ VLOG(compiler) << "Could not resolve class from inline cache in AOT mode "
+ << caller_compilation_unit_.GetDexFile()->PrettyMethod(
+ invoke_instruction->GetDexMethodIndex()) << " : "
+ << caller_compilation_unit_
+ .GetDexFile()->StringByTypeIdx(class_ref.type_index);
+ return kInlineCacheMissingTypes;
+ }
+ }
+ return GetInlineCacheType(inline_cache);
}
HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
@@ -556,6 +691,13 @@
// Insert before setting the kind, as setting the kind affects the inputs.
bb_cursor->InsertInstructionAfter(load_class, receiver_class);
load_class->SetLoadKind(kind);
+ // In AOT mode, we will most likely load the class from BSS, which will involve a call
+ // to the runtime. In this case, the load instruction will need an environment so copy
+ // it from the invoke instruction.
+ if (load_class->NeedsEnvironment()) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ load_class->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ }
HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
@@ -746,7 +888,10 @@
ArtMethod* resolved_method,
Handle<mirror::ObjectArray<mirror::Class>> classes) {
// This optimization only works under JIT for now.
- DCHECK(Runtime::Current()->UseJitCompilation());
+ if (!Runtime::Current()->UseJitCompilation()) {
+ return false;
+ }
+
if (graph_->GetInstructionSet() == kMips64) {
// TODO: Support HClassTableGet for mips64.
return false;
@@ -1064,9 +1209,8 @@
// TODO: Needs null check.
return false;
}
- Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
- HInstanceFieldGet* iget = CreateInstanceFieldGet(dex_cache, data.field_idx, obj);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(data.field_idx, resolved_method, obj);
DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
@@ -1079,10 +1223,9 @@
// TODO: Needs null check.
return false;
}
- Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
- HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, data.field_idx, obj, value);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(data.field_idx, resolved_method, obj, value);
DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
@@ -1116,24 +1259,19 @@
[](uint16_t index) { return index != DexFile::kDexNoIndex16; }));
// Create HInstanceFieldSet for each IPUT that stores non-zero data.
- Handle<mirror::DexCache> dex_cache;
HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, /* this */ 0u);
bool needs_constructor_barrier = false;
for (size_t i = 0; i != number_of_iputs; ++i) {
HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, iput_args[i]);
if (!value->IsConstant() || !value->AsConstant()->IsZeroBitPattern()) {
- if (dex_cache.GetReference() == nullptr) {
- dex_cache = handles_->NewHandle(resolved_method->GetDexCache());
- }
uint16_t field_index = iput_field_indexes[i];
- HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, field_index, obj, value);
+ bool is_final;
+ HInstanceFieldSet* iput =
+ CreateInstanceFieldSet(field_index, resolved_method, obj, value, &is_final);
invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
// Check whether the field is final. If it is, we need to add a barrier.
- PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
- ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
- DCHECK(resolved_field != nullptr);
- if (resolved_field->IsFinal()) {
+ if (is_final) {
needs_constructor_barrier = true;
}
}
@@ -1152,12 +1290,13 @@
return true;
}
-HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex_cache,
- uint32_t field_index,
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(uint32_t field_index,
+ ArtMethod* referrer,
HInstruction* obj)
REQUIRES_SHARED(Locks::mutator_lock_) {
- PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
- ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* resolved_field =
+ class_linker->LookupResolvedField(field_index, referrer, /* is_static */ false);
DCHECK(resolved_field != nullptr);
HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
obj,
@@ -1167,12 +1306,13 @@
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
- *dex_cache->GetDexFile(),
+ *referrer->GetDexFile(),
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
if (iget->GetType() == Primitive::kPrimNot) {
// Use the same dex_cache that we used for field lookup as the hint_dex_cache.
+ Handle<mirror::DexCache> dex_cache = handles_->NewHandle(referrer->GetDexCache());
ReferenceTypePropagation rtp(graph_,
outer_compilation_unit_.GetClassLoader(),
dex_cache,
@@ -1183,14 +1323,21 @@
return iget;
}
-HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex_cache,
- uint32_t field_index,
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(uint32_t field_index,
+ ArtMethod* referrer,
HInstruction* obj,
- HInstruction* value)
+ HInstruction* value,
+ bool* is_final)
REQUIRES_SHARED(Locks::mutator_lock_) {
- PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
- ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* resolved_field =
+ class_linker->LookupResolvedField(field_index, referrer, /* is_static */ false);
DCHECK(resolved_field != nullptr);
+ if (is_final != nullptr) {
+ // This information is needed only for constructors.
+ DCHECK(referrer->IsConstructor());
+ *is_final = resolved_field->IsFinal();
+ }
HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
obj,
value,
@@ -1200,7 +1347,7 @@
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
- *dex_cache->GetDexFile(),
+ *referrer->GetDexFile(),
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 75d025a..a032042 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -20,6 +20,7 @@
#include "dex_file_types.h"
#include "invoke_type.h"
#include "optimization.h"
+#include "jit/profile_compilation_info.h"
namespace art {
@@ -59,6 +60,15 @@
static constexpr const char* kInlinerPassName = "inliner";
private:
+ enum InlineCacheType {
+ kInlineCacheNoData = 0,
+ kInlineCacheUninitialized = 1,
+ kInlineCacheMonomorphic = 2,
+ kInlineCachePolymorphic = 3,
+ kInlineCacheMegamorphic = 4,
+ kInlineCacheMissingTypes = 5
+ };
+
bool TryInline(HInvoke* invoke_instruction);
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
@@ -97,14 +107,54 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Create a new HInstanceFieldGet.
- HInstanceFieldGet* CreateInstanceFieldGet(Handle<mirror::DexCache> dex_cache,
- uint32_t field_index,
+ HInstanceFieldGet* CreateInstanceFieldGet(uint32_t field_index,
+ ArtMethod* referrer,
HInstruction* obj);
// Create a new HInstanceFieldSet.
- HInstanceFieldSet* CreateInstanceFieldSet(Handle<mirror::DexCache> dex_cache,
- uint32_t field_index,
+ HInstanceFieldSet* CreateInstanceFieldSet(uint32_t field_index,
+ ArtMethod* referrer,
HInstruction* obj,
- HInstruction* value);
+ HInstruction* value,
+ bool* is_final = nullptr);
+
+ // Try inlining the invoke instruction using inline caches.
+ bool TryInlineFromInlineCache(
+ const DexFile& caller_dex_file,
+ HInvoke* invoke_instruction,
+ ArtMethod* resolved_method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Try getting the inline cache from JIT code cache.
+ // Return true if the inline cache was successfully allocated and the
+ // invoke info was found in the profile info.
+ InlineCacheType GetInlineCacheJIT(
+ HInvoke* invoke_instruction,
+ StackHandleScope<1>* hs,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Try getting the inline cache from AOT offline profile.
+ // Return true if the inline cache was successfully allocated and the
+ // invoke info was found in the profile info.
+ InlineCacheType GetInlineCacheAOT(const DexFile& caller_dex_file,
+ HInvoke* invoke_instruction,
+ StackHandleScope<1>* hs,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Extract the mirror classes from the offline profile and add them to the `inline_cache`.
+ // Note that even if we have profile data for the invoke the inline_cache might contain
+ // only null entries if the types cannot be resolved.
+ InlineCacheType ExtractClassesFromOfflineProfile(
+ const HInvoke* invoke_instruction,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& offline_profile,
+ /*out*/Handle<mirror::ObjectArray<mirror::Class>> inline_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Compute the inline cache type.
+ InlineCacheType GetInlineCacheType(
+ const Handle<mirror::ObjectArray<mirror::Class>>& classes)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Try to inline the target of a monomorphic call. If successful, the code
// in the graph will look like:
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 934ba1b..807d6cf 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1560,7 +1560,10 @@
// Load `count` field of the argument string and check if it matches the const string.
// Also compares the compression style, if differs return false.
__ Ldr(temp, MemOperand(arg.X(), count_offset));
+ // Temporarily release temp1 as we may not be able to embed the flagged count in CMP immediate.
+ scratch_scope.Release(temp1);
__ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+ temp1 = scratch_scope.AcquireW();
__ B(&return_false, ne);
} else {
// Load `count` fields of this and argument strings.
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index 490e50c..0e02311 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -24,17 +24,17 @@
#include "optimizing/code_generator.h"
#include "optimizing/optimizing_unit_test.h"
#include "utils/assembler.h"
-#ifdef ART_USE_OLD_ARM_BACKEND
-#include "utils/arm/assembler_thumb2.h"
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
#include "utils/arm/assembler_arm_vixl.h"
+#else
+#include "utils/arm/assembler_thumb2.h"
#endif
#include "utils/mips/assembler_mips.h"
#include "utils/mips64/assembler_mips64.h"
#include "optimizing/optimizing_cfi_test_expected.inc"
-#ifndef ART_USE_OLD_ARM_BACKEND
+#ifdef ART_USE_VIXL_ARM_BACKEND
namespace vixl32 = vixl::aarch32;
using vixl32::r0;
@@ -196,15 +196,7 @@
expected_cfi_kThumb2_adjust,
expected_cfi_kThumb2_adjust + arraysize(expected_cfi_kThumb2_adjust));
SetUpFrame(kThumb2);
-#ifdef ART_USE_OLD_ARM_BACKEND
-#define __ down_cast<arm::Thumb2Assembler*>(GetCodeGenerator()->GetAssembler())->
- Label target;
- __ CompareAndBranchIfZero(arm::R0, &target);
- // Push the target out of range of CBZ.
- for (size_t i = 0; i != 65; ++i) {
- __ ldr(arm::R0, arm::Address(arm::R0));
- }
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
#define __ down_cast<arm::ArmVIXLAssembler*>(GetCodeGenerator() \
->GetAssembler())->GetVIXLAssembler()->
vixl32::Label target;
@@ -213,6 +205,14 @@
for (size_t i = 0; i != 65; ++i) {
__ Ldr(r0, vixl32::MemOperand(r0));
}
+#else
+#define __ down_cast<arm::Thumb2Assembler*>(GetCodeGenerator()->GetAssembler())->
+ Label target;
+ __ CompareAndBranchIfZero(arm::R0, &target);
+ // Push the target out of range of CBZ.
+ for (size_t i = 0; i != 65; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
#endif
__ Bind(&target);
#undef __
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index d84fe6c..82670c3 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -223,15 +223,15 @@
// 0x00000040: .cfi_def_cfa_offset: 64
static constexpr uint8_t expected_asm_kThumb2_adjust[] = {
-#ifdef ART_USE_OLD_ARM_BACKEND
- 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28,
- 0x40, 0xD0, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
// VIXL emits an extra 2 bytes here for a 32-bit beq as there is no
// optimistic 16-bit emit and subsequent fixup for out of reach targets
- // as with the old assembler.
+ // as with the current assembler.
0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28, 0x00, 0xF0,
0x41, 0x80, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
+#else
+ 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28,
+ 0x40, 0xD0, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
#endif
0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
@@ -247,10 +247,10 @@
};
static constexpr uint8_t expected_cfi_kThumb2_adjust[] = {
0x42, 0x0E, 0x0C, 0x85, 0x03, 0x86, 0x02, 0x8E, 0x01, 0x44, 0x0E, 0x14,
-#ifdef ART_USE_OLD_ARM_BACKEND
- 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x86, 0x0A,
-#else
+#ifdef ART_USE_VIXL_ARM_BACKEND
0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x88, 0x0A,
+#else
+ 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x86, 0x0A,
#endif
0x42, 0x0E, 0x14, 0x44, 0x0E, 0x0C, 0x06, 0x50, 0x06, 0x51, 0x42, 0x0B,
0x0E, 0x40,
diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc
index 59523a9..8a9c1cc 100644
--- a/compiler/optimizing/register_allocation_resolver.cc
+++ b/compiler/optimizing/register_allocation_resolver.cc
@@ -306,7 +306,7 @@
: Location::StackSlot(interval->GetParent()->GetSpillSlot()));
}
UsePosition* use = current->GetFirstUse();
- UsePosition* env_use = current->GetFirstEnvironmentUse();
+ EnvUsePosition* env_use = current->GetFirstEnvironmentUse();
// Walk over all siblings, updating locations of use positions, and
// connecting them when they are adjacent.
@@ -323,7 +323,6 @@
use = use->GetNext();
}
while (use != nullptr && use->GetPosition() <= range->GetEnd()) {
- DCHECK(!use->GetIsEnvironment());
DCHECK(current->CoversSlow(use->GetPosition()) || (use->GetPosition() == range->GetEnd()));
if (!use->IsSynthesized()) {
LocationSummary* locations = use->GetUser()->GetLocations();
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 2227872..667afb1 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -912,9 +912,9 @@
// Create an interval with lifetime holes.
static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one);
- first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_);
- first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_);
- first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, false, 8, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, false, 7, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, false, 6, first->first_use_);
locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
locations->SetOut(Location::RequiresRegister());
@@ -934,9 +934,9 @@
// before lifetime position 6 yet.
static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three);
- third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_);
- third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_);
- third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, false, 8, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, false, 4, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, false, 3, third->first_use_);
locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
locations->SetOut(Location::RequiresRegister());
third = third->SplitAt(3);
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index a239bd5..340d0cc 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_
#define ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_
-#include "nodes.h"
#include <iostream>
+#include "nodes.h"
+
namespace art {
class CodeGenerator;
@@ -103,21 +104,20 @@
*/
class UsePosition : public ArenaObject<kArenaAllocSsaLiveness> {
public:
- UsePosition(HInstruction* user,
- HEnvironment* environment,
- size_t input_index,
- size_t position,
- UsePosition* next)
+ UsePosition(HInstruction* user, size_t input_index, size_t position, UsePosition* next)
: user_(user),
- environment_(environment),
input_index_(input_index),
position_(position),
next_(next) {
- DCHECK(environment == nullptr || user == nullptr);
DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
}
- static constexpr size_t kNoInput = -1;
+ explicit UsePosition(size_t position)
+ : user_(nullptr),
+ input_index_(kNoInput),
+ position_(dchecked_integral_cast<uint32_t>(position)),
+ next_(nullptr) {
+ }
size_t GetPosition() const { return position_; }
@@ -125,9 +125,7 @@
void SetNext(UsePosition* next) { next_ = next; }
HInstruction* GetUser() const { return user_; }
- HEnvironment* GetEnvironment() const { return environment_; }
- bool GetIsEnvironment() const { return environment_ != nullptr; }
bool IsSynthesized() const { return user_ == nullptr; }
size_t GetInputIndex() const { return input_index_; }
@@ -142,20 +140,20 @@
UsePosition* Dup(ArenaAllocator* allocator) const {
return new (allocator) UsePosition(
- user_, environment_, input_index_, position_,
+ user_, input_index_, position_,
next_ == nullptr ? nullptr : next_->Dup(allocator));
}
bool RequiresRegister() const {
- if (GetIsEnvironment()) return false;
if (IsSynthesized()) return false;
Location location = GetUser()->GetLocations()->InAt(GetInputIndex());
return location.IsUnallocated() && location.RequiresRegisterKind();
}
private:
+ static constexpr uint32_t kNoInput = static_cast<uint32_t>(-1);
+
HInstruction* const user_;
- HEnvironment* const environment_;
const size_t input_index_;
const size_t position_;
UsePosition* next_;
@@ -163,6 +161,50 @@
DISALLOW_COPY_AND_ASSIGN(UsePosition);
};
+/**
+ * An environment use position represents a live interval for environment use at a given position.
+ */
+class EnvUsePosition : public ArenaObject<kArenaAllocSsaLiveness> {
+ public:
+ EnvUsePosition(HEnvironment* environment,
+ size_t input_index,
+ size_t position,
+ EnvUsePosition* next)
+ : environment_(environment),
+ input_index_(input_index),
+ position_(position),
+ next_(next) {
+ DCHECK(environment != nullptr);
+ DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
+ }
+
+ size_t GetPosition() const { return position_; }
+
+ EnvUsePosition* GetNext() const { return next_; }
+ void SetNext(EnvUsePosition* next) { next_ = next; }
+
+ HEnvironment* GetEnvironment() const { return environment_; }
+ size_t GetInputIndex() const { return input_index_; }
+
+ void Dump(std::ostream& stream) const {
+ stream << position_;
+ }
+
+ EnvUsePosition* Dup(ArenaAllocator* allocator) const {
+ return new (allocator) EnvUsePosition(
+ environment_, input_index_, position_,
+ next_ == nullptr ? nullptr : next_->Dup(allocator));
+ }
+
+ private:
+ HEnvironment* const environment_;
+ const size_t input_index_;
+ const size_t position_;
+ EnvUsePosition* next_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnvUsePosition);
+};
+
class SafepointPosition : public ArenaObject<kArenaAllocSsaLiveness> {
public:
explicit SafepointPosition(HInstruction* instruction)
@@ -227,7 +269,7 @@
DCHECK(first_env_use_ == nullptr) << "A temporary cannot have environment user";
size_t position = instruction->GetLifetimePosition();
first_use_ = new (allocator_) UsePosition(
- instruction, /* environment */ nullptr, temp_index, position, first_use_);
+ instruction, temp_index, position, first_use_);
AddRange(position, position + 1);
}
@@ -276,7 +318,7 @@
}
DCHECK(first_use_->GetPosition() + 1 == position);
UsePosition* new_use = new (allocator_) UsePosition(
- instruction, nullptr /* environment */, input_index, position, cursor->GetNext());
+ instruction, input_index, position, cursor->GetNext());
cursor->SetNext(new_use);
if (first_range_->GetEnd() == first_use_->GetPosition()) {
first_range_->end_ = position;
@@ -285,11 +327,11 @@
}
if (is_environment) {
- first_env_use_ = new (allocator_) UsePosition(
- nullptr /* instruction */, environment, input_index, position, first_env_use_);
+ first_env_use_ = new (allocator_) EnvUsePosition(
+ environment, input_index, position, first_env_use_);
} else {
first_use_ = new (allocator_) UsePosition(
- instruction, nullptr /* environment */, input_index, position, first_use_);
+ instruction, input_index, position, first_use_);
}
if (is_environment && !keep_alive) {
@@ -328,7 +370,7 @@
AddBackEdgeUses(*block);
}
first_use_ = new (allocator_) UsePosition(
- instruction, /* environment */ nullptr, input_index, block->GetLifetimeEnd(), first_use_);
+ instruction, input_index, block->GetLifetimeEnd(), first_use_);
}
ALWAYS_INLINE void AddRange(size_t start, size_t end) {
@@ -538,7 +580,7 @@
return first_use_;
}
- UsePosition* GetFirstEnvironmentUse() const {
+ EnvUsePosition* GetFirstEnvironmentUse() const {
return first_env_use_;
}
@@ -676,7 +718,7 @@
current = current->GetNext();
}
stream << "}, uses: { ";
- UsePosition* use = first_use_;
+ const UsePosition* use = first_use_;
if (use != nullptr) {
do {
use->Dump(stream);
@@ -684,12 +726,12 @@
} while ((use = use->GetNext()) != nullptr);
}
stream << "}, { ";
- use = first_env_use_;
- if (use != nullptr) {
+ const EnvUsePosition* env_use = first_env_use_;
+ if (env_use != nullptr) {
do {
- use->Dump(stream);
+ env_use->Dump(stream);
stream << " ";
- } while ((use = use->GetNext()) != nullptr);
+ } while ((env_use = env_use->GetNext()) != nullptr);
}
stream << "}";
stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit();
@@ -1015,12 +1057,7 @@
DCHECK(last_in_new_list == nullptr ||
back_edge_use_position > last_in_new_list->GetPosition());
- UsePosition* new_use = new (allocator_) UsePosition(
- /* user */ nullptr,
- /* environment */ nullptr,
- UsePosition::kNoInput,
- back_edge_use_position,
- /* next */ nullptr);
+ UsePosition* new_use = new (allocator_) UsePosition(back_edge_use_position);
if (last_in_new_list != nullptr) {
// Going outward. The latest created use needs to point to the new use.
@@ -1056,7 +1093,7 @@
// Uses of this interval. Note that this linked list is shared amongst siblings.
UsePosition* first_use_;
- UsePosition* first_env_use_;
+ EnvUsePosition* first_env_use_;
// The instruction type this interval corresponds to.
const Primitive::Type type_;
@@ -1210,8 +1247,7 @@
// Returns whether `instruction` in an HEnvironment held by `env_holder`
// should be kept live by the HEnvironment.
- static bool ShouldBeLiveForEnvironment(HInstruction* env_holder,
- HInstruction* instruction) {
+ static bool ShouldBeLiveForEnvironment(HInstruction* env_holder, HInstruction* instruction) {
if (instruction == nullptr) return false;
// A value that's not live in compiled code may still be needed in interpreter,
// due to code motion, etc.
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
new file mode 100644
index 0000000..1916c73
--- /dev/null
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/instruction_set.h"
+#include "arch/instruction_set_features.h"
+#include "base/arena_allocator.h"
+#include "base/arena_containers.h"
+#include "driver/compiler_options.h"
+#include "code_generator.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "ssa_liveness_analysis.h"
+
+namespace art {
+
+class SsaLivenessAnalysisTest : public testing::Test {
+ public:
+ SsaLivenessAnalysisTest()
+ : pool_(),
+ allocator_(&pool_),
+ graph_(CreateGraph(&allocator_)),
+ compiler_options_(),
+ instruction_set_(kRuntimeISA) {
+ std::string error_msg;
+ instruction_set_features_ =
+ InstructionSetFeatures::FromVariant(instruction_set_, "default", &error_msg);
+ codegen_ = CodeGenerator::Create(graph_,
+ instruction_set_,
+ *instruction_set_features_,
+ compiler_options_);
+ CHECK(codegen_ != nullptr) << instruction_set_ << " is not a supported target architecture.";
+ // Create entry block.
+ entry_ = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry_);
+ graph_->SetEntryBlock(entry_);
+ }
+
+ protected:
+ HBasicBlock* CreateSuccessor(HBasicBlock* block) {
+ HGraph* graph = block->GetGraph();
+ HBasicBlock* successor = new (&allocator_) HBasicBlock(graph);
+ graph->AddBlock(successor);
+ block->AddSuccessor(successor);
+ return successor;
+ }
+
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+ CompilerOptions compiler_options_;
+ InstructionSet instruction_set_;
+ std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+ std::unique_ptr<CodeGenerator> codegen_;
+ HBasicBlock* entry_;
+};
+
+TEST_F(SsaLivenessAnalysisTest, TestReturnArg) {
+ HInstruction* arg = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
+ entry_->AddInstruction(arg);
+
+ HBasicBlock* block = CreateSuccessor(entry_);
+ HInstruction* ret = new (&allocator_) HReturn(arg);
+ block->AddInstruction(ret);
+ block->AddInstruction(new (&allocator_) HExit());
+
+ graph_->BuildDominatorTree();
+ SsaLivenessAnalysis ssa_analysis(graph_, codegen_.get());
+ ssa_analysis.Analyze();
+
+ std::ostringstream arg_dump;
+ arg->GetLiveInterval()->Dump(arg_dump);
+ EXPECT_STREQ("ranges: { [2,6) }, uses: { 6 }, { } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ arg_dump.str().c_str());
+}
+
+TEST_F(SsaLivenessAnalysisTest, TestAput) {
+ HInstruction* array = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
+ HInstruction* index = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(1), 1, Primitive::kPrimInt);
+ HInstruction* value = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(2), 2, Primitive::kPrimInt);
+ HInstruction* extra_arg1 = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(3), 3, Primitive::kPrimInt);
+ HInstruction* extra_arg2 = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(4), 4, Primitive::kPrimNot);
+ ArenaVector<HInstruction*> args({ array, index, value, extra_arg1, extra_arg2 },
+ allocator_.Adapter());
+ for (HInstruction* insn : args) {
+ entry_->AddInstruction(insn);
+ }
+
+ HBasicBlock* block = CreateSuccessor(entry_);
+ HInstruction* null_check = new (&allocator_) HNullCheck(array, 0);
+ block->AddInstruction(null_check);
+ HEnvironment* null_check_env = new (&allocator_) HEnvironment(&allocator_,
+ /* number_of_vregs */ 5,
+ /* method */ nullptr,
+ /* dex_pc */ 0u,
+ null_check);
+ null_check_env->CopyFrom(args);
+ null_check->SetRawEnvironment(null_check_env);
+ HInstruction* length = new (&allocator_) HArrayLength(array, 0);
+ block->AddInstruction(length);
+ HInstruction* bounds_check = new (&allocator_) HBoundsCheck(index, length, /* dex_pc */ 0u);
+ block->AddInstruction(bounds_check);
+ HEnvironment* bounds_check_env = new (&allocator_) HEnvironment(&allocator_,
+ /* number_of_vregs */ 5,
+ /* method */ nullptr,
+ /* dex_pc */ 0u,
+ bounds_check);
+ bounds_check_env->CopyFrom(args);
+ bounds_check->SetRawEnvironment(bounds_check_env);
+ HInstruction* array_set =
+ new (&allocator_) HArraySet(array, index, value, Primitive::kPrimInt, /* dex_pc */ 0);
+ block->AddInstruction(array_set);
+
+ graph_->BuildDominatorTree();
+ SsaLivenessAnalysis ssa_analysis(graph_, codegen_.get());
+ ssa_analysis.Analyze();
+
+ EXPECT_FALSE(graph_->IsDebuggable());
+ EXPECT_EQ(18u, bounds_check->GetLifetimePosition());
+ static const char* const expected[] = {
+ "ranges: { [2,21) }, uses: { 15 17 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 "
+ "is_high: 0",
+ "ranges: { [4,21) }, uses: { 19 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 "
+ "is_high: 0",
+ "ranges: { [6,21) }, uses: { 21 }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 "
+ "is_high: 0",
+ // Environment uses do not keep the non-reference argument alive.
+ "ranges: { [8,10) }, uses: { }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ // Environment uses keep the reference argument alive.
+ "ranges: { [10,19) }, uses: { }, { 15 19 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ };
+ ASSERT_EQ(arraysize(expected), args.size());
+ size_t arg_index = 0u;
+ for (HInstruction* arg : args) {
+ std::ostringstream arg_dump;
+ arg->GetLiveInterval()->Dump(arg_dump);
+ EXPECT_STREQ(expected[arg_index], arg_dump.str().c_str()) << arg_index;
+ ++arg_index;
+ }
+}
+
+TEST_F(SsaLivenessAnalysisTest, TestDeoptimize) {
+ HInstruction* array = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
+ HInstruction* index = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(1), 1, Primitive::kPrimInt);
+ HInstruction* value = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(2), 2, Primitive::kPrimInt);
+ HInstruction* extra_arg1 = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(3), 3, Primitive::kPrimInt);
+ HInstruction* extra_arg2 = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(4), 4, Primitive::kPrimNot);
+ ArenaVector<HInstruction*> args({ array, index, value, extra_arg1, extra_arg2 },
+ allocator_.Adapter());
+ for (HInstruction* insn : args) {
+ entry_->AddInstruction(insn);
+ }
+
+ HBasicBlock* block = CreateSuccessor(entry_);
+ HInstruction* null_check = new (&allocator_) HNullCheck(array, 0);
+ block->AddInstruction(null_check);
+ HEnvironment* null_check_env = new (&allocator_) HEnvironment(&allocator_,
+ /* number_of_vregs */ 5,
+ /* method */ nullptr,
+ /* dex_pc */ 0u,
+ null_check);
+ null_check_env->CopyFrom(args);
+ null_check->SetRawEnvironment(null_check_env);
+ HInstruction* length = new (&allocator_) HArrayLength(array, 0);
+ block->AddInstruction(length);
+ // Use HAboveOrEqual+HDeoptimize as the bounds check.
+ HInstruction* ae = new (&allocator_) HAboveOrEqual(index, length);
+ block->AddInstruction(ae);
+ HInstruction* deoptimize = new(&allocator_) HDeoptimize(ae, /* dex_pc */ 0u);
+ block->AddInstruction(deoptimize);
+ HEnvironment* deoptimize_env = new (&allocator_) HEnvironment(&allocator_,
+ /* number_of_vregs */ 5,
+ /* method */ nullptr,
+ /* dex_pc */ 0u,
+ deoptimize);
+ deoptimize_env->CopyFrom(args);
+ deoptimize->SetRawEnvironment(deoptimize_env);
+ HInstruction* array_set =
+ new (&allocator_) HArraySet(array, index, value, Primitive::kPrimInt, /* dex_pc */ 0);
+ block->AddInstruction(array_set);
+
+ graph_->BuildDominatorTree();
+ SsaLivenessAnalysis ssa_analysis(graph_, codegen_.get());
+ ssa_analysis.Analyze();
+
+ EXPECT_FALSE(graph_->IsDebuggable());
+ EXPECT_EQ(20u, deoptimize->GetLifetimePosition());
+ static const char* const expected[] = {
+ "ranges: { [2,23) }, uses: { 15 17 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 "
+ "is_high: 0",
+ "ranges: { [4,23) }, uses: { 19 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 "
+ "is_high: 0",
+ "ranges: { [6,23) }, uses: { 23 }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ // Environment use in HDeoptimize keeps even the non-reference argument alive.
+ "ranges: { [8,21) }, uses: { }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ // Environment uses keep the reference argument alive.
+ "ranges: { [10,21) }, uses: { }, { 15 21 } is_fixed: 0, is_split: 0 is_low: 0 is_high: 0",
+ };
+ ASSERT_EQ(arraysize(expected), args.size());
+ size_t arg_index = 0u;
+ for (HInstruction* arg : args) {
+ std::ostringstream arg_dump;
+ arg->GetLiveInterval()->Dump(arg_dump);
+ EXPECT_STREQ(expected[arg_index], arg_dump.str().c_str()) << arg_index;
+ ++arg_index;
+ }
+}
+
+} // namespace art
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 5c48759..d265a44 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -42,7 +42,10 @@
kUseQuaternaryName,
};
-template<typename Ass, typename Reg, typename FPReg, typename Imm>
+// For use in the template as the default type to get a nonvector registers version.
+struct NoVectorRegs {};
+
+template<typename Ass, typename Reg, typename FPReg, typename Imm, typename VecReg = NoVectorRegs>
class AssemblerTest : public testing::Test {
public:
Ass* GetAssembler() {
@@ -146,7 +149,8 @@
std::string (AssemblerTest::*GetName1)(const Reg1&),
std::string (AssemblerTest::*GetName2)(const Reg2&),
const std::string& fmt,
- int bias = 0) {
+ int bias = 0,
+ int multiplier = 1) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
@@ -154,7 +158,7 @@
for (auto reg2 : reg2_registers) {
for (int64_t imm : imms) {
ImmType new_imm = CreateImmediate(imm);
- (assembler_.get()->*f)(*reg1, *reg2, new_imm + bias);
+ (assembler_.get()->*f)(*reg1, *reg2, new_imm * multiplier + bias);
std::string base = fmt;
std::string reg1_string = (this->*GetName1)(*reg1);
@@ -172,7 +176,7 @@
size_t imm_index = base.find(IMM_TOKEN);
if (imm_index != std::string::npos) {
std::ostringstream sreg;
- sreg << imm + bias;
+ sreg << imm * multiplier + bias;
std::string imm_string = sreg.str();
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
}
@@ -538,6 +542,69 @@
return str;
}
+ std::string RepeatVV(void (Ass::*f)(VecReg, VecReg), const std::string& fmt) {
+ return RepeatTemplatedRegisters<VecReg, VecReg>(f,
+ GetVectorRegisters(),
+ GetVectorRegisters(),
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetVecRegName,
+ fmt);
+ }
+
+ std::string RepeatVVV(void (Ass::*f)(VecReg, VecReg, VecReg), const std::string& fmt) {
+ return RepeatTemplatedRegisters<VecReg, VecReg, VecReg>(f,
+ GetVectorRegisters(),
+ GetVectorRegisters(),
+ GetVectorRegisters(),
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetVecRegName,
+ fmt);
+ }
+
+ std::string RepeatVR(void (Ass::*f)(VecReg, Reg), const std::string& fmt) {
+ return RepeatTemplatedRegisters<VecReg, Reg>(
+ f,
+ GetVectorRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
+
+ template <typename ImmType>
+ std::string RepeatVRIb(void (Ass::*f)(VecReg, Reg, ImmType),
+ int imm_bits,
+ const std::string& fmt,
+ int bias = 0,
+ int multiplier = 1) {
+ return RepeatTemplatedRegistersImmBits<VecReg, Reg, ImmType>(
+ f,
+ imm_bits,
+ GetVectorRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt,
+ bias,
+ multiplier);
+ }
+
+ template <typename ImmType>
+ std::string RepeatVVIb(void (Ass::*f)(VecReg, VecReg, ImmType),
+ int imm_bits,
+ const std::string& fmt,
+ int bias = 0) {
+ return RepeatTemplatedRegistersImmBits<VecReg, VecReg, ImmType>(f,
+ imm_bits,
+ GetVectorRegisters(),
+ GetVectorRegisters(),
+ &AssemblerTest::GetVecRegName,
+ &AssemblerTest::GetVecRegName,
+ fmt,
+ bias);
+ }
+
// This is intended to be run as a test.
bool CheckTools() {
return test_helper_->CheckTools();
@@ -552,6 +619,11 @@
UNREACHABLE();
}
+ virtual std::vector<VecReg*> GetVectorRegisters() {
+ UNIMPLEMENTED(FATAL) << "Architecture does not support vector registers";
+ UNREACHABLE();
+ }
+
// Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
@@ -971,6 +1043,12 @@
return sreg.str();
}
+ std::string GetVecRegName(const VecReg& reg) {
+ std::ostringstream sreg;
+ sreg << reg;
+ return sreg.str();
+ }
+
// If the assembly file needs a header, return it in a sub-class.
virtual const char* GetAssemblyHeader() {
return nullptr;
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 39eb589..c14315a 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -184,6 +184,106 @@
Emit(encoding);
}
+void Mips64Assembler::EmitMsa3R(int operation,
+ int df,
+ VectorRegister wt,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode) {
+ CHECK_NE(wt, kNoVectorRegister);
+ CHECK_NE(ws, kNoVectorRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ operation << kMsaOperationShift |
+ df << kDfShift |
+ static_cast<uint32_t>(wt) << kWtShift |
+ static_cast<uint32_t>(ws) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitMsaBIT(int operation,
+ int df_m,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode) {
+ CHECK_NE(ws, kNoVectorRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ operation << kMsaOperationShift |
+ df_m << kDfMShift |
+ static_cast<uint32_t>(ws) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitMsaELM(int operation,
+ int df_n,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode) {
+ CHECK_NE(ws, kNoVectorRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ operation << kMsaELMOperationShift |
+ df_n << kDfNShift |
+ static_cast<uint32_t>(ws) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitMsaMI10(int s10,
+ GpuRegister rs,
+ VectorRegister wd,
+ int minor_opcode,
+ int df) {
+ CHECK_NE(rs, kNoGpuRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ CHECK(IsUint<10>(s10)) << s10;
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ s10 << kS10Shift |
+ static_cast<uint32_t>(rs) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode << kS10MinorShift |
+ df;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitMsa2R(int operation,
+ int df,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode) {
+ CHECK_NE(ws, kNoVectorRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ operation << kMsa2ROperationShift |
+ df << kDf2RShift |
+ static_cast<uint32_t>(ws) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitMsa2RF(int operation,
+ int df,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode) {
+ CHECK_NE(ws, kNoVectorRegister);
+ CHECK_NE(wd, kNoVectorRegister);
+ uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+ operation << kMsa2RFOperationShift |
+ df << kDf2RShift |
+ static_cast<uint32_t>(ws) << kWsShift |
+ static_cast<uint32_t>(wd) << kWdShift |
+ minor_opcode;
+ Emit(encoding);
+}
+
void Mips64Assembler::Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
EmitR(0, rs, rt, rd, 0, 0x21);
}
@@ -1080,6 +1180,378 @@
Nor(rd, rs, ZERO);
}
+// TODO: Check for MSA presence in Mips64InstructionSetFeatures for each MSA instruction.
+
+void Mips64Assembler::AndV(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::OrV(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::NorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::XorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::AddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::AddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::AddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::AddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::SubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::SubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::SubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::SubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xe);
+}
+
+void Mips64Assembler::MulvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MulvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MulvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MulvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Div_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::Mod_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::FaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmulW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmulD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FdivW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FdivD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::Ffint_sW(VectorRegister wd, VectorRegister ws) {
+ EmitMsa2RF(0x19e, 0x0, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::Ffint_sD(VectorRegister wd, VectorRegister ws) {
+ EmitMsa2RF(0x19e, 0x1, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::Ftint_sW(VectorRegister wd, VectorRegister ws) {
+ EmitMsa2RF(0x19c, 0x0, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::Ftint_sD(VectorRegister wd, VectorRegister ws) {
+ EmitMsa2RF(0x19c, 0x1, ws, wd, 0x1e);
+}
+
+void Mips64Assembler::SllB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SllH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SllW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SllD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SraB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SraH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SraW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SraD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SrlB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SrlH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SrlW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SrlD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xd);
+}
+
+void Mips64Assembler::SlliB(VectorRegister wd, VectorRegister ws, int shamt3) {
+ CHECK(IsUint<3>(shamt3)) << shamt3;
+ EmitMsaBIT(0x0, shamt3 | kMsaDfMByteMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SlliH(VectorRegister wd, VectorRegister ws, int shamt4) {
+ CHECK(IsUint<4>(shamt4)) << shamt4;
+ EmitMsaBIT(0x0, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SlliW(VectorRegister wd, VectorRegister ws, int shamt5) {
+ CHECK(IsUint<5>(shamt5)) << shamt5;
+ EmitMsaBIT(0x0, shamt5 | kMsaDfMWordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SlliD(VectorRegister wd, VectorRegister ws, int shamt6) {
+ CHECK(IsUint<6>(shamt6)) << shamt6;
+ EmitMsaBIT(0x0, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SraiB(VectorRegister wd, VectorRegister ws, int shamt3) {
+ CHECK(IsUint<3>(shamt3)) << shamt3;
+ EmitMsaBIT(0x1, shamt3 | kMsaDfMByteMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SraiH(VectorRegister wd, VectorRegister ws, int shamt4) {
+ CHECK(IsUint<4>(shamt4)) << shamt4;
+ EmitMsaBIT(0x1, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SraiW(VectorRegister wd, VectorRegister ws, int shamt5) {
+ CHECK(IsUint<5>(shamt5)) << shamt5;
+ EmitMsaBIT(0x1, shamt5 | kMsaDfMWordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SraiD(VectorRegister wd, VectorRegister ws, int shamt6) {
+ CHECK(IsUint<6>(shamt6)) << shamt6;
+ EmitMsaBIT(0x1, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SrliB(VectorRegister wd, VectorRegister ws, int shamt3) {
+ CHECK(IsUint<3>(shamt3)) << shamt3;
+ EmitMsaBIT(0x2, shamt3 | kMsaDfMByteMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SrliH(VectorRegister wd, VectorRegister ws, int shamt4) {
+ CHECK(IsUint<4>(shamt4)) << shamt4;
+ EmitMsaBIT(0x2, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SrliW(VectorRegister wd, VectorRegister ws, int shamt5) {
+ CHECK(IsUint<5>(shamt5)) << shamt5;
+ EmitMsaBIT(0x2, shamt5 | kMsaDfMWordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::SrliD(VectorRegister wd, VectorRegister ws, int shamt6) {
+ CHECK(IsUint<6>(shamt6)) << shamt6;
+ EmitMsaBIT(0x2, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9);
+}
+
+void Mips64Assembler::MoveV(VectorRegister wd, VectorRegister ws) {
+ EmitMsaBIT(0x1, 0x3e, ws, wd, 0x19);
+}
+
+void Mips64Assembler::SplatiB(VectorRegister wd, VectorRegister ws, int n4) {
+ CHECK(IsUint<4>(n4)) << n4;
+ EmitMsaELM(0x1, n4 | kMsaDfNByteMask, ws, wd, 0x19);
+}
+
+void Mips64Assembler::SplatiH(VectorRegister wd, VectorRegister ws, int n3) {
+ CHECK(IsUint<3>(n3)) << n3;
+ EmitMsaELM(0x1, n3 | kMsaDfNHalfwordMask, ws, wd, 0x19);
+}
+
+void Mips64Assembler::SplatiW(VectorRegister wd, VectorRegister ws, int n2) {
+ CHECK(IsUint<2>(n2)) << n2;
+ EmitMsaELM(0x1, n2 | kMsaDfNWordMask, ws, wd, 0x19);
+}
+
+void Mips64Assembler::SplatiD(VectorRegister wd, VectorRegister ws, int n1) {
+ CHECK(IsUint<1>(n1)) << n1;
+ EmitMsaELM(0x1, n1 | kMsaDfNDoublewordMask, ws, wd, 0x19);
+}
+
+void Mips64Assembler::FillB(VectorRegister wd, GpuRegister rs) {
+ EmitMsa2R(0xc0, 0x0, static_cast<VectorRegister>(rs), wd, 0x1e);
+}
+
+void Mips64Assembler::FillH(VectorRegister wd, GpuRegister rs) {
+ EmitMsa2R(0xc0, 0x1, static_cast<VectorRegister>(rs), wd, 0x1e);
+}
+
+void Mips64Assembler::FillW(VectorRegister wd, GpuRegister rs) {
+ EmitMsa2R(0xc0, 0x2, static_cast<VectorRegister>(rs), wd, 0x1e);
+}
+
+void Mips64Assembler::FillD(VectorRegister wd, GpuRegister rs) {
+ EmitMsa2R(0xc0, 0x3, static_cast<VectorRegister>(rs), wd, 0x1e);
+}
+
+void Mips64Assembler::LdB(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<10>(offset)) << offset;
+ EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x8, 0x0);
+}
+
+void Mips64Assembler::LdH(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<11>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64HalfwordSize);
+ EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x8, 0x1);
+}
+
+void Mips64Assembler::LdW(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<12>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x8, 0x2);
+}
+
+void Mips64Assembler::LdD(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<13>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64DoublewordSize);
+ EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x8, 0x3);
+}
+
+void Mips64Assembler::StB(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<10>(offset)) << offset;
+ EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x9, 0x0);
+}
+
+void Mips64Assembler::StH(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<11>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64HalfwordSize);
+ EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x9, 0x1);
+}
+
+void Mips64Assembler::StW(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<12>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x9, 0x2);
+}
+
+void Mips64Assembler::StD(VectorRegister wd, GpuRegister rs, int offset) {
+ CHECK(IsInt<13>(offset)) << offset;
+ CHECK_ALIGNED(offset, kMips64DoublewordSize);
+ EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x9, 0x3);
+}
+
void Mips64Assembler::LoadConst32(GpuRegister rd, int32_t value) {
TemplateLoadConst32(this, rd, value);
}
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 8bbe862..471fdf2 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -266,6 +266,7 @@
}
}
+static constexpr size_t kMips64HalfwordSize = 2;
static constexpr size_t kMips64WordSize = 4;
static constexpr size_t kMips64DoublewordSize = 8;
@@ -644,6 +645,101 @@
void Clear(GpuRegister rd);
void Not(GpuRegister rd, GpuRegister rs);
+ // MSA instructions.
+ void AndV(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void OrV(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void NorV(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void XorV(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
+ void AddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void AddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void AddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void AddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MulvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MulvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MulvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MulvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Div_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void Mod_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
+ void FaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmulW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmulD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FdivW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FdivD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
+ void Ffint_sW(VectorRegister wd, VectorRegister ws);
+ void Ffint_sD(VectorRegister wd, VectorRegister ws);
+ void Ftint_sW(VectorRegister wd, VectorRegister ws);
+ void Ftint_sD(VectorRegister wd, VectorRegister ws);
+
+ void SllB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SllH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SllW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SllD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SraB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SraH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SraW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SraD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SrlB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SrlH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SrlW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void SrlD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
+ // Immediate shift instructions, where shamtN denotes shift amount (must be between 0 and 2^N-1).
+ void SlliB(VectorRegister wd, VectorRegister ws, int shamt3);
+ void SlliH(VectorRegister wd, VectorRegister ws, int shamt4);
+ void SlliW(VectorRegister wd, VectorRegister ws, int shamt5);
+ void SlliD(VectorRegister wd, VectorRegister ws, int shamt6);
+ void SraiB(VectorRegister wd, VectorRegister ws, int shamt3);
+ void SraiH(VectorRegister wd, VectorRegister ws, int shamt4);
+ void SraiW(VectorRegister wd, VectorRegister ws, int shamt5);
+ void SraiD(VectorRegister wd, VectorRegister ws, int shamt6);
+ void SrliB(VectorRegister wd, VectorRegister ws, int shamt3);
+ void SrliH(VectorRegister wd, VectorRegister ws, int shamt4);
+ void SrliW(VectorRegister wd, VectorRegister ws, int shamt5);
+ void SrliD(VectorRegister wd, VectorRegister ws, int shamt6);
+
+ void MoveV(VectorRegister wd, VectorRegister ws);
+ void SplatiB(VectorRegister wd, VectorRegister ws, int n4);
+ void SplatiH(VectorRegister wd, VectorRegister ws, int n3);
+ void SplatiW(VectorRegister wd, VectorRegister ws, int n2);
+ void SplatiD(VectorRegister wd, VectorRegister ws, int n1);
+ void FillB(VectorRegister wd, GpuRegister rs);
+ void FillH(VectorRegister wd, GpuRegister rs);
+ void FillW(VectorRegister wd, GpuRegister rs);
+ void FillD(VectorRegister wd, GpuRegister rs);
+
+ void LdB(VectorRegister wd, GpuRegister rs, int offset);
+ void LdH(VectorRegister wd, GpuRegister rs, int offset);
+ void LdW(VectorRegister wd, GpuRegister rs, int offset);
+ void LdD(VectorRegister wd, GpuRegister rs, int offset);
+ void StB(VectorRegister wd, GpuRegister rs, int offset);
+ void StH(VectorRegister wd, GpuRegister rs, int offset);
+ void StW(VectorRegister wd, GpuRegister rs, int offset);
+ void StD(VectorRegister wd, GpuRegister rs, int offset);
+
// Higher level composite instructions.
int InstrCountForLoadReplicatedConst32(int64_t);
void LoadConst32(GpuRegister rd, int32_t value);
@@ -782,6 +878,86 @@
public:
template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreConstToOffset(StoreOperandType type,
+ int64_t value,
+ GpuRegister base,
+ int32_t offset,
+ GpuRegister temp,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ // We permit `base` and `temp` to coincide (however, we check that neither is AT),
+ // in which case the `base` register may be overwritten in the process.
+ CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
+ if (!IsInt<16>(offset) ||
+ (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
+ Daddu(AT, AT, base);
+ base = AT;
+ offset &= (kMips64DoublewordSize - 1);
+ }
+ GpuRegister reg;
+ // If the adjustment left `base` unchanged and equal to `temp`, we can't use `temp`
+ // to load and hold the value but we can use AT instead as AT hasn't been used yet.
+ // Otherwise, `temp` can be used for the value. And if `temp` is the same as the
+ // original `base` (that is, `base` prior to the adjustment), the original `base`
+ // register will be overwritten.
+ if (base == temp) {
+ temp = AT;
+ }
+
+ if (type == kStoreDoubleword && IsAligned<kMips64DoublewordSize>(offset)) {
+ if (value == 0) {
+ reg = ZERO;
+ } else {
+ reg = temp;
+ LoadConst64(reg, value);
+ }
+ Sd(reg, base, offset);
+ null_checker();
+ } else {
+ uint32_t low = Low32Bits(value);
+ uint32_t high = High32Bits(value);
+ if (low == 0) {
+ reg = ZERO;
+ } else {
+ reg = temp;
+ LoadConst32(reg, low);
+ }
+ switch (type) {
+ case kStoreByte:
+ Sb(reg, base, offset);
+ break;
+ case kStoreHalfword:
+ Sh(reg, base, offset);
+ break;
+ case kStoreWord:
+ Sw(reg, base, offset);
+ break;
+ case kStoreDoubleword:
+ // not aligned to kMips64DoublewordSize
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Sw(reg, base, offset);
+ null_checker();
+ if (high == 0) {
+ reg = ZERO;
+ } else {
+ reg = temp;
+ if (high != low) {
+ LoadConst32(reg, high);
+ }
+ }
+ Sw(reg, base, offset + kMips64WordSize);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ if (type != kStoreDoubleword) {
+ null_checker();
+ }
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
void LoadFromOffset(LoadOperandType type,
GpuRegister reg,
GpuRegister base,
@@ -1300,6 +1476,17 @@
void EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, int funct);
void EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm);
void EmitBcondc(BranchCondition cond, GpuRegister rs, GpuRegister rt, uint32_t imm16_21);
+ void EmitMsa3R(int operation,
+ int df,
+ VectorRegister wt,
+ VectorRegister ws,
+ VectorRegister wd,
+ int minor_opcode);
+ void EmitMsaBIT(int operation, int df_m, VectorRegister ws, VectorRegister wd, int minor_opcode);
+ void EmitMsaELM(int operation, int df_n, VectorRegister ws, VectorRegister wd, int minor_opcode);
+ void EmitMsaMI10(int s10, GpuRegister rs, VectorRegister wd, int minor_opcode, int df);
+ void EmitMsa2R(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode);
+ void EmitMsa2RF(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode);
void Buncond(Mips64Label* label);
void Bcond(Mips64Label* label,
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index 96a02c4..87a1823 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -37,12 +37,14 @@
class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler,
mips64::GpuRegister,
mips64::FpuRegister,
- uint32_t> {
+ uint32_t,
+ mips64::VectorRegister> {
public:
typedef AssemblerTest<mips64::Mips64Assembler,
mips64::GpuRegister,
mips64::FpuRegister,
- uint32_t> Base;
+ uint32_t,
+ mips64::VectorRegister> Base;
protected:
// Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
@@ -60,7 +62,7 @@
// (and MIPS32R6) with the GNU assembler don't have correct final offsets in PC-relative
// branches in the .text section and so they require a relocation pass (there's a relocation
// section, .rela.text, that has the needed info to fix up the branches).
- return " -march=mips64r6 -Wa,--no-warn -Wl,-Ttext=0 -Wl,-e0 -nostdlib";
+ return " -march=mips64r6 -mmsa -Wa,--no-warn -Wl,-Ttext=0 -Wl,-e0 -nostdlib";
}
void Pad(std::vector<uint8_t>& data) OVERRIDE {
@@ -176,6 +178,39 @@
fp_registers_.push_back(new mips64::FpuRegister(mips64::F29));
fp_registers_.push_back(new mips64::FpuRegister(mips64::F30));
fp_registers_.push_back(new mips64::FpuRegister(mips64::F31));
+
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W0));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W1));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W2));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W3));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W4));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W5));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W6));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W7));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W8));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W9));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W10));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W11));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W12));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W13));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W14));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W15));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W16));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W17));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W18));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W19));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W20));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W21));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W22));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W23));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W24));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W25));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W26));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W27));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W28));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W29));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W30));
+ vec_registers_.push_back(new mips64::VectorRegister(mips64::W31));
}
}
@@ -193,6 +228,10 @@
return fp_registers_;
}
+ std::vector<mips64::VectorRegister*> GetVectorRegisters() OVERRIDE {
+ return vec_registers_;
+ }
+
uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
return imm_value;
}
@@ -272,6 +311,7 @@
std::map<mips64::GpuRegister, std::string, MIPS64CpuRegisterCompare> secondary_register_names_;
std::vector<mips64::FpuRegister*> fp_registers_;
+ std::vector<mips64::VectorRegister*> vec_registers_;
};
@@ -2178,6 +2218,82 @@
DriverStr(expected, "StoreFpuToOffset");
}
+TEST_F(AssemblerMIPS64Test, StoreConstToOffset) {
+ __ StoreConstToOffset(mips64::kStoreByte, 0xFF, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreHalfword, 0xFFFF, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreWord, 0x12345678, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreDoubleword, 0x123456789ABCDEF0, mips64::A1, +0, mips64::T8);
+
+ __ StoreConstToOffset(mips64::kStoreByte, 0, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreHalfword, 0, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreWord, 0, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreDoubleword, 0, mips64::A1, +0, mips64::T8);
+
+ __ StoreConstToOffset(mips64::kStoreDoubleword, 0x1234567812345678, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreDoubleword, 0x1234567800000000, mips64::A1, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreDoubleword, 0x0000000012345678, mips64::A1, +0, mips64::T8);
+
+ __ StoreConstToOffset(mips64::kStoreWord, 0, mips64::T8, +0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreWord, 0x12345678, mips64::T8, +0, mips64::T8);
+
+ __ StoreConstToOffset(mips64::kStoreWord, 0, mips64::A1, -0xFFF0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreWord, 0x12345678, mips64::A1, +0xFFF0, mips64::T8);
+
+ __ StoreConstToOffset(mips64::kStoreWord, 0, mips64::T8, -0xFFF0, mips64::T8);
+ __ StoreConstToOffset(mips64::kStoreWord, 0x12345678, mips64::T8, +0xFFF0, mips64::T8);
+
+ const char* expected =
+ "ori $t8, $zero, 0xFF\n"
+ "sb $t8, 0($a1)\n"
+ "ori $t8, $zero, 0xFFFF\n"
+ "sh $t8, 0($a1)\n"
+ "lui $t8, 0x1234\n"
+ "ori $t8, $t8,0x5678\n"
+ "sw $t8, 0($a1)\n"
+ "lui $t8, 0x9abc\n"
+ "ori $t8, $t8,0xdef0\n"
+ "dahi $t8, $t8, 0x5679\n"
+ "dati $t8, $t8, 0x1234\n"
+ "sd $t8, 0($a1)\n"
+ "sb $zero, 0($a1)\n"
+ "sh $zero, 0($a1)\n"
+ "sw $zero, 0($a1)\n"
+ "sd $zero, 0($a1)\n"
+ "lui $t8, 0x1234\n"
+ "ori $t8, $t8,0x5678\n"
+ "dins $t8, $t8, 0x20, 0x20\n"
+ "sd $t8, 0($a1)\n"
+ "lui $t8, 0x246\n"
+ "ori $t8, $t8, 0x8acf\n"
+ "dsll32 $t8, $t8, 0x3\n"
+ "sd $t8, 0($a1)\n"
+ "lui $t8, 0x1234\n"
+ "ori $t8, $t8, 0x5678\n"
+ "sd $t8, 0($a1)\n"
+ "sw $zero, 0($t8)\n"
+ "lui $at,0x1234\n"
+ "ori $at, $at, 0x5678\n"
+ "sw $at, 0($t8)\n"
+ "lui $at, 0xffff\n"
+ "ori $at, $at, 0x10\n"
+ "daddu $at, $at, $a1\n"
+ "sw $zero, 0($at)\n"
+ "li $at, 0xfff0\n"
+ "daddu $at, $at, $a1\n"
+ "lui $t8, 0x1234\n"
+ "ori $t8, $t8, 0x5678\n"
+ "sw $t8, 0($at)\n"
+ "lui $at, 0xffff\n"
+ "ori $at, $at, 0x10\n"
+ "daddu $at, $at, $t8\n"
+ "sw $zero, 0($at)\n"
+ "li $at, 0xfff0\n"
+ "daddu $at, $at, $t8\n"
+ "lui $t8, 0x1234\n"
+ "ori $t8, $t8, 0x5678\n"
+ "sw $t8, 0($at)\n";
+ DriverStr(expected, "StoreConstToOffset");
+}
//////////////////////////////
// Loading/adding Constants //
//////////////////////////////
@@ -2374,6 +2490,370 @@
EXPECT_EQ(tester.GetPathsCovered(), art::mips64::kLoadConst64PathAllPaths);
}
+// MSA instructions.
+
+TEST_F(AssemblerMIPS64Test, AndV) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::AndV, "and.v ${reg1}, ${reg2}, ${reg3}"), "and.v");
+}
+
+TEST_F(AssemblerMIPS64Test, OrV) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::OrV, "or.v ${reg1}, ${reg2}, ${reg3}"), "or.v");
+}
+
+TEST_F(AssemblerMIPS64Test, NorV) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::NorV, "nor.v ${reg1}, ${reg2}, ${reg3}"), "nor.v");
+}
+
+TEST_F(AssemblerMIPS64Test, XorV) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::XorV, "xor.v ${reg1}, ${reg2}, ${reg3}"), "xor.v");
+}
+
+TEST_F(AssemblerMIPS64Test, AddvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::AddvB, "addv.b ${reg1}, ${reg2}, ${reg3}"),
+ "addv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, AddvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::AddvH, "addv.h ${reg1}, ${reg2}, ${reg3}"),
+ "addv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, AddvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::AddvW, "addv.w ${reg1}, ${reg2}, ${reg3}"),
+ "addv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, AddvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::AddvD, "addv.d ${reg1}, ${reg2}, ${reg3}"),
+ "addv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SubvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SubvB, "subv.b ${reg1}, ${reg2}, ${reg3}"),
+ "subv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SubvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SubvH, "subv.h ${reg1}, ${reg2}, ${reg3}"),
+ "subv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SubvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SubvW, "subv.w ${reg1}, ${reg2}, ${reg3}"),
+ "subv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SubvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SubvD, "subv.d ${reg1}, ${reg2}, ${reg3}"),
+ "subv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MulvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MulvB, "mulv.b ${reg1}, ${reg2}, ${reg3}"),
+ "mulv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MulvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MulvH, "mulv.h ${reg1}, ${reg2}, ${reg3}"),
+ "mulv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MulvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MulvW, "mulv.w ${reg1}, ${reg2}, ${reg3}"),
+ "mulv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MulvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MulvD, "mulv.d ${reg1}, ${reg2}, ${reg3}"),
+ "mulv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_sB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_sB, "div_s.b ${reg1}, ${reg2}, ${reg3}"),
+ "div_s.b");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_sH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_sH, "div_s.h ${reg1}, ${reg2}, ${reg3}"),
+ "div_s.h");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_sW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_sW, "div_s.w ${reg1}, ${reg2}, ${reg3}"),
+ "div_s.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_sD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_sD, "div_s.d ${reg1}, ${reg2}, ${reg3}"),
+ "div_s.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_uB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_uB, "div_u.b ${reg1}, ${reg2}, ${reg3}"),
+ "div_u.b");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_uH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_uH, "div_u.h ${reg1}, ${reg2}, ${reg3}"),
+ "div_u.h");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_uW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_uW, "div_u.w ${reg1}, ${reg2}, ${reg3}"),
+ "div_u.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Div_uD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Div_uD, "div_u.d ${reg1}, ${reg2}, ${reg3}"),
+ "div_u.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_sB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_sB, "mod_s.b ${reg1}, ${reg2}, ${reg3}"),
+ "mod_s.b");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_sH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_sH, "mod_s.h ${reg1}, ${reg2}, ${reg3}"),
+ "mod_s.h");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_sW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_sW, "mod_s.w ${reg1}, ${reg2}, ${reg3}"),
+ "mod_s.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_sD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_sD, "mod_s.d ${reg1}, ${reg2}, ${reg3}"),
+ "mod_s.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_uB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_uB, "mod_u.b ${reg1}, ${reg2}, ${reg3}"),
+ "mod_u.b");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_uH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_uH, "mod_u.h ${reg1}, ${reg2}, ${reg3}"),
+ "mod_u.h");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_uW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_uW, "mod_u.w ${reg1}, ${reg2}, ${reg3}"),
+ "mod_u.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Mod_uD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::Mod_uD, "mod_u.d ${reg1}, ${reg2}, ${reg3}"),
+ "mod_u.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FaddW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FaddW, "fadd.w ${reg1}, ${reg2}, ${reg3}"),
+ "fadd.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FaddD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FaddD, "fadd.d ${reg1}, ${reg2}, ${reg3}"),
+ "fadd.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FsubW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FsubW, "fsub.w ${reg1}, ${reg2}, ${reg3}"),
+ "fsub.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FsubD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FsubD, "fsub.d ${reg1}, ${reg2}, ${reg3}"),
+ "fsub.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmulW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmulW, "fmul.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmul.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmulD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmulD, "fmul.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmul.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FdivW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FdivW, "fdiv.w ${reg1}, ${reg2}, ${reg3}"),
+ "fdiv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FdivD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FdivD, "fdiv.d ${reg1}, ${reg2}, ${reg3}"),
+ "fdiv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Ffint_sW) {
+ DriverStr(RepeatVV(&mips64::Mips64Assembler::Ffint_sW, "ffint_s.w ${reg1}, ${reg2}"),
+ "ffint_s.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Ffint_sD) {
+ DriverStr(RepeatVV(&mips64::Mips64Assembler::Ffint_sD, "ffint_s.d ${reg1}, ${reg2}"),
+ "ffint_s.d");
+}
+
+TEST_F(AssemblerMIPS64Test, Ftint_sW) {
+ DriverStr(RepeatVV(&mips64::Mips64Assembler::Ftint_sW, "ftint_s.w ${reg1}, ${reg2}"),
+ "ftint_s.w");
+}
+
+TEST_F(AssemblerMIPS64Test, Ftint_sD) {
+ DriverStr(RepeatVV(&mips64::Mips64Assembler::Ftint_sD, "ftint_s.d ${reg1}, ${reg2}"),
+ "ftint_s.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SllB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SllB, "sll.b ${reg1}, ${reg2}, ${reg3}"), "sll.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SllH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SllH, "sll.h ${reg1}, ${reg2}, ${reg3}"), "sll.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SllW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SllW, "sll.w ${reg1}, ${reg2}, ${reg3}"), "sll.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SllD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SllD, "sll.d ${reg1}, ${reg2}, ${reg3}"), "sll.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SraB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SraB, "sra.b ${reg1}, ${reg2}, ${reg3}"), "sra.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SraH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SraH, "sra.h ${reg1}, ${reg2}, ${reg3}"), "sra.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SraW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SraW, "sra.w ${reg1}, ${reg2}, ${reg3}"), "sra.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SraD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SraD, "sra.d ${reg1}, ${reg2}, ${reg3}"), "sra.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SrlB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SrlB, "srl.b ${reg1}, ${reg2}, ${reg3}"), "srl.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SrlH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SrlH, "srl.h ${reg1}, ${reg2}, ${reg3}"), "srl.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SrlW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SrlW, "srl.w ${reg1}, ${reg2}, ${reg3}"), "srl.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SrlD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::SrlD, "srl.d ${reg1}, ${reg2}, ${reg3}"), "srl.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SlliB) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SlliB, 3, "slli.b ${reg1}, ${reg2}, {imm}"),
+ "slli.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SlliH) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SlliH, 4, "slli.h ${reg1}, ${reg2}, {imm}"),
+ "slli.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SlliW) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SlliW, 5, "slli.w ${reg1}, ${reg2}, {imm}"),
+ "slli.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SlliD) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SlliD, 6, "slli.d ${reg1}, ${reg2}, {imm}"),
+ "slli.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MoveV) {
+ DriverStr(RepeatVV(&mips64::Mips64Assembler::MoveV, "move.v ${reg1}, ${reg2}"), "move.v");
+}
+
+TEST_F(AssemblerMIPS64Test, SplatiB) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SplatiB, 4, "splati.b ${reg1}, ${reg2}[{imm}]"),
+ "splati.b");
+}
+
+TEST_F(AssemblerMIPS64Test, SplatiH) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SplatiH, 3, "splati.h ${reg1}, ${reg2}[{imm}]"),
+ "splati.h");
+}
+
+TEST_F(AssemblerMIPS64Test, SplatiW) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SplatiW, 2, "splati.w ${reg1}, ${reg2}[{imm}]"),
+ "splati.w");
+}
+
+TEST_F(AssemblerMIPS64Test, SplatiD) {
+ DriverStr(RepeatVVIb(&mips64::Mips64Assembler::SplatiD, 1, "splati.d ${reg1}, ${reg2}[{imm}]"),
+ "splati.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FillB) {
+ DriverStr(RepeatVR(&mips64::Mips64Assembler::FillB, "fill.b ${reg1}, ${reg2}"), "fill.b");
+}
+
+TEST_F(AssemblerMIPS64Test, FillH) {
+ DriverStr(RepeatVR(&mips64::Mips64Assembler::FillH, "fill.h ${reg1}, ${reg2}"), "fill.h");
+}
+
+TEST_F(AssemblerMIPS64Test, FillW) {
+ DriverStr(RepeatVR(&mips64::Mips64Assembler::FillW, "fill.w ${reg1}, ${reg2}"), "fill.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FillD) {
+ DriverStr(RepeatVR(&mips64::Mips64Assembler::FillD, "fill.d ${reg1}, ${reg2}"), "fill.d");
+}
+
+TEST_F(AssemblerMIPS64Test, LdB) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::LdB, -10, "ld.b ${reg1}, {imm}(${reg2})"), "ld.b");
+}
+
+TEST_F(AssemblerMIPS64Test, LdH) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::LdH, -10, "ld.h ${reg1}, {imm}(${reg2})", 0, 2),
+ "ld.h");
+}
+
+TEST_F(AssemblerMIPS64Test, LdW) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::LdW, -10, "ld.w ${reg1}, {imm}(${reg2})", 0, 4),
+ "ld.w");
+}
+
+TEST_F(AssemblerMIPS64Test, LdD) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::LdD, -10, "ld.d ${reg1}, {imm}(${reg2})", 0, 8),
+ "ld.d");
+}
+
+TEST_F(AssemblerMIPS64Test, StB) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::StB, -10, "st.b ${reg1}, {imm}(${reg2})"), "st.b");
+}
+
+TEST_F(AssemblerMIPS64Test, StH) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::StH, -10, "st.h ${reg1}, {imm}(${reg2})", 0, 2),
+ "st.h");
+}
+
+TEST_F(AssemblerMIPS64Test, StW) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::StW, -10, "st.w ${reg1}, {imm}(${reg2})", 0, 4),
+ "st.w");
+}
+
+TEST_F(AssemblerMIPS64Test, StD) {
+ DriverStr(RepeatVRIb(&mips64::Mips64Assembler::StD, -10, "st.d ${reg1}, {imm}(${reg2})", 0, 8),
+ "st.d");
+}
+
#undef __
} // namespace art
diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h
index f57498d..5ae9c73 100644
--- a/compiler/utils/mips64/constants_mips64.h
+++ b/compiler/utils/mips64/constants_mips64.h
@@ -51,8 +51,35 @@
kFdShift = 6,
kFdBits = 5,
+ kMsaOperationShift = 23,
+ kMsaELMOperationShift = 22,
+ kMsa2ROperationShift = 18,
+ kMsa2RFOperationShift = 17,
+ kDfShift = 21,
+ kDfMShift = 16,
+ kDf2RShift = 16,
+ kDfNShift = 16,
+ kWtShift = 16,
+ kWtBits = 5,
+ kWsShift = 11,
+ kWsBits = 5,
+ kWdShift = 6,
+ kWdBits = 5,
+ kS10Shift = 16,
+ kS10MinorShift = 2,
+
kBranchOffsetMask = 0x0000ffff,
kJumpOffsetMask = 0x03ffffff,
+ kMsaMajorOpcode = 0x1e,
+ kMsaDfMByteMask = 0x70,
+ kMsaDfMHalfwordMask = 0x60,
+ kMsaDfMWordMask = 0x40,
+ kMsaDfMDoublewordMask = 0x00,
+ kMsaDfNByteMask = 0x00,
+ kMsaDfNHalfwordMask = 0x20,
+ kMsaDfNWordMask = 0x30,
+ kMsaDfNDoublewordMask = 0x38,
+ kMsaS10Mask = 0x3ff,
};
enum ScaleFactor {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 6a57f45..0a6ceef 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1169,6 +1169,32 @@
}
+void X86Assembler::andnpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x55);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::andnps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x55);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pandn(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xDF);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
void X86Assembler::orpd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index e3c123c..9d7ca77 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -487,6 +487,10 @@
void andps(XmmRegister dst, const Address& src);
void pand(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void andnpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void andnps(XmmRegister dst, XmmRegister src);
+ void pandn(XmmRegister dst, XmmRegister src);
+
void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void orps(XmmRegister dst, XmmRegister src);
void por(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 110d0dc..52c7507 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -581,6 +581,18 @@
DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
}
+TEST_F(AssemblerX86Test, AndnPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::andnpd, "andnpd %{reg2}, %{reg1}"), "andnpd");
+}
+
+TEST_F(AssemblerX86Test, AndnPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::andnps, "andnps %{reg2}, %{reg1}"), "andnps");
+}
+
+TEST_F(AssemblerX86Test, PAndn) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pandn, "pandn %{reg2}, %{reg1}"), "pandn");
+}
+
TEST_F(AssemblerX86Test, OrPD) {
DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 688fdcc..d7fed5b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1375,6 +1375,32 @@
EmitXmmRegisterOperand(dst.LowBits(), src);
}
+void X86_64Assembler::andnpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x55);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::andnps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x55);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::pandn(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xDF);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 480e711..93c24b8 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -515,6 +515,10 @@
void andps(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void pand(XmmRegister dst, XmmRegister src);
+ void andnpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void andnps(XmmRegister dst, XmmRegister src);
+ void pandn(XmmRegister dst, XmmRegister src);
+
void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void orps(XmmRegister dst, XmmRegister src);
void por(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index ba011c9..9d62fd1 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1269,6 +1269,18 @@
DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
}
+TEST_F(AssemblerX86_64Test, andnpd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::andnpd, "andnpd %{reg2}, %{reg1}"), "andnpd");
+}
+
+TEST_F(AssemblerX86_64Test, andnps) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::andnps, "andnps %{reg2}, %{reg1}"), "andnps");
+}
+
+TEST_F(AssemblerX86_64Test, Pandn) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pandn, "pandn %{reg2}, %{reg1}"), "pandn");
+}
+
TEST_F(AssemblerX86_64Test, Orps) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
}
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 01c3359..1a1d163 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -714,12 +714,12 @@
TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/lang/System;", true, "public final"));
+ ASSERT_TRUE(HasClass("Ljava/lang/System;", true, "public"));
ASSERT_TRUE(HasField("Ljava/lang/System;",
"out",
"Ljava/io/PrintStream;",
true,
- "public final static",
+ "public static",
"Ljava/lang/System;"));
}
@@ -727,13 +727,13 @@
ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass1"));
ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public"));
ASSERT_TRUE(HasField(
- "Ljava/util/SimpleTimeZone;", "LONG", "I", true, "public final static", "Ljava/util/TimeZone;"));
+ "Ljava/util/SimpleTimeZone;", "LONG", "I", true, "public static", "Ljava/util/TimeZone;"));
}
TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInSuperclass2) {
ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass2"));
ASSERT_TRUE(HasField(
- "LMySimpleTimeZone;", "SHORT", "I", true, "public final static", "Ljava/util/TimeZone;"));
+ "LMySimpleTimeZone;", "SHORT", "I", true, "public static", "Ljava/util/TimeZone;"));
}
TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface1) {
@@ -743,7 +743,7 @@
"PI_ENABLE_OUTPUT_ESCAPING",
"Ljava/lang/String;",
true,
- "public final static",
+ "public static",
"Ljavax/xml/transform/Result;"));
}
@@ -753,7 +753,7 @@
"PI_ENABLE_OUTPUT_ESCAPING",
"Ljava/lang/String;",
true,
- "public final static",
+ "public static",
"Ljavax/xml/transform/Result;"));
}
@@ -763,7 +763,7 @@
"PI_ENABLE_OUTPUT_ESCAPING",
"Ljava/lang/String;",
true,
- "public final static",
+ "public static",
"Ljavax/xml/transform/Result;"));
}
@@ -773,13 +773,13 @@
"ELEMENT_NODE",
"S",
true,
- "public final static",
+ "public static",
"Lorg/w3c/dom/Node;"));
}
TEST_F(VerifierDepsTest, StaticField_Unresolved_ReferrerInBoot) {
ASSERT_TRUE(VerifyMethod("StaticField_Unresolved_ReferrerInBoot"));
- ASSERT_TRUE(HasClass("Ljava/util/TimeZone;", true, "public abstract"));
+ ASSERT_TRUE(HasClass("Ljava/util/TimeZone;", true, "public"));
ASSERT_TRUE(HasField("Ljava/util/TimeZone;", "x", "I", false));
}
@@ -851,7 +851,7 @@
TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass1) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract"));
+ ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
ASSERT_TRUE(HasMethod("direct",
"Ljavax/net/ssl/SSLSocket;",
"setSocketImplFactory",
@@ -874,7 +874,7 @@
TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_DeclaredInInterface1"));
- ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public abstract interface"));
+ ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public interface"));
ASSERT_TRUE(HasMethod("direct",
"Ljava/util/Map$Entry;",
"comparingByKey",
@@ -896,7 +896,7 @@
TEST_F(VerifierDepsTest, InvokeStatic_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract"));
+ ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false));
}
@@ -914,7 +914,7 @@
TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass1) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract"));
+ ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
ASSERT_TRUE(HasMethod("direct",
"Ljavax/net/ssl/SSLSocket;",
"checkOldImpl",
@@ -932,7 +932,7 @@
TEST_F(VerifierDepsTest, InvokeDirect_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract"));
+ ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false));
}
@@ -987,7 +987,7 @@
"size",
"()I",
true,
- "public abstract",
+ "public",
"Ljava/util/Set;"));
}
@@ -1016,13 +1016,13 @@
TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeInterface_Resolved_DeclaredInReferenced"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface"));
+ ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
ASSERT_TRUE(HasMethod("interface",
"Ljava/lang/Runnable;",
"run",
"()V",
true,
- "public abstract",
+ "public",
"Ljava/lang/Runnable;"));
}
@@ -1038,7 +1038,7 @@
"run",
"()V",
true,
- "public abstract",
+ "public",
"Ljava/lang/Runnable;"));
}
@@ -1049,13 +1049,13 @@
"isEmpty",
"()Z",
true,
- "public abstract",
+ "public",
"Ljava/util/Set;"));
}
TEST_F(VerifierDepsTest, InvokeInterface_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved1"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface"));
+ ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "x", "()V", false));
}
@@ -1066,20 +1066,20 @@
TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) {
ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable"));
- ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface"));
+ ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true));
ASSERT_TRUE(HasMethod("interface",
"Ljava/lang/Runnable;",
"run",
"()V",
true,
- "public abstract",
+ "public",
"Ljava/lang/Runnable;"));
}
TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) {
ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable"));
- ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public final"));
+ ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false));
ASSERT_TRUE(HasMethod(
"virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;"));
@@ -1087,12 +1087,12 @@
TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) {
ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray"));
- ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public final abstract"));
+ ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public"));
}
TEST_F(VerifierDepsTest, NewArray_Resolved) {
ASSERT_TRUE(VerifyMethod("NewArray_Resolved"));
- ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public final abstract"));
+ ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public"));
}
TEST_F(VerifierDepsTest, EncodeDecode) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2684f3d..92a12c8 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1486,7 +1486,7 @@
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
std::vector<gc::space::ImageSpace*> image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
- image_file_location_oat_checksum_ = OatFileAssistant::CalculateCombinedImageChecksum();
+ image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum();
image_file_location_oat_data_begin_ =
reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin());
image_patch_delta_ = image_spaces[0]->GetImageHeader().GetPatchDelta();
@@ -1907,6 +1907,14 @@
oat_writer->GetOatDataOffset(),
oat_writer->GetOatSize());
}
+
+ if (IsBootImage()) {
+ // Have the image_file_location_oat_checksum_ for boot oat files
+ // depend on the contents of all the boot oat files. This way only
+ // the primary image checksum needs to be checked to determine
+ // whether any of the images are out of date.
+ image_file_location_oat_checksum_ ^= oat_writer->GetOatHeader().GetChecksum();
+ }
}
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
@@ -2100,6 +2108,10 @@
return DoProfileGuidedOptimizations();
}
+ bool HasInputVdexFile() const {
+ return input_vdex_file_ != nullptr || input_vdex_fd_ != -1 || !input_vdex_.empty();
+ }
+
bool LoadProfile() {
DCHECK(UseProfile());
@@ -2885,6 +2897,13 @@
}
}
+ if (dex2oat->DoDexLayoutOptimizations()) {
+ if (dex2oat->HasInputVdexFile()) {
+ LOG(ERROR) << "Dexlayout is incompatible with an input VDEX";
+ return EXIT_FAILURE;
+ }
+ }
+
art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex.
// Check early that the result of compilation can be written
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index e7277bc..289b8ab 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -604,7 +604,8 @@
const std::string& app_image_file_name,
bool use_fd,
size_t num_profile_classes,
- const std::vector<std::string>& extra_args = {}) {
+ const std::vector<std::string>& extra_args = {},
+ bool expect_success = true) {
const std::string profile_location = GetScratchDir() + "/primary.prof";
const char* location = dex_location.c_str();
std::string error_msg;
@@ -631,7 +632,7 @@
odex_location,
CompilerFilter::kSpeedProfile,
copy,
- /* expect_success */ true,
+ expect_success,
use_fd);
if (app_image_file != nullptr) {
ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file";
@@ -709,6 +710,7 @@
EXPECT_GT(vdex_file1->GetLength(), 0u);
}
{
+ // Test that vdex and dexlayout fail gracefully.
std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2.GetFd());
CompileProfileOdex(dex_location,
@@ -716,13 +718,13 @@
app_image_file_name,
/* use_fd */ true,
/* num_profile_classes */ 1,
- { input_vdex, output_vdex });
- EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u);
+ { input_vdex, output_vdex },
+ /* expect_success */ false);
+ EXPECT_EQ(vdex_file2.GetFile()->GetLength(), 0u);
}
ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
CheckValidity();
- ASSERT_TRUE(success_);
- CheckResult(dex_location, odex_location, app_image_file_name);
+ ASSERT_FALSE(success_);
}
void CheckResult(const std::string& dex_location,
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 9ee9ebd..cf523ec 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -19,6 +19,7 @@
"dexlayout.cc",
"dex_ir.cc",
"dex_ir_builder.cc",
+ "dex_verify.cc",
"dex_visualize.cc",
"dex_writer.cc",
],
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 34983cf..4228503 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -56,7 +56,7 @@
entry.end_address_, entry.reg_)));
}
-static uint32_t GetCodeItemSize(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item) {
+static uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) {
uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&disk_code_item);
uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
uint32_t tries_size = disk_code_item.tries_size_;
@@ -675,7 +675,7 @@
}
}
- uint32_t size = GetCodeItemSize(dex_file, disk_code_item);
+ uint32_t size = GetCodeItemSize(disk_code_item);
CodeItem* code_item = new CodeItem(
registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list);
code_item->SetSize(size);
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
new file mode 100644
index 0000000..aec1d0c
--- /dev/null
+++ b/dexlayout/dex_verify.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Implementation file of dex ir verifier.
+ *
+ * Compares two dex files at the IR level, allowing differences in layout, but not in data.
+ */
+
+#include "dex_verify.h"
+
+#include "android-base/stringprintf.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+bool VerifyOutputDexFile(dex_ir::Header* orig_header,
+ dex_ir::Header* output_header,
+ std::string* error_msg) {
+ dex_ir::Collections& orig = orig_header->GetCollections();
+ dex_ir::Collections& output = output_header->GetCollections();
+
+ // Compare all id sections.
+ if (!VerifyIds(orig.StringIds(), output.StringIds(), "string ids", error_msg) ||
+ !VerifyIds(orig.TypeIds(), output.TypeIds(), "type ids", error_msg) ||
+ !VerifyIds(orig.ProtoIds(), output.ProtoIds(), "proto ids", error_msg) ||
+ !VerifyIds(orig.FieldIds(), output.FieldIds(), "field ids", error_msg) ||
+ !VerifyIds(orig.MethodIds(), output.MethodIds(), "method ids", error_msg)) {
+ return false;
+ }
+ return true;
+}
+
+template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig,
+ std::vector<std::unique_ptr<T>>& output,
+ const char* section_name,
+ std::string* error_msg) {
+ if (orig.size() != output.size()) {
+ *error_msg = StringPrintf(
+ "Mismatched size for %s section, %zu vs %zu.", section_name, orig.size(), output.size());
+ return false;
+ }
+ for (size_t i = 0; i < orig.size(); ++i) {
+ if (!VerifyId(orig[i].get(), output[i].get(), error_msg)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg) {
+ if (strcmp(orig->Data(), output->Data()) != 0) {
+ *error_msg = StringPrintf("Mismatched string data for string id %u @ orig offset %x, %s vs %s.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Data(),
+ output->Data());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg) {
+ if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for type id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->GetStringId()->GetIndex(),
+ output->GetStringId()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg) {
+ if (orig->Shorty()->GetIndex() != output->Shorty()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for proto id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Shorty()->GetIndex(),
+ output->Shorty()->GetIndex());
+ return false;
+ }
+ if (orig->ReturnType()->GetIndex() != output->ReturnType()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for proto id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->ReturnType()->GetIndex(),
+ output->ReturnType()->GetIndex());
+ return false;
+ }
+ if (!VerifyTypeList(orig->Parameters(), output->Parameters())) {
+ *error_msg = StringPrintf("Mismatched type list for proto id %u @ orig offset %x.",
+ orig->GetIndex(),
+ orig->GetOffset());
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg) {
+ if (orig->Class()->GetIndex() != output->Class()->GetIndex()) {
+ *error_msg =
+ StringPrintf("Mismatched class type index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Type()->GetIndex() != output->Type()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Name()->GetIndex() != output->Name()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Name()->GetIndex(),
+ output->Name()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg) {
+ if (orig->Class()->GetIndex() != output->Class()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Proto()->GetIndex() != output->Proto()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched proto index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Name()->GetIndex() != output->Name()->GetIndex()) {
+ *error_msg =
+ StringPrintf("Mismatched string index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Name()->GetIndex(),
+ output->Name()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output) {
+ if (orig == nullptr || output == nullptr) {
+ return orig == output;
+ }
+ const dex_ir::TypeIdVector* orig_list = orig->GetTypeList();
+ const dex_ir::TypeIdVector* output_list = output->GetTypeList();
+ if (orig_list->size() != output_list->size()) {
+ return false;
+ }
+ for (size_t i = 0; i < orig_list->size(); ++i) {
+ if ((*orig_list)[i]->GetIndex() != (*output_list)[i]->GetIndex()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace art
diff --git a/dexlayout/dex_verify.h b/dexlayout/dex_verify.h
new file mode 100644
index 0000000..a19431c
--- /dev/null
+++ b/dexlayout/dex_verify.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file of dex ir verifier.
+ *
+ * Compares two dex files at the IR level, allowing differences in layout, but not in data.
+ */
+
+#ifndef ART_DEXLAYOUT_DEX_VERIFY_H_
+#define ART_DEXLAYOUT_DEX_VERIFY_H_
+
+#include "dex_ir.h"
+
+namespace art {
+
+// Check that the output dex file contains the same data as the original.
+// Compares the dex IR of both dex files. Allows the dex files to have different layouts.
+bool VerifyOutputDexFile(dex_ir::Header* orig_header,
+ dex_ir::Header* output_header,
+ std::string* error_msg);
+template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig,
+ std::vector<std::unique_ptr<T>>& output,
+ const char* section_name,
+ std::string* error_msg);
+bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg);
+bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg);
+bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg);
+bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg);
+bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg);
+bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output);
+
+} // namespace art
+
+#endif // ART_DEXLAYOUT_DEX_VERIFY_H_
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index 75d47e4..8997146 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -35,6 +35,12 @@
namespace art {
+std::string MultidexName(const std::string& prefix,
+ size_t dex_file_index,
+ const std::string& suffix) {
+ return prefix + ((dex_file_index > 0) ? std::to_string(dex_file_index + 1) : "") + suffix;
+}
+
struct FileSection {
public:
std::string name_;
@@ -43,8 +49,22 @@
std::function<uint32_t(const dex_ir::Collections&)> offset_fn_;
};
+static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
+ return 0;
+}
+
+static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
+ // Size is in elements, so there is only one header.
+ return 1;
+}
+
static const std::vector<FileSection> kFileSections = {
{
+ "Header",
+ DexFile::kDexTypeHeaderItem,
+ &HeaderSize,
+ &HeaderOffset,
+ }, {
"StringId",
DexFile::kDexTypeStringIdItem,
&dex_ir::Collections::StringIdsSize,
@@ -127,58 +147,71 @@
}
};
+static constexpr bool kSortAscending = false;
+static constexpr bool kSortDescending = true;
+
+static std::vector<const FileSection*> GetSortedSections(
+ const dex_ir::Collections& collections,
+ bool sort_descending) {
+ std::vector<const FileSection*> sorted_sections;
+ // Build the table that will map from offset to color
+ for (const FileSection& s : kFileSections) {
+ sorted_sections.push_back(&s);
+ }
+ // Sort by offset.
+ std::sort(sorted_sections.begin(),
+ sorted_sections.end(),
+ [&](const FileSection* a, const FileSection* b) {
+ if (sort_descending) {
+ return a->offset_fn_(collections) > b->offset_fn_(collections);
+ } else {
+ return a->offset_fn_(collections) < b->offset_fn_(collections);
+ }
+ });
+ return sorted_sections;
+}
+
class Dumper {
public:
// Colors are based on the type of the section in MapList.
- Dumper(const dex_ir::Collections& collections, size_t dex_file_index) {
- // Build the table that will map from offset to color
- table_.emplace_back(DexFile::kDexTypeHeaderItem, 0u);
- for (const FileSection& s : kFileSections) {
- table_.emplace_back(s.type_, s.offset_fn_(collections));
- }
- // Sort into descending order by offset.
- std::sort(table_.begin(),
- table_.end(),
- [](const SectionColor& a, const SectionColor& b) { return a.offset_ > b.offset_; });
+ explicit Dumper(const dex_ir::Collections& collections)
+ : collections_(collections), out_file_(nullptr),
+ sorted_sections_(GetSortedSections(collections, kSortDescending)) { }
+
+ bool OpenAndPrintHeader(size_t dex_index) {
// Open the file and emit the gnuplot prologue.
- std::string dex_file_name("classes");
- std::string out_file_base_name("layout");
- if (dex_file_index > 0) {
- out_file_base_name += std::to_string(dex_file_index + 1);
- dex_file_name += std::to_string(dex_file_index + 1);
+ out_file_ = fopen(MultidexName("layout", dex_index, ".gnuplot").c_str(), "w");
+ if (out_file_ == nullptr) {
+ return false;
}
- dex_file_name += ".dex";
- std::string out_file_name(out_file_base_name + ".gnuplot");
- std::string png_file_name(out_file_base_name + ".png");
- out_file_ = fopen(out_file_name.c_str(), "w");
fprintf(out_file_, "set terminal png size 1920,1080\n");
- fprintf(out_file_, "set output \"%s\"\n", png_file_name.c_str());
- fprintf(out_file_, "set title \"%s\"\n", dex_file_name.c_str());
+ fprintf(out_file_, "set output \"%s\"\n", MultidexName("layout", dex_index, ".png").c_str());
+ fprintf(out_file_, "set title \"%s\"\n", MultidexName("classes", dex_index, ".dex").c_str());
fprintf(out_file_, "set xlabel \"Page offset into dex\"\n");
fprintf(out_file_, "set ylabel \"ClassDef index\"\n");
fprintf(out_file_, "set xtics rotate out (");
- fprintf(out_file_, "\"Header\" %d, ", 0);
bool printed_one = false;
for (const FileSection& s : kFileSections) {
- if (s.size_fn_(collections) > 0) {
+ if (s.size_fn_(collections_) > 0) {
if (printed_one) {
fprintf(out_file_, ", ");
}
- fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections) / kPageSize);
+ fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections_) / kPageSize);
printed_one = true;
}
}
fprintf(out_file_, ")\n");
fprintf(out_file_,
"plot \"-\" using 1:2:3:4:5 with vector nohead linewidth 1 lc variable notitle\n");
+ return true;
}
int GetColor(uint32_t offset) const {
// The dread linear search to find the right section for the reference.
uint16_t section = 0;
- for (uint16_t i = 0; i < table_.size(); ++i) {
- if (table_[i].offset_ < offset) {
- section = table_[i].type_;
+ for (const FileSection* file_section : sorted_sections_) {
+ if (file_section->offset_fn_(collections_) < offset) {
+ section = file_section->type_;
break;
}
}
@@ -308,13 +341,6 @@
}
private:
- struct SectionColor {
- public:
- SectionColor(uint16_t type, uint32_t offset) : type_(type), offset_(offset) { }
- uint16_t type_;
- uint32_t offset_;
- };
-
using ColorMapType = std::map<uint16_t, int>;
const ColorMapType kColorMap = {
{ DexFile::kDexTypeHeaderItem, 1 },
@@ -336,8 +362,9 @@
{ DexFile::kDexTypeAnnotationsDirectoryItem, 16 }
};
- std::vector<SectionColor> table_;
+ const dex_ir::Collections& collections_;
FILE* out_file_;
+ std::vector<const FileSection*> sorted_sections_;
DISALLOW_COPY_AND_ASSIGN(Dumper);
};
@@ -350,7 +377,11 @@
const DexFile* dex_file,
size_t dex_file_index,
ProfileCompilationInfo* profile_info) {
- std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections(), dex_file_index));
+ std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections()));
+ if (!dumper->OpenAndPrintHeader(dex_file_index)) {
+ fprintf(stderr, "Could not open output file.\n");
+ return;
+ }
const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) {
@@ -401,4 +432,22 @@
} // for
}
+/*
+ * Dumps the offset and size of sections within the file.
+ */
+void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) {
+ // Compute the (multidex) class file name).
+ fprintf(stdout, "%s\n", MultidexName("classes", dex_file_index, ".dex").c_str());
+ fprintf(stdout, "section offset items\n");
+ const dex_ir::Collections& collections = header->GetCollections();
+ std::vector<const FileSection*> sorted_sections(GetSortedSections(collections, kSortAscending));
+ for (const FileSection* file_section : sorted_sections) {
+ fprintf(stdout, "%-10s 0x%08x 0x%08x\n",
+ file_section->name_.c_str(),
+ file_section->offset_fn_(collections),
+ file_section->size_fn_(collections));
+ }
+ fprintf(stdout, "\n");
+}
+
} // namespace art
diff --git a/dexlayout/dex_visualize.h b/dexlayout/dex_visualize.h
index 09f8306..a1aa2cd 100644
--- a/dexlayout/dex_visualize.h
+++ b/dexlayout/dex_visualize.h
@@ -38,6 +38,8 @@
size_t dex_file_index,
ProfileCompilationInfo* profile_info);
+void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index);
+
} // namespace art
#endif // ART_DEXLAYOUT_DEX_VISUALIZE_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 4aa8b82..a310424 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -34,7 +34,9 @@
#include "dex_ir_builder.h"
#include "dex_file-inl.h"
+#include "dex_file_verifier.h"
#include "dex_instruction-inl.h"
+#include "dex_verify.h"
#include "dex_visualize.h"
#include "dex_writer.h"
#include "jit/profile_compilation_info.h"
@@ -1691,7 +1693,8 @@
header_->SetFileSize(header_->FileSize() + diff);
}
-void DexLayout::OutputDexFile(const std::string& dex_file_location) {
+void DexLayout::OutputDexFile(const DexFile* dex_file) {
+ const std::string& dex_file_location = dex_file->GetLocation();
std::string error_msg;
std::unique_ptr<File> new_file;
if (!options_.output_to_memmap_) {
@@ -1724,6 +1727,25 @@
if (new_file != nullptr) {
UNUSED(new_file->FlushCloseOrErase());
}
+ // Verify the output dex file's structure for debug builds.
+ if (kIsDebugBuild) {
+ std::string location = "memory mapped file for " + dex_file_location;
+ std::unique_ptr<const DexFile> output_dex_file(DexFile::Open(mem_map_->Begin(),
+ mem_map_->Size(),
+ location,
+ header_->Checksum(),
+ /*oat_dex_file*/ nullptr,
+ /*verify*/ true,
+ /*verify_checksum*/ false,
+ &error_msg));
+ DCHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg;
+ }
+ // Do IR-level comparison between input and output. This check ignores potential differences
+ // due to layout, so offsets are not checked. Instead, it checks the data contents of each item.
+ if (options_.verify_output_) {
+ std::unique_ptr<dex_ir::Header> orig_header(dex_ir::DexIrBuilder(*dex_file));
+ CHECK(VerifyOutputDexFile(orig_header.get(), header_, &error_msg)) << error_msg;
+ }
}
/*
@@ -1745,6 +1767,11 @@
return;
}
+ if (options_.show_section_statistics_) {
+ ShowDexSectionStatistics(header_, dex_file_index);
+ return;
+ }
+
// Dump dex file.
if (options_.dump_) {
DumpDexFile();
@@ -1755,7 +1782,7 @@
if (info_ != nullptr) {
LayoutOutputFile(dex_file);
}
- OutputDexFile(dex_file->GetLocation());
+ OutputDexFile(dex_file);
}
}
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 3918706..f26b423 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -56,7 +56,9 @@
bool show_annotations_ = false;
bool show_file_headers_ = false;
bool show_section_headers_ = false;
+ bool show_section_statistics_ = false;
bool verbose_ = false;
+ bool verify_output_ = false;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
@@ -114,7 +116,7 @@
// Creates a new layout for the dex file based on profile info.
// Currently reorders ClassDefs, ClassDataItems, and CodeItems.
void LayoutOutputFile(const DexFile* dex_file);
- void OutputDexFile(const std::string& dex_file_location);
+ void OutputDexFile(const DexFile* dex_file);
void DumpCFG(const DexFile* dex_file, int idx);
void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code);
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index ad599ae..38faf96 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,7 +44,7 @@
static void Usage(void) {
fprintf(stderr, "Copyright (C) 2016 The Android Open Source Project\n\n");
fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
- " [-s] [-w directory] dexfile...\n\n", kProgramName);
+ " [-s] [-t] [-v] [-w directory] dexfile...\n\n", kProgramName);
fprintf(stderr, " -a : display annotations\n");
fprintf(stderr, " -b : build dex_ir\n");
fprintf(stderr, " -c : verify checksum and exit\n");
@@ -57,6 +57,8 @@
fprintf(stderr, " -o : output file name (defaults to stdout)\n");
fprintf(stderr, " -p : profile file name (defaults to no profile)\n");
fprintf(stderr, " -s : visualize reference pattern\n");
+ fprintf(stderr, " -t : display file section sizes\n");
+ fprintf(stderr, " -v : verify output file is canonical to input (IR level comparison)\n");
fprintf(stderr, " -w : output dex directory \n");
}
@@ -75,7 +77,7 @@
// Parse all arguments.
while (1) {
- const int ic = getopt(argc, argv, "abcdefghil:mo:p:sw:");
+ const int ic = getopt(argc, argv, "abcdefghil:mo:p:stvw:");
if (ic < 0) {
break; // done
}
@@ -127,6 +129,13 @@
options.visualize_pattern_ = true;
options.verbose_ = false;
break;
+ case 't': // display section statistics
+ options.show_section_statistics_ = true;
+ options.verbose_ = false;
+ break;
+ case 'v': // verify output
+ options.verify_output_ = true;
+ break;
case 'w': // output dex files directory
options.output_dex_directory_ = optarg;
break;
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 1f6b874..fc6c18b 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -43,6 +43,7 @@
static const uint32_t kOpcodeShift = 26;
static const uint32_t kCop1 = (17 << kOpcodeShift);
+static const uint32_t kMsa = (30 << kOpcodeShift); // MSA major opcode.
static const uint32_t kITypeMask = (0x3f << kOpcodeShift);
static const uint32_t kJTypeMask = (0x3f << kOpcodeShift);
@@ -51,6 +52,8 @@
static const uint32_t kSpecial2Mask = (0x3f << kOpcodeShift);
static const uint32_t kSpecial3Mask = (0x3f << kOpcodeShift);
static const uint32_t kFpMask = kRTypeMask;
+static const uint32_t kMsaMask = kRTypeMask;
+static const uint32_t kMsaSpecialMask = (0x3f << kOpcodeShift);
static const MipsInstruction gMipsInstructions[] = {
// "sll r0, r0, 0" is the canonical "nop", used in delay slots.
@@ -417,6 +420,36 @@
{ kFpMask, kCop1 | 0x10, "sel", "fadt" },
{ kFpMask, kCop1 | 0x1e, "max", "fadt" },
{ kFpMask, kCop1 | 0x1c, "min", "fadt" },
+
+ // MSA instructions.
+ { kMsaMask | (0x1f << 21), kMsa | (0x0 << 21) | 0x1e, "and.v", "kmn" },
+ { kMsaMask | (0x1f << 21), kMsa | (0x1 << 21) | 0x1e, "or.v", "kmn" },
+ { kMsaMask | (0x1f << 21), kMsa | (0x2 << 21) | 0x1e, "nor.v", "kmn" },
+ { kMsaMask | (0x1f << 21), kMsa | (0x3 << 21) | 0x1e, "xor.v", "kmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x0 << 23) | 0xe, "addv", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0xe, "subv", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x0 << 23) | 0x12, "mulv", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x4 << 23) | 0x12, "div_s", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x5 << 23) | 0x12, "div_u", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x6 << 23) | 0x12, "mod_s", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x7 << 23) | 0x12, "mod_u", "Vkmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x0 << 22) | 0x1b, "fadd", "Ukmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x1 << 22) | 0x1b, "fsub", "Ukmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x2 << 22) | 0x1b, "fmul", "Ukmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x3 << 22) | 0x1b, "fdiv", "Ukmn" },
+ { kMsaMask | (0x1ff << 17), kMsa | (0x19e << 17) | 0x1e, "ffint_s", "ukm" },
+ { kMsaMask | (0x1ff << 17), kMsa | (0x19c << 17) | 0x1e, "ftint_s", "ukm" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x0 << 23) | 0xd, "sll", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0xd, "sra", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0xd, "srl", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x0 << 23) | 0x9, "slli", "kmW" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0x9, "srai", "kmW" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0x9, "srli", "kmW" },
+ { kMsaMask | (0x3ff << 16), kMsa | (0xbe << 16) | 0x19, "move.v", "km" },
+ { kMsaMask | (0xf << 22), kMsa | (0x1 << 22) | 0x19, "splati", "kX" },
+ { kMsaMask | (0xff << 18), kMsa | (0xc0 << 18) | 0x1e, "fill", "vkD" },
+ { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
+ { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
};
static uint32_t ReadU32(const uint8_t* ptr) {
@@ -559,6 +592,111 @@
case 't': args << 'f' << rt; break;
case 'Z': args << (rd + 1); break; // sz ([d]ext size).
case 'z': args << (rd - sa + 1); break; // sz ([d]ins, dinsu size).
+ case 'k': args << 'w' << sa; break;
+ case 'm': args << 'w' << rd; break;
+ case 'n': args << 'w' << rt; break;
+ case 'U': // MSA 1-bit df (word/doubleword), position 21.
+ {
+ int32_t df = (instruction >> 21) & 0x1;
+ switch (df) {
+ case 0: opcode += ".w"; break;
+ case 1: opcode += ".d"; break;
+ }
+ continue; // No ", ".
+ }
+ case 'u': // MSA 1-bit df (word/doubleword), position 16.
+ {
+ int32_t df = (instruction >> 16) & 0x1;
+ switch (df) {
+ case 0: opcode += ".w"; break;
+ case 1: opcode += ".d"; break;
+ }
+ continue; // No ", ".
+ }
+ case 'V': // MSA 2-bit df, position 21.
+ {
+ int32_t df = (instruction >> 21) & 0x3;
+ switch (df) {
+ case 0: opcode += ".b"; break;
+ case 1: opcode += ".h"; break;
+ case 2: opcode += ".w"; break;
+ case 3: opcode += ".d"; break;
+ }
+ continue; // No ", ".
+ }
+ case 'v': // MSA 2-bit df, position 16.
+ {
+ int32_t df = (instruction >> 16) & 0x3;
+ switch (df) {
+ case 0: opcode += ".b"; break;
+ case 1: opcode += ".h"; break;
+ case 2: opcode += ".w"; break;
+ case 3: opcode += ".d"; break;
+ }
+ continue; // No ", ".
+ }
+ case 'W': // MSA df/m.
+ {
+ int32_t df_m = (instruction >> 16) & 0x7f;
+ if ((df_m & (0x1 << 6)) == 0) {
+ opcode += ".d";
+ args << (df_m & 0x3f);
+ break;
+ }
+ if ((df_m & (0x1 << 5)) == 0) {
+ opcode += ".w";
+ args << (df_m & 0x1f);
+ break;
+ }
+ if ((df_m & (0x1 << 4)) == 0) {
+ opcode += ".h";
+ args << (df_m & 0xf);
+ break;
+ }
+ if ((df_m & (0x1 << 3)) == 0) {
+ opcode += ".b";
+ args << (df_m & 0x7);
+ }
+ break;
+ }
+ case 'w': // MSA +x(rs).
+ {
+ int32_t df = instruction & 0x3;
+ int32_t s10 = (instruction >> 16) & 0x3ff;
+ s10 -= (s10 & 0x200) << 1; // Sign-extend s10.
+ switch (df) {
+ case 0: opcode += ".b"; break;
+ case 1: opcode += ".h"; break;
+ case 2: opcode += ".w"; break;
+ case 3: opcode += ".d"; break;
+ }
+ args << StringPrintf("%+d(r%d)", s10 << df, rd);
+ break;
+ }
+ case 'X': // MSA df/n - ws[x].
+ {
+ int32_t df_n = (instruction >> 16) & 0x3f;
+ if ((df_n & (0x3 << 4)) == 0) {
+ opcode += ".b";
+ args << 'w' << rd << '[' << (df_n & 0xf) << ']';
+ break;
+ }
+ if ((df_n & (0x3 << 3)) == 0) {
+ opcode += ".h";
+ args << 'w' << rd << '[' << (df_n & 0x7) << ']';
+ break;
+ }
+ if ((df_n & (0x3 << 2)) == 0) {
+ opcode += ".w";
+ args << 'w' << rd << '[' << (df_n & 0x3) << ']';
+ break;
+ }
+ if ((df_n & (0x3 << 1)) == 0) {
+ opcode += ".d";
+ args << 'w' << rd << '[' << (df_n & 0x1) << ']';
+ }
+ break;
+ }
}
if (*(args_fmt + 1)) {
args << ", ";
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index becb827..e767023 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -2210,13 +2210,13 @@
ScopedIndentation indent2(&state->vios_);
auto* resolved_fields = dex_cache->GetResolvedFields();
for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
- auto* elem = mirror::DexCache::GetElementPtrSize(
- resolved_fields, i, image_pointer_size);
+ auto* elem = mirror::DexCache::GetNativePairPtrSize(
+ resolved_fields, i, image_pointer_size).object;
size_t run = 0;
for (size_t j = i + 1;
- j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_fields,
- j,
- image_pointer_size);
+ j != length &&
+ elem == mirror::DexCache::GetNativePairPtrSize(
+ resolved_fields, j, image_pointer_size).object;
++j) {
++run;
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 18a6670..dfaae7d 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -534,17 +534,18 @@
mirror::DexCache::SetElementPtrSize(copy_methods, j, copy, pointer_size);
}
}
- ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
- ArtField** relocated_fields = RelocatedAddressOfPointer(orig_fields);
+ mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields();
+ mirror::FieldDexCacheType* relocated_fields = RelocatedAddressOfPointer(orig_fields);
copy_dex_cache->SetField64<false>(
mirror::DexCache::ResolvedFieldsOffset(),
static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_fields)));
if (orig_fields != nullptr) {
- ArtField** copy_fields = RelocatedCopyOf(orig_fields);
+ mirror::FieldDexCacheType* copy_fields = RelocatedCopyOf(orig_fields);
for (size_t j = 0, num = orig_dex_cache->NumResolvedFields(); j != num; ++j) {
- ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, j, pointer_size);
- ArtField* copy = RelocatedAddressOfPointer(orig);
- mirror::DexCache::SetElementPtrSize(copy_fields, j, copy, pointer_size);
+ mirror::FieldDexCachePair orig =
+ mirror::DexCache::GetNativePairPtrSize(orig_fields, j, pointer_size);
+ mirror::FieldDexCachePair copy(RelocatedAddressOfPointer(orig.object), orig.index);
+ mirror::DexCache::SetNativePairPtrSize(copy_fields, j, copy, pointer_size);
}
}
mirror::MethodTypeDexCacheType* orig_method_types = orig_dex_cache->GetResolvedMethodTypes();
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index daa2dff..923ff4f 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -47,24 +47,6 @@
return instr_size;
}
-void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
- // Note that in this handler we set up the registers and return to
- // longjmp directly rather than going through an assembly language stub. The
- // reason for this is that longjmp is (currently) in ARM mode and that would
- // require switching modes in the stub - incurring an unwanted relocation.
-
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- Thread* self = Thread::Current();
- CHECK(self != nullptr); // This will cause a SIGABRT if self is null.
-
- sc->arm_r0 = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
- sc->arm_r1 = 1;
- sc->arm_pc = reinterpret_cast<uintptr_t>(longjmp);
- VLOG(signals) << "longjmp address: " << reinterpret_cast<void*>(sc->arm_pc);
-}
-
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context,
ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index c02be87..193af58 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -39,21 +39,6 @@
namespace art {
-void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
- // To match the case used in ARM we return directly to the longjmp function
- // rather than through a trivial assembly language stub.
-
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- Thread* self = Thread::Current();
- CHECK(self != nullptr); // This will cause a SIGABRT if self is null.
-
- sc->regs[0] = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
- sc->regs[1] = 1;
- sc->pc = reinterpret_cast<uintptr_t>(longjmp);
-}
-
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context,
ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index 1792f31..f9c19e8 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -35,10 +35,6 @@
namespace art {
-void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context ATTRIBUTE_UNUSED) {
-}
-
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
index 709cab5..d668d3a 100644
--- a/runtime/arch/mips64/fault_handler_mips64.cc
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -35,10 +35,6 @@
namespace art {
-void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context ATTRIBUTE_UNUSED) {
-}
-
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/mips64/registers_mips64.cc b/runtime/arch/mips64/registers_mips64.cc
index 4959208..1ee2cdd 100644
--- a/runtime/arch/mips64/registers_mips64.cc
+++ b/runtime/arch/mips64/registers_mips64.cc
@@ -46,5 +46,14 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, const VectorRegister& rhs) {
+ if (rhs >= W0 && rhs < kNumberOfVectorRegisters) {
+ os << "w" << static_cast<int>(rhs);
+ } else {
+ os << "VectorRegister[" << static_cast<int>(rhs) << "]";
+ }
+ return os;
+}
+
} // namespace mips64
} // namespace art
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
index 81fae72..30de2cc 100644
--- a/runtime/arch/mips64/registers_mips64.h
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -107,6 +107,45 @@
};
std::ostream& operator<<(std::ostream& os, const FpuRegister& rhs);
+// Values for vector registers.
+enum VectorRegister {
+ W0 = 0,
+ W1 = 1,
+ W2 = 2,
+ W3 = 3,
+ W4 = 4,
+ W5 = 5,
+ W6 = 6,
+ W7 = 7,
+ W8 = 8,
+ W9 = 9,
+ W10 = 10,
+ W11 = 11,
+ W12 = 12,
+ W13 = 13,
+ W14 = 14,
+ W15 = 15,
+ W16 = 16,
+ W17 = 17,
+ W18 = 18,
+ W19 = 19,
+ W20 = 20,
+ W21 = 21,
+ W22 = 22,
+ W23 = 23,
+ W24 = 24,
+ W25 = 25,
+ W26 = 26,
+ W27 = 27,
+ W28 = 28,
+ W29 = 29,
+ W30 = 30,
+ W31 = 31,
+ kNumberOfVectorRegisters = 32,
+ kNoVectorRegister = -1,
+};
+std::ostream& operator<<(std::ostream& os, const VectorRegister& rhs);
+
} // namespace mips64
} // namespace art
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index a4d6bb4..f407ebf 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -75,12 +75,6 @@
extern "C" void art_quick_throw_stack_overflow();
extern "C" void art_quick_test_suspend();
-// Note this is different from the others (no underscore on 64 bit mac) due to
-// the way the symbol is defined in the .S file.
-// TODO: fix the symbols for 64 bit mac - there is a double underscore prefix for some
-// of them.
-extern "C" void art_nested_signal_return();
-
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
static uint32_t GetInstructionSize(const uint8_t* pc) {
@@ -247,21 +241,6 @@
return pc - startpc;
}
-void FaultManager::HandleNestedSignal(int, siginfo_t*, void* context) {
- // For the Intel architectures we need to go to an assembly language
- // stub. This is because the 32 bit call to longjmp is much different
- // from the 64 bit ABI call and pushing things onto the stack inside this
- // handler was unwieldy and ugly. The use of the stub means we can keep
- // this code the same for both 32 and 64 bit.
-
- Thread* self = Thread::Current();
- CHECK(self != nullptr); // This will cause a SIGABRT if self is null.
-
- struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
- uc->CTX_JMP_BUF = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
- uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_nested_signal_return);
-}
-
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index ff7ba92..5f38dc8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2137,19 +2137,6 @@
ret
END_FUNCTION art_quick_string_compareto
-// Return from a nested signal:
-// Entry:
-// eax: address of jmp_buf in TLS
-
-DEFINE_FUNCTION art_nested_signal_return
- SETUP_GOT_NOSAVE ebx // sets %ebx for call into PLT
- movl LITERAL(1), %ecx
- PUSH ecx // second arg to longjmp (1)
- PUSH eax // first arg to longjmp (jmp_buf)
- call PLT_SYMBOL(longjmp)
- UNREACHABLE
-END_FUNCTION art_nested_signal_return
-
// Create a function `name` calling the ReadBarrier::Mark routine,
// getting its argument and returning its result through register
// `reg`, saving and restoring all caller-save registers.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 8a663d1..e87b165 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2100,18 +2100,6 @@
ret
END_FUNCTION art_quick_instance_of
-
-// Return from a nested signal:
-// Entry:
-// rdi: address of jmp_buf in TLS
-
-DEFINE_FUNCTION art_nested_signal_return
- // first arg to longjmp is already in correct register
- movq LITERAL(1), %rsi // second arg to longjmp (1)
- call PLT_SYMBOL(longjmp)
- UNREACHABLE
-END_FUNCTION art_nested_signal_return
-
// Create a function `name` calling the ReadBarrier::Mark routine,
// getting its argument and returning its result through register
// `reg`, saving and restoring all caller-save registers.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 685e26c..b47f8f0 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -347,7 +347,11 @@
inline uint16_t ArtMethod::GetClassDefIndex() {
DCHECK(!IsProxyMethod());
- return GetDeclaringClass()->GetDexClassDefIndex();
+ if (LIKELY(!IsObsolete())) {
+ return GetDeclaringClass()->GetDexClassDefIndex();
+ } else {
+ return FindObsoleteDexClassDefIndex();
+ }
}
inline const DexFile::ClassDef& ArtMethod::GetClassDef() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 9d74e7c..80a8773 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -104,6 +104,16 @@
UNREACHABLE();
}
+uint16_t ArtMethod::FindObsoleteDexClassDefIndex() {
+ DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod();
+ DCHECK(IsObsolete());
+ const DexFile* dex_file = GetDexFile();
+ const dex::TypeIndex declaring_class_type = dex_file->GetMethodId(GetDexMethodIndex()).class_idx_;
+ const DexFile::ClassDef* class_def = dex_file->FindClassDef(declaring_class_type);
+ CHECK(class_def != nullptr);
+ return dex_file->GetIndexForClassDef(*class_def);
+}
+
mirror::String* ArtMethod::GetNameAsString(Thread* self) {
CHECK(!IsProxyMethod());
StackHandleScope<1> hs(self);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index cd1950c..2248c3b 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -700,6 +700,8 @@
} ptr_sized_fields_;
private:
+ uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_);
+
bool IsAnnotatedWith(jclass klass, uint32_t visibility);
static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) {
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index db43319..5aede38 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -15,6 +15,7 @@
*/
#include <algorithm>
+#include <cstddef>
#include <iomanip>
#include <numeric>
@@ -27,7 +28,7 @@
namespace art {
-static constexpr size_t kMemoryToolRedZoneBytes = 8;
+constexpr size_t kMemoryToolRedZoneBytes = 8;
constexpr size_t Arena::kDefaultSize;
template <bool kCount>
@@ -168,23 +169,75 @@
Arena::Arena() : bytes_allocated_(0), next_(nullptr) {
}
+class MallocArena FINAL : public Arena {
+ public:
+ explicit MallocArena(size_t size = Arena::kDefaultSize);
+ virtual ~MallocArena();
+ private:
+ static constexpr size_t RequiredOverallocation() {
+ return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
+ ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
+ : 0u;
+ }
+
+ uint8_t* unaligned_memory_;
+};
+
MallocArena::MallocArena(size_t size) {
- memory_ = reinterpret_cast<uint8_t*>(calloc(1, size));
- CHECK(memory_ != nullptr); // Abort on OOM.
- DCHECK_ALIGNED(memory_, ArenaAllocator::kAlignment);
+ // We need to guarantee kArenaAlignment aligned allocation for the new arena.
+ // TODO: Use std::aligned_alloc() when it becomes available with C++17.
+ constexpr size_t overallocation = RequiredOverallocation();
+ unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
+ CHECK(unaligned_memory_ != nullptr); // Abort on OOM.
+ DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
+ if (overallocation == 0u) {
+ memory_ = unaligned_memory_;
+ } else {
+ memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
+ if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ size_t head = memory_ - unaligned_memory_;
+ size_t tail = overallocation - head;
+ MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
+ MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
+ }
+ }
+ DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
size_ = size;
}
MallocArena::~MallocArena() {
- free(reinterpret_cast<void*>(memory_));
+ constexpr size_t overallocation = RequiredOverallocation();
+ if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ size_t head = memory_ - unaligned_memory_;
+ size_t tail = overallocation - head;
+ MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
+ MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
+ }
+ free(reinterpret_cast<void*>(unaligned_memory_));
}
+class MemMapArena FINAL : public Arena {
+ public:
+ MemMapArena(size_t size, bool low_4gb, const char* name);
+ virtual ~MemMapArena();
+ void Release() OVERRIDE;
+
+ private:
+ std::unique_ptr<MemMap> map_;
+};
+
MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
+ // Round up to a full page as that's the smallest unit of allocation for mmap()
+ // and we want to be able to use all memory that we actually allocate.
+ size = RoundUp(size, kPageSize);
std::string error_msg;
map_.reset(MemMap::MapAnonymous(
name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
CHECK(map_.get() != nullptr) << error_msg;
memory_ = map_->Begin();
+ static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
+ "Arena should not need stronger alignment than kPageSize.");
+ DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
size_ = map_->Size();
}
@@ -332,20 +385,7 @@
ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
uint8_t* ret;
if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
- ret = AllocFromNewArena(rounded_bytes);
- uint8_t* noaccess_begin = ret + bytes;
- uint8_t* noaccess_end;
- if (ret == arena_head_->Begin()) {
- DCHECK(ptr_ - rounded_bytes == ret);
- noaccess_end = end_;
- } else {
- // We're still using the old arena but `ret` comes from a new one just after it.
- DCHECK(arena_head_->next_ != nullptr);
- DCHECK(ret == arena_head_->next_->Begin());
- DCHECK_EQ(rounded_bytes, arena_head_->next_->GetBytesAllocated());
- noaccess_end = arena_head_->next_->End();
- }
- MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
+ ret = AllocFromNewArenaWithMemoryTool(rounded_bytes);
} else {
ret = ptr_;
ptr_ += rounded_bytes;
@@ -356,6 +396,30 @@
return ret;
}
+void* ArenaAllocator::AllocWithMemoryToolAlign16(size_t bytes, ArenaAllocKind kind) {
+ // We mark all memory for a newly retrieved arena as inaccessible and then
+ // mark only the actually allocated memory as defined. That leaves red zones
+ // and padding between allocations marked as inaccessible.
+ size_t rounded_bytes = bytes + kMemoryToolRedZoneBytes;
+ DCHECK_ALIGNED(rounded_bytes, 8); // `bytes` is 16-byte aligned, red zone is 8-byte aligned.
+ uintptr_t padding =
+ ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_);
+ ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
+ uint8_t* ret;
+ if (UNLIKELY(padding + rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
+ static_assert(kArenaAlignment >= 16, "Expecting sufficient alignment for new Arena.");
+ ret = AllocFromNewArenaWithMemoryTool(rounded_bytes);
+ } else {
+ ptr_ += padding; // Leave padding inaccessible.
+ ret = ptr_;
+ ptr_ += rounded_bytes;
+ }
+ MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
+ // Check that the memory is already zeroed out.
+ DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
+ return ret;
+}
+
ArenaAllocator::~ArenaAllocator() {
// Reclaim all the arenas by giving them back to the thread pool.
UpdateBytesAllocated();
@@ -386,6 +450,24 @@
return new_arena->Begin();
}
+uint8_t* ArenaAllocator::AllocFromNewArenaWithMemoryTool(size_t bytes) {
+ uint8_t* ret = AllocFromNewArena(bytes);
+ uint8_t* noaccess_begin = ret + bytes;
+ uint8_t* noaccess_end;
+ if (ret == arena_head_->Begin()) {
+ DCHECK(ptr_ - bytes == ret);
+ noaccess_end = end_;
+ } else {
+ // We're still using the old arena but `ret` comes from a new one just after it.
+ DCHECK(arena_head_->next_ != nullptr);
+ DCHECK(ret == arena_head_->next_->Begin());
+ DCHECK_EQ(bytes, arena_head_->next_->GetBytesAllocated());
+ noaccess_end = arena_head_->next_->End();
+ }
+ MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
+ return ret;
+}
+
bool ArenaAllocator::Contains(const void* ptr) const {
if (ptr >= begin_ && ptr < end_) {
return true;
@@ -398,7 +480,9 @@
return false;
}
-MemStats::MemStats(const char* name, const ArenaAllocatorStats* stats, const Arena* first_arena,
+MemStats::MemStats(const char* name,
+ const ArenaAllocatorStats* stats,
+ const Arena* first_arena,
ssize_t lost_bytes_adjustment)
: name_(name),
stats_(stats),
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index f92fbea..c39429c 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -34,7 +34,6 @@
class ArenaAllocator;
class ArenaStack;
class ScopedArenaAllocator;
-class MemMap;
class MemStats;
template <typename T>
@@ -244,22 +243,6 @@
DISALLOW_COPY_AND_ASSIGN(Arena);
};
-class MallocArena FINAL : public Arena {
- public:
- explicit MallocArena(size_t size = Arena::kDefaultSize);
- virtual ~MallocArena();
-};
-
-class MemMapArena FINAL : public Arena {
- public:
- MemMapArena(size_t size, bool low_4gb, const char* name);
- virtual ~MemMapArena();
- void Release() OVERRIDE;
-
- private:
- std::unique_ptr<MemMap> map_;
-};
-
class ArenaPool {
public:
explicit ArenaPool(bool use_malloc = true,
@@ -319,8 +302,31 @@
return ret;
}
+ // Returns zeroed memory.
+ void* AllocAlign16(size_t bytes, ArenaAllocKind kind = kArenaAllocMisc) ALWAYS_INLINE {
+ // It is an error to request 16-byte aligned allocation of unaligned size.
+ DCHECK_ALIGNED(bytes, 16);
+ if (UNLIKELY(IsRunningOnMemoryTool())) {
+ return AllocWithMemoryToolAlign16(bytes, kind);
+ }
+ uintptr_t padding =
+ ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_);
+ ArenaAllocatorStats::RecordAlloc(bytes, kind);
+ if (UNLIKELY(padding + bytes > static_cast<size_t>(end_ - ptr_))) {
+ static_assert(kArenaAlignment >= 16, "Expecting sufficient alignment for new Arena.");
+ return AllocFromNewArena(bytes);
+ }
+ ptr_ += padding;
+ uint8_t* ret = ptr_;
+ DCHECK_ALIGNED(ret, 16);
+ ptr_ += bytes;
+ return ret;
+ }
+
// Realloc never frees the input pointer, it is the caller's job to do this if necessary.
- void* Realloc(void* ptr, size_t ptr_size, size_t new_size,
+ void* Realloc(void* ptr,
+ size_t ptr_size,
+ size_t new_size,
ArenaAllocKind kind = kArenaAllocMisc) ALWAYS_INLINE {
DCHECK_GE(new_size, ptr_size);
DCHECK_EQ(ptr == nullptr, ptr_size == 0u);
@@ -371,12 +377,17 @@
bool Contains(const void* ptr) const;
- static constexpr size_t kAlignment = 8;
+ // The alignment guaranteed for individual allocations.
+ static constexpr size_t kAlignment = 8u;
+
+ // The alignment required for the whole Arena rather than individual allocations.
+ static constexpr size_t kArenaAlignment = 16u;
private:
void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
+ void* AllocWithMemoryToolAlign16(size_t bytes, ArenaAllocKind kind);
uint8_t* AllocFromNewArena(size_t bytes);
-
+ uint8_t* AllocFromNewArenaWithMemoryTool(size_t bytes);
void UpdateBytesAllocated();
@@ -396,7 +407,9 @@
class MemStats {
public:
- MemStats(const char* name, const ArenaAllocatorStats* stats, const Arena* first_arena,
+ MemStats(const char* name,
+ const ArenaAllocatorStats* stats,
+ const Arena* first_arena,
ssize_t lost_bytes_adjustment = 0);
void Dump(std::ostream& os) const;
diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h
index 55044b3..1a0eb5e 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/runtime/base/scoped_arena_allocator.h
@@ -39,8 +39,6 @@
kFree,
};
-static constexpr size_t kArenaAlignment = 8;
-
// Holds a list of Arenas for use by ScopedArenaAllocator stack.
// The memory is returned to the ArenaPool when the ArenaStack is destroyed.
class ArenaStack : private DebugStackRefCounter, private ArenaAllocatorMemoryTool {
@@ -67,6 +65,9 @@
return *(reinterpret_cast<ArenaFreeTag*>(ptr) - 1);
}
+ // The alignment guaranteed for individual allocations.
+ static constexpr size_t kAlignment = 8u;
+
private:
struct Peak;
struct Current;
@@ -89,8 +90,8 @@
if (UNLIKELY(IsRunningOnMemoryTool())) {
return AllocWithMemoryTool(bytes, kind);
}
- // Add kArenaAlignment for the free or used tag. Required to preserve alignment.
- size_t rounded_bytes = RoundUp(bytes + (kIsDebugBuild ? kArenaAlignment : 0u), kArenaAlignment);
+ // Add kAlignment for the free or used tag. Required to preserve alignment.
+ size_t rounded_bytes = RoundUp(bytes + (kIsDebugBuild ? kAlignment : 0u), kAlignment);
uint8_t* ptr = top_ptr_;
if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) {
ptr = AllocateFromNextArena(rounded_bytes);
@@ -98,7 +99,7 @@
CurrentStats()->RecordAlloc(bytes, kind);
top_ptr_ = ptr + rounded_bytes;
if (kIsDebugBuild) {
- ptr += kArenaAlignment;
+ ptr += kAlignment;
ArenaTagForAllocation(ptr) = ArenaFreeTag::kUsed;
}
return ptr;
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index bd510ca..9ddc6cf 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -161,9 +161,15 @@
return resolved_method;
}
-inline ArtField* ClassLinker::GetResolvedField(uint32_t field_idx,
- ObjPtr<mirror::DexCache> dex_cache) {
- return dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
+ ArtMethod* referrer,
+ bool is_static) {
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ ArtField* field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+ if (field == nullptr) {
+ field = LookupResolvedField(field_idx, dex_cache, referrer->GetClassLoader(), is_static);
+ }
+ return field;
}
inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
@@ -171,7 +177,8 @@
bool is_static) {
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- ArtField* resolved_field = GetResolvedField(field_idx, referrer->GetDexCache());
+ ArtField* resolved_field =
+ referrer->GetDexCache()->GetResolvedField(field_idx, image_pointer_size_);
if (UNLIKELY(resolved_field == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b611aa2..eb7d7bd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -906,7 +906,6 @@
runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
DCHECK(!oat_files.empty());
const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
- CHECK_EQ(default_oat_header.GetImageFileLocationOatChecksum(), 0U);
CHECK_EQ(default_oat_header.GetImageFileLocationOatDataBegin(), 0U);
const char* image_file_location = oat_files[0]->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
@@ -1163,9 +1162,7 @@
for (ObjPtr<mirror::Class> klass : classes_) {
for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr)
- << klass->PrettyDescriptor() << " iface #" << i
- << klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i))
- << " Bug: 34839984";
+ << klass->PrettyDescriptor() << " iface #" << i;
}
}
}
@@ -1282,7 +1279,10 @@
num_types = dex_file->NumTypeIds();
}
const size_t num_methods = dex_file->NumMethodIds();
- const size_t num_fields = dex_file->NumFieldIds();
+ size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize;
+ if (dex_file->NumFieldIds() < num_fields) {
+ num_fields = dex_file->NumFieldIds();
+ }
size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
if (dex_file->NumProtoIds() < num_method_types) {
num_method_types = dex_file->NumProtoIds();
@@ -1326,17 +1326,22 @@
dex_cache->SetResolvedMethods(methods);
}
if (num_fields != 0u) {
- ArtField** const fields =
- reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
- DCHECK(fields[j] == nullptr);
+ mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields();
+ mirror::FieldDexCacheType* const fields =
+ reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
+ for (size_t j = 0; j < num_fields; ++j) {
+ DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size_).index,
+ 0u);
+ DCHECK(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size_).object ==
+ nullptr);
+ mirror::DexCache::SetNativePairPtrSize(
+ fields,
+ j,
+ mirror::DexCache::GetNativePairPtrSize(image_resolved_fields,
+ j,
+ image_pointer_size_),
+ image_pointer_size_);
}
- CopyNonNull(dex_cache->GetResolvedFields(),
- num_fields,
- fields,
- [] (const ArtField* field) {
- return field == nullptr;
- });
dex_cache->SetResolvedFields(fields);
}
if (num_method_types != 0u) {
@@ -1913,12 +1918,22 @@
// Since it ensures classes are in the class table.
VerifyClassInTableArtMethodVisitor visitor2(class_table);
header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize);
- }
- if (app_image) {
- // TODO: Restrict this check to debug builds. Bug: 34839984
+ // Verify that all direct interfaces of classes in the class table are also resolved.
VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get());
class_table->Visit(visitor);
visitor.Check();
+ // Check that all non-primitive classes in dex caches are also in the class table.
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+ if (klass != nullptr && !klass->IsPrimitive()) {
+ CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor()
+ << " " << dex_cache->GetDexFile()->GetLocation();
+ }
+ }
+ }
}
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
@@ -4538,108 +4553,6 @@
return CanWeInitializeClass(super_class, can_init_statics, can_init_parents);
}
-std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr());
- if (cs != nullptr) {
- if (cs->IsImageSpace()) {
- oss << "image/" << cs->GetName() << "/" << cs->AsImageSpace()->GetImageFilename();
- } else {
- oss << "continuous/" << cs->GetName();
- }
- } else {
- gc::space::DiscontinuousSpace* ds =
- heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
- if (ds != nullptr) {
- oss << "discontinuous/" << ds->GetName();
- } else {
- oss << "invalid";
- }
- }
- return oss.str();
-}
-
-std::string DescribeLoaders(ObjPtr<mirror::Class> klass, const char* iface_descriptor)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- uint32_t hash = ComputeModifiedUtf8Hash(iface_descriptor);
- ScopedObjectAccessUnchecked soa(Thread::Current());
- ObjPtr<mirror::Class> path_class_loader =
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader);
- ObjPtr<mirror::Class> dex_class_loader =
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader);
-
- // Print the class loader chain.
- bool found_iface;
- const char* loader_separator = "";
- for (ObjPtr<mirror::ClassLoader> loader = klass->GetClassLoader();
- loader != nullptr;
- loader = loader->GetParent()) {
- oss << loader_separator << loader->GetClass()->PrettyDescriptor();
- loader_separator = ";";
- // If we didn't find the interface yet, try to find it in the current class loader.
- if (!found_iface) {
- ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
- ObjPtr<mirror::Class> iface =
- (table != nullptr) ? table->Lookup(iface_descriptor, hash) : nullptr;
- if (iface != nullptr) {
- found_iface = true;
- oss << "[hit:" << DescribeSpace(iface) << "]";
- }
- }
-
- // For PathClassLoader or DexClassLoader also dump the dex file locations.
- if (loader->GetClass() == path_class_loader || loader->GetClass() == dex_class_loader) {
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(loader);
- if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
- ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- if (dex_elements_obj != nullptr) {
- ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
- dex_elements_obj->AsObjectArray<mirror::Object>();
- oss << "(";
- const char* path_separator = "";
- for (int32_t i = 0; i != dex_elements->GetLength(); ++i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
- ObjPtr<mirror::Object> dex_file =
- (element != nullptr) ? dex_file_field->GetObject(element) : nullptr;
- ObjPtr<mirror::LongArray> long_array =
- (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr;
- if (long_array != nullptr) {
- int32_t long_array_size = long_array->GetLength();
- // First element is the oat file.
- for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
- const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(
- static_cast<uintptr_t>(long_array->GetWithoutChecks(j)));
- oss << path_separator << cp_dex_file->GetLocation();
- path_separator = ":";
- }
- }
- }
- oss << ")";
- }
- }
- }
- }
-
- // Do a paranoid check that the `klass` itself is in the class table.
- ClassTable* table =
- Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(klass->GetClassLoader());
- ObjPtr<mirror::Class> k = (table != nullptr) ? table->LookupByDescriptor(klass) : nullptr;
- if (k != klass) {
- oss << "{FAIL:" << k.Ptr() << "!=" << klass.Ptr() << "}";
- }
- return oss.str();
-}
-
bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
// see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
@@ -4787,15 +4700,7 @@
MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
for (size_t i = 0; i < num_direct_interfaces; i++) {
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i));
- if (UNLIKELY(handle_scope_iface == nullptr)) {
- const char* iface_descriptor =
- klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i));
- LOG(FATAL) << "Check failed: handle_scope_iface != nullptr "
- << "Debug data for bug 34839984: "
- << klass->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor
- << " space: " << DescribeSpace(klass.Get())
- << " loaders: " << DescribeLoaders(klass.Get(), iface_descriptor);
- }
+ CHECK(handle_scope_iface != nullptr) << klass->PrettyDescriptor() << " iface #" << i;
CHECK(handle_scope_iface->IsInterface());
if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
// We have already done this for this interface. Skip it.
@@ -4931,15 +4836,7 @@
// First we initialize all of iface's super-interfaces recursively.
for (size_t i = 0; i < num_direct_ifaces; i++) {
ObjPtr<mirror::Class> super_iface = mirror::Class::GetDirectInterface(self, iface.Get(), i);
- if (UNLIKELY(super_iface == nullptr)) {
- const char* iface_descriptor =
- iface->GetDexFile().StringByTypeIdx(iface->GetDirectInterfaceTypeIdx(i));
- LOG(FATAL) << "Check failed: super_iface != nullptr "
- << "Debug data for bug 34839984: "
- << iface->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor
- << " space: " << DescribeSpace(iface.Get())
- << " loaders: " << DescribeLoaders(iface.Get(), iface_descriptor);
- }
+ CHECK(super_iface != nullptr) << iface->PrettyDescriptor() << " iface #" << i;
if (!super_iface->HasBeenRecursivelyInitialized()) {
// Recursive step
handle_super_iface.Assign(super_iface);
@@ -8260,6 +8157,43 @@
return resolved;
}
+ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ bool is_static) {
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
+ ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(field_id.class_idx_);
+ if (klass == nullptr) {
+ klass = LookupResolvedType(dex_file, field_id.class_idx_, dex_cache, class_loader);
+ }
+ if (klass == nullptr) {
+ // The class has not been resolved yet, so the field is also unresolved.
+ return nullptr;
+ }
+ DCHECK(klass->IsResolved());
+ Thread* self = is_static ? Thread::Current() : nullptr;
+
+ // First try to find a field declared directly by `klass` by the field index.
+ ArtField* resolved_field = is_static
+ ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx)
+ : klass->FindInstanceField(dex_cache, field_idx);
+
+ if (resolved_field == nullptr) {
+ // If not found in `klass` by field index, search the class hierarchy using the name and type.
+ const char* name = dex_file.GetFieldName(field_id);
+ const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ resolved_field = is_static
+ ? mirror::Class::FindStaticField(self, klass, name, type)
+ : klass->FindInstanceField(name, type);
+ }
+
+ if (resolved_field != nullptr) {
+ dex_cache->SetResolvedField(field_idx, resolved_field, image_pointer_size_);
+ }
+ return resolved_field;
+}
+
ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
@@ -8320,9 +8254,8 @@
return nullptr;
}
- StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
- StringPiece type(dex_file.StringDataByIdx(
- dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+ StringPiece name(dex_file.GetFieldName(field_id));
+ StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr) {
dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a5d26c7..6254acb 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -333,7 +333,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- ArtField* GetResolvedField(uint32_t field_idx, ObjPtr<mirror::DexCache> dex_cache)
+ ArtField* LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -842,6 +842,13 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a field by its field index.
+ ArtField* LookupResolvedField(uint32_t field_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ bool is_static)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
void RegisterDexFileLocked(const DexFile& dex_file,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader)
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index af4f998..374b711 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -55,6 +55,12 @@
return nullptr;
}
+// To take into account http://b/35845221
+#pragma clang diagnostic push
+#if __clang_major__ < 4
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+
mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) {
WriterMutexLock mu(Thread::Current(), lock_);
// Should only be updating latest table.
@@ -80,6 +86,8 @@
return existing;
}
+#pragma clang diagnostic pop
+
size_t ClassTable::CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
const ClassSet& set) const {
size_t count = 0;
@@ -105,6 +113,20 @@
return CountDefiningLoaderClasses(defining_loader, classes_.back());
}
+size_t ClassTable::NumReferencedZygoteClasses() const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ size_t sum = 0;
+ for (size_t i = 0; i < classes_.size() - 1; ++i) {
+ sum += classes_[i].Size();
+ }
+ return sum;
+}
+
+size_t ClassTable::NumReferencedNonZygoteClasses() const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ return classes_.back().Size();
+}
+
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
DescriptorHashPair pair(descriptor, hash);
ReaderMutexLock mu(Thread::Current(), lock_);
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 711eae4..79f5aea 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -144,16 +144,26 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns the number of classes in previous snapshots.
+ // Returns the number of classes in previous snapshots defined by `defining_loader`.
size_t NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns all off the classes in the lastest snapshot.
+ // Returns all off the classes in the lastest snapshot defined by `defining_loader`.
size_t NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns the number of classes in previous snapshots no matter the defining loader.
+ size_t NumReferencedZygoteClasses() const
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns all off the classes in the lastest snapshot no matter the defining loader.
+ size_t NumReferencedNonZygoteClasses() const
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Update a class in the table with the new class. Returns the existing class which was replaced.
mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
REQUIRES(!lock_)
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index d39ea35..6b9654d 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -41,7 +41,80 @@
};
namespace {
-mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass,
+
+// A helper class that contains all the data needed to do annotation lookup.
+class ClassData {
+ public:
+ explicit ClassData(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
+ : ClassData(ScopedNullHandle<mirror::Class>(), // klass
+ method,
+ *method->GetDexFile(),
+ &method->GetClassDef()) {}
+
+ // Requires Scope to be able to create at least 1 handles.
+ template <typename Scope>
+ ClassData(Scope& hs, ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_)
+ : ClassData(hs.NewHandle(field->GetDeclaringClass())) { }
+
+ explicit ClassData(Handle<mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : ClassData(klass, // klass
+ nullptr, // method
+ klass->GetDexFile(),
+ klass->GetClassDef()) {}
+
+ const DexFile& GetDexFile() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return dex_file_;
+ }
+
+ const DexFile::ClassDef* GetClassDef() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return class_def_;
+ }
+
+ ObjPtr<mirror::DexCache> GetDexCache() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (method_ != nullptr) {
+ return method_->GetDexCache();
+ } else {
+ return real_klass_->GetDexCache();
+ }
+ }
+
+ ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (method_ != nullptr) {
+ return method_->GetDeclaringClass()->GetClassLoader();
+ } else {
+ return real_klass_->GetClassLoader();
+ }
+ }
+
+ ObjPtr<mirror::Class> GetRealClass() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (method_ != nullptr) {
+ return method_->GetDeclaringClass();
+ } else {
+ return real_klass_.Get();
+ }
+ }
+
+ private:
+ ClassData(Handle<mirror::Class> klass,
+ ArtMethod* method,
+ const DexFile& dex_file,
+ const DexFile::ClassDef* class_def) REQUIRES_SHARED(Locks::mutator_lock_)
+ : real_klass_(klass),
+ method_(method),
+ dex_file_(dex_file),
+ class_def_(class_def) {
+ DCHECK((method_ == nullptr) || real_klass_.IsNull());
+ }
+
+ Handle<mirror::Class> real_klass_;
+ ArtMethod* method_;
+ const DexFile& dex_file_;
+ const DexFile::ClassDef* class_def_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassData);
+};
+
+mirror::Object* CreateAnnotationMember(const ClassData& klass,
Handle<mirror::Class> annotation_class,
const uint8_t** annotation)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -185,9 +258,8 @@
const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile* dex_file = method->GetDexFile();
- mirror::Class* klass = method->GetDeclaringClass();
const DexFile::AnnotationsDirectoryItem* annotations_dir =
- dex_file->GetAnnotationsDirectory(*klass->GetClassDef());
+ dex_file->GetAnnotationsDirectory(method->GetClassDef());
if (annotations_dir == nullptr) {
return nullptr;
}
@@ -209,9 +281,8 @@
const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile* dex_file = method->GetDexFile();
- mirror::Class* klass = method->GetDeclaringClass();
const DexFile::AnnotationsDirectoryItem* annotations_dir =
- dex_file->GetAnnotationsDirectory(*klass->GetClassDef());
+ dex_file->GetAnnotationsDirectory(method->GetClassDef());
if (annotations_dir == nullptr) {
return nullptr;
}
@@ -230,30 +301,34 @@
return nullptr;
}
-const DexFile::AnnotationSetItem* FindAnnotationSetForClass(Handle<mirror::Class> klass)
+const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
const DexFile::AnnotationsDirectoryItem* annotations_dir =
- dex_file.GetAnnotationsDirectory(*klass->GetClassDef());
+ dex_file.GetAnnotationsDirectory(*klass.GetClassDef());
if (annotations_dir == nullptr) {
return nullptr;
}
return dex_file.GetClassAnnotationSet(annotations_dir);
}
-mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint8_t** annotation)
+mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation)
REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t type_index = DecodeUnsignedLeb128(annotation);
uint32_t size = DecodeUnsignedLeb128(annotation);
Thread* self = Thread::Current();
ScopedObjectAccessUnchecked soa(self);
- StackHandleScope<2> hs(self);
+ StackHandleScope<4> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> annotation_class(hs.NewHandle(
- class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get())));
+ class_linker->ResolveType(klass.GetDexFile(),
+ dex::TypeIndex(type_index),
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()))));
if (annotation_class == nullptr) {
- LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index;
+ LOG(INFO) << "Unable to resolve " << klass.GetRealClass()->PrettyClass()
+ << " annotation class " << type_index;
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
return nullptr;
@@ -300,13 +375,13 @@
}
template <bool kTransactionActive>
-bool ProcessAnnotationValue(Handle<mirror::Class> klass,
+bool ProcessAnnotationValue(const ClassData& klass,
const uint8_t** annotation_ptr,
DexFile::AnnotationValue* annotation_value,
Handle<mirror::Class> array_class,
DexFile::AnnotationResultStyle result_style)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
ObjPtr<mirror::Object> element_object = nullptr;
bool set_object = false;
@@ -361,9 +436,8 @@
annotation_value->value_.SetI(index);
} else {
StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
element_object = Runtime::Current()->GetClassLinker()->ResolveString(
- klass->GetDexFile(), dex::StringIndex(index), dex_cache);
+ klass.GetDexFile(), dex::StringIndex(index), hs.NewHandle(klass.GetDexCache()));
set_object = true;
if (element_object == nullptr) {
return false;
@@ -377,8 +451,12 @@
annotation_value->value_.SetI(index);
} else {
dex::TypeIndex type_index(index);
+ StackHandleScope<2> hs(self);
element_object = Runtime::Current()->GetClassLinker()->ResolveType(
- klass->GetDexFile(), type_index, klass.Get());
+ klass.GetDexFile(),
+ type_index,
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()));
set_object = true;
if (element_object == nullptr) {
CHECK(self->IsExceptionPending());
@@ -399,12 +477,13 @@
if (result_style == DexFile::kAllRaw) {
annotation_value->value_.SetI(index);
} else {
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(self);
ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType(
- klass->GetDexFile(), index, dex_cache, class_loader);
+ klass.GetDexFile(),
+ index,
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()));
if (method == nullptr) {
return false;
}
@@ -439,10 +518,11 @@
annotation_value->value_.SetI(index);
} else {
StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(
- klass->GetDexFile(), index, dex_cache, class_loader);
+ klass.GetDexFile(),
+ index,
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()));
if (field == nullptr) {
return false;
}
@@ -467,10 +547,12 @@
annotation_value->value_.SetI(index);
} else {
StackHandleScope<3> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField(
- klass->GetDexFile(), index, dex_cache, class_loader, true);
+ klass.GetDexFile(),
+ index,
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()),
+ true);
if (enum_field == nullptr) {
return false;
} else {
@@ -595,10 +677,10 @@
return true;
}
-mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass,
+mirror::Object* CreateAnnotationMember(const ClassData& klass,
Handle<mirror::Class> annotation_class,
const uint8_t** annotation) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
ScopedObjectAccessUnchecked soa(self);
StackHandleScope<5> hs(self);
@@ -666,12 +748,12 @@
}
const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
- Handle<mirror::Class> klass,
+ const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set,
uint32_t visibility,
Handle<mirror::Class> annotation_class)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
for (uint32_t i = 0; i < annotation_set->size_; ++i) {
const DexFile::AnnotationItem* annotation_item = dex_file.GetAnnotationItem(annotation_set, i);
if (!IsVisibilityCompatible(annotation_item->visibility_, visibility)) {
@@ -679,12 +761,16 @@
}
const uint8_t* annotation = annotation_item->annotation_;
uint32_t type_index = DecodeUnsignedLeb128(&annotation);
+ StackHandleScope<2> hs(Thread::Current());
mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
- klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get());
+ klass.GetDexFile(),
+ dex::TypeIndex(type_index),
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()));
if (resolved_class == nullptr) {
std::string temp;
LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
- klass->GetDescriptor(&temp), type_index);
+ klass.GetRealClass()->GetDescriptor(&temp), type_index);
CHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
continue;
@@ -698,7 +784,7 @@
}
mirror::Object* GetAnnotationObjectFromAnnotationSet(
- Handle<mirror::Class> klass,
+ const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set,
uint32_t visibility,
Handle<mirror::Class> annotation_class)
@@ -712,13 +798,13 @@
return ProcessEncodedAnnotation(klass, &annotation);
}
-mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass,
+mirror::Object* GetAnnotationValue(const ClassData& klass,
const DexFile::AnnotationItem* annotation_item,
const char* annotation_name,
Handle<mirror::Class> array_class,
uint32_t expected_type)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
const uint8_t* annotation =
SearchEncodedAnnotation(dex_file, annotation_item->annotation_, annotation_name);
if (annotation == nullptr) {
@@ -745,10 +831,10 @@
return annotation_value.value_.GetL();
}
-mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> klass,
+mirror::ObjectArray<mirror::String>* GetSignatureValue(const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
StackHandleScope<1> hs(Thread::Current());
const DexFile::AnnotationItem* annotation_item =
SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Signature;",
@@ -771,10 +857,10 @@
return obj->AsObjectArray<mirror::String>();
}
-mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass,
+mirror::ObjectArray<mirror::Class>* GetThrowsValue(const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
StackHandleScope<1> hs(Thread::Current());
const DexFile::AnnotationItem* annotation_item =
SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;",
@@ -798,11 +884,11 @@
}
mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet(
- Handle<mirror::Class> klass,
+ const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set,
uint32_t visibility)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
ScopedObjectAccessUnchecked soa(self);
StackHandleScope<2> hs(self);
@@ -856,11 +942,11 @@
}
mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList(
- Handle<mirror::Class> klass,
+ const ClassData& klass,
const DexFile::AnnotationSetRefList* set_ref_list,
uint32_t size)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile& dex_file = klass->GetDexFile();
+ const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
ScopedObjectAccessUnchecked soa(self);
StackHandleScope<1> hs(self);
@@ -899,15 +985,17 @@
return nullptr;
}
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass()));
- return GetAnnotationObjectFromAnnotationSet(field_class, annotation_set,
- DexFile::kDexVisibilityRuntime, annotation_class);
+ const ClassData field_class(hs, field);
+ return GetAnnotationObjectFromAnnotationSet(field_class,
+ annotation_set,
+ DexFile::kDexVisibilityRuntime,
+ annotation_class);
}
mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field) {
const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field);
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass()));
+ const ClassData field_class(hs, field);
return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime);
}
@@ -917,7 +1005,7 @@
return nullptr;
}
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass()));
+ const ClassData field_class(hs, field);
return GetSignatureValue(field_class, annotation_set);
}
@@ -927,17 +1015,17 @@
return false;
}
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass()));
+ const ClassData field_class(hs, field);
const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet(
field_class, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class);
return annotation_item != nullptr;
}
mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) {
- const DexFile* dex_file = method->GetDexFile();
- mirror::Class* klass = method->GetDeclaringClass();
+ const ClassData klass(method);
+ const DexFile* dex_file = &klass.GetDexFile();
const DexFile::AnnotationsDirectoryItem* annotations_dir =
- dex_file->GetAnnotationsDirectory(*klass->GetClassDef());
+ dex_file->GetAnnotationsDirectory(*klass.GetClassDef());
if (annotations_dir == nullptr) {
return nullptr;
}
@@ -965,10 +1053,9 @@
return nullptr;
}
DexFile::AnnotationValue annotation_value;
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::Class> h_klass(hs.NewHandle(klass));
+ StackHandleScope<1> hs(Thread::Current());
Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
- if (!ProcessAnnotationValue<false>(h_klass,
+ if (!ProcessAnnotationValue<false>(klass,
&annotation,
&annotation_value,
return_type,
@@ -983,17 +1070,15 @@
if (annotation_set == nullptr) {
return nullptr;
}
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return GetAnnotationObjectFromAnnotationSet(method_class, annotation_set,
+ return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set,
DexFile::kDexVisibilityRuntime, annotation_class);
}
mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method) {
const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return ProcessAnnotationSet(method_class, annotation_set, DexFile::kDexVisibilityRuntime);
+ return ProcessAnnotationSet(ClassData(method),
+ annotation_set,
+ DexFile::kDexVisibilityRuntime);
}
mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method) {
@@ -1001,9 +1086,7 @@
if (annotation_set == nullptr) {
return nullptr;
}
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return GetThrowsValue(method_class, annotation_set);
+ return GetThrowsValue(ClassData(method), annotation_set);
}
mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) {
@@ -1019,9 +1102,7 @@
return nullptr;
}
uint32_t size = set_ref_list->size_;
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return ProcessAnnotationSetRefList(method_class, set_ref_list, size);
+ return ProcessAnnotationSetRefList(ClassData(method), set_ref_list, size);
}
mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
@@ -1045,9 +1126,7 @@
const DexFile::AnnotationSetItem* annotation_set =
dex_file->GetSetRefItemItem(annotation_set_ref);
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return GetAnnotationObjectFromAnnotationSet(method_class,
+ return GetAnnotationObjectFromAnnotationSet(ClassData(method),
annotation_set,
DexFile::kDexVisibilityRuntime,
annotation_class);
@@ -1072,7 +1151,7 @@
return false;
}
- StackHandleScope<5> hs(Thread::Current());
+ StackHandleScope<4> hs(Thread::Current());
// Extract the parameters' names String[].
ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
@@ -1082,9 +1161,9 @@
return false;
}
- Handle<mirror::Class> klass = hs.NewHandle(method->GetDeclaringClass());
+ ClassData data(method);
Handle<mirror::Object> names_obj =
- hs.NewHandle(GetAnnotationValue(klass,
+ hs.NewHandle(GetAnnotationValue(data,
annotation_item,
"names",
string_array_class,
@@ -1099,7 +1178,7 @@
return false;
}
Handle<mirror::Object> access_flags_obj =
- hs.NewHandle(GetAnnotationValue(klass,
+ hs.NewHandle(GetAnnotationValue(data,
annotation_item,
"accessFlags",
int_array_class,
@@ -1118,9 +1197,7 @@
if (annotation_set == nullptr) {
return nullptr;
}
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
- return GetSignatureValue(method_class, annotation_set);
+ return GetSignatureValue(ClassData(method), annotation_set);
}
bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class,
@@ -1129,37 +1206,39 @@
if (annotation_set == nullptr) {
return false;
}
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass()));
const DexFile::AnnotationItem* annotation_item =
- GetAnnotationItemFromAnnotationSet(method_class, annotation_set, visibility,
- annotation_class);
+ GetAnnotationItemFromAnnotationSet(ClassData(method),
+ annotation_set, visibility, annotation_class);
return annotation_item != nullptr;
}
mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass,
Handle<mirror::Class> annotation_class) {
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
- return GetAnnotationObjectFromAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime,
+ return GetAnnotationObjectFromAnnotationSet(data,
+ annotation_set,
+ DexFile::kDexVisibilityRuntime,
annotation_class);
}
mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass) {
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
- return ProcessAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+ return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime);
}
mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> klass) {
- const DexFile& dex_file = klass->GetDexFile();
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
const DexFile::AnnotationItem* annotation_item =
- SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/MemberClasses;",
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/MemberClasses;",
DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return nullptr;
@@ -1172,7 +1251,7 @@
return nullptr;
}
mirror::Object* obj =
- GetAnnotationValue(klass, annotation_item, "value", class_array_class,
+ GetAnnotationValue(data, annotation_item, "value", class_array_class,
DexFile::kDexAnnotationArray);
if (obj == nullptr) {
return nullptr;
@@ -1181,18 +1260,18 @@
}
mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) {
- const DexFile& dex_file = klass->GetDexFile();
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
const DexFile::AnnotationItem* annotation_item =
- SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingClass;",
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/EnclosingClass;",
DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return nullptr;
}
- mirror::Object* obj = GetAnnotationValue(klass, annotation_item, "value",
+ mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value",
ScopedNullHandle<mirror::Class>(),
DexFile::kDexAnnotationType);
if (obj == nullptr) {
@@ -1202,28 +1281,30 @@
}
mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) {
- const DexFile& dex_file = klass->GetDexFile();
mirror::Class* declaring_class = GetDeclaringClass(klass);
if (declaring_class != nullptr) {
return declaring_class;
}
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
const DexFile::AnnotationItem* annotation_item =
- SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;",
+ SearchAnnotationSet(data.GetDexFile(),
+ annotation_set,
+ "Ldalvik/annotation/EnclosingMethod;",
DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return nullptr;
}
const uint8_t* annotation =
- SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "value");
+ SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "value");
if (annotation == nullptr) {
return nullptr;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue<false>(klass,
+ if (!ProcessAnnotationValue<false>(data,
&annotation,
&annotation_value,
ScopedNullHandle<mirror::Class>(),
@@ -1234,10 +1315,11 @@
return nullptr;
}
StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType(
- klass->GetDexFile(), annotation_value.value_.GetI(), dex_cache, class_loader);
+ data.GetDexFile(),
+ annotation_value.value_.GetI(),
+ hs.NewHandle(data.GetDexCache()),
+ hs.NewHandle(data.GetClassLoader()));
if (method == nullptr) {
return nullptr;
}
@@ -1245,39 +1327,44 @@
}
mirror::Object* GetEnclosingMethod(Handle<mirror::Class> klass) {
- const DexFile& dex_file = klass->GetDexFile();
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
const DexFile::AnnotationItem* annotation_item =
- SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;",
+ SearchAnnotationSet(data.GetDexFile(),
+ annotation_set,
+ "Ldalvik/annotation/EnclosingMethod;",
DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return nullptr;
}
- return GetAnnotationValue(klass, annotation_item, "value", ScopedNullHandle<mirror::Class>(),
+ return GetAnnotationValue(data, annotation_item, "value", ScopedNullHandle<mirror::Class>(),
DexFile::kDexAnnotationMethod);
}
bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) {
- const DexFile& dex_file = klass->GetDexFile();
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return false;
}
const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(
- dex_file, annotation_set, "Ldalvik/annotation/InnerClass;", DexFile::kDexVisibilitySystem);
+ data.GetDexFile(),
+ annotation_set,
+ "Ldalvik/annotation/InnerClass;",
+ DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return false;
}
const uint8_t* annotation =
- SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "name");
+ SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "name");
if (annotation == nullptr) {
return false;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue<false>(klass,
+ if (!ProcessAnnotationValue<false>(data,
&annotation,
&annotation_value,
ScopedNullHandle<mirror::Class>(),
@@ -1293,24 +1380,24 @@
}
bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) {
- const DexFile& dex_file = klass->GetDexFile();
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return false;
}
const DexFile::AnnotationItem* annotation_item =
- SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/InnerClass;",
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/InnerClass;",
DexFile::kDexVisibilitySystem);
if (annotation_item == nullptr) {
return false;
}
const uint8_t* annotation =
- SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "accessFlags");
+ SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "accessFlags");
if (annotation == nullptr) {
return false;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue<false>(klass,
+ if (!ProcessAnnotationValue<false>(data,
&annotation,
&annotation_value,
ScopedNullHandle<mirror::Class>(),
@@ -1325,20 +1412,22 @@
}
mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForClass(Handle<mirror::Class> klass) {
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return nullptr;
}
- return GetSignatureValue(klass, annotation_set);
+ return GetSignatureValue(data, annotation_set);
}
bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) {
- const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass);
+ ClassData data(klass);
+ const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
if (annotation_set == nullptr) {
return false;
}
const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet(
- klass, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class);
+ data, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class);
return annotation_item != nullptr;
}
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 5167869..db65e40 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -111,7 +111,7 @@
&error_msg));
ASSERT_TRUE(image_header != nullptr) << error_msg;
const OatHeader& oat_header = odex_file->GetOatHeader();
- uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+ uint32_t combined_checksum = image_header->GetOatChecksum();
if (CompilerFilter::DependsOnImageChecksum(filter)) {
if (with_alternate_image) {
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index c8ee99a..1520e13 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -62,9 +62,7 @@
extern "C" NO_RETURN void artThrowNullPointerExceptionFromSignal(uintptr_t addr, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- self->NoteSignalBeingHandled();
ThrowNullPointerExceptionFromDexPC(/* check_address */ true, addr);
- self->NoteSignalHandlerDone();
self->QuickDeliverException();
}
@@ -95,9 +93,7 @@
extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- self->NoteSignalBeingHandled();
ThrowStackOverflowError(self);
- self->NoteSignalHandlerDone();
self->QuickDeliverException();
}
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index d0687ce..55a4625 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -133,9 +133,8 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end,
sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, held_mutexes, nested_signal_state,
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, held_mutexes, flip_function,
sizeof(void*) * kLockLevelCount);
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, nested_signal_state, flip_function, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, flip_function, method_verifier, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, method_verifier, thread_local_mark_stack, sizeof(void*));
EXPECT_OFFSET_DIFF(Thread, tlsPtr_.thread_local_mark_stack, Thread, wait_mutex_, sizeof(void*),
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index f9345b6..64128cc 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -28,47 +28,6 @@
#include "thread-inl.h"
#include "verify_object-inl.h"
-// Note on nested signal support
-// -----------------------------
-//
-// Typically a signal handler should not need to deal with signals that occur within it.
-// However, when a SIGSEGV occurs that is in generated code and is not one of the
-// handled signals (implicit checks), we call a function to try to dump the stack
-// to the log. This enhances the debugging experience but may have the side effect
-// that it may not work. If the cause of the original SIGSEGV is a corrupted stack or other
-// memory region, the stack backtrace code may run into trouble and may either crash
-// or fail with an abort (SIGABRT). In either case we don't want that (new) signal to
-// mask the original signal and thus prevent useful debug output from being presented.
-//
-// In order to handle this situation, before we call the stack tracer we do the following:
-//
-// 1. shutdown the fault manager so that we are talking to the real signal management
-// functions rather than those in sigchain.
-// 2. use pthread_sigmask to allow SIGSEGV and SIGABRT signals to be delivered to the
-// thread running the signal handler.
-// 3. set the handler for SIGSEGV and SIGABRT to a secondary signal handler.
-// 4. save the thread's state to the TLS of the current thread using 'setjmp'
-//
-// We then call the stack tracer and one of two things may happen:
-// a. it completes successfully
-// b. it crashes and a signal is raised.
-//
-// In the former case, we fall through and everything is fine. In the latter case
-// our secondary signal handler gets called in a signal context. This results in
-// a call to FaultManager::HandledNestedSignal(), an archirecture specific function
-// whose purpose is to call 'longjmp' on the jmp_buf saved in the TLS of the current
-// thread. This results in a return with a non-zero value from 'setjmp'. We detect this
-// and write something to the log to tell the user that it happened.
-//
-// Regardless of how we got there, we reach the code after the stack tracer and we
-// restore the signal states to their original values, reinstate the fault manager (thus
-// reestablishing the signal chain) and continue.
-
-// This is difficult to test with a runtime test. To invoke the nested signal code
-// on any signal, uncomment the following line and run something that throws a
-// NullPointerException.
-// #define TEST_NESTED_SIGNAL
-
namespace art {
// Static fault manger object accessed by signal handler.
FaultManager fault_manager;
@@ -83,11 +42,6 @@
fault_manager.HandleFault(sig, info, context);
}
-// Signal handler for dealing with a nested signal.
-static void art_nested_signal_handler(int sig, siginfo_t* info, void* context) {
- fault_manager.HandleNestedSignal(sig, info, context);
-}
-
FaultManager::FaultManager() : initialized_(false) {
sigaction(SIGSEGV, nullptr, &oldaction_);
}
@@ -156,122 +110,93 @@
DCHECK(self != nullptr);
DCHECK(Runtime::Current() != nullptr);
DCHECK(Runtime::Current()->IsStarted());
-
- // Now set up the nested signal handler.
-
- // TODO: add SIGSEGV back to the nested signals when we can handle running out stack gracefully.
- static const int handled_nested_signals[] = {SIGABRT};
- constexpr size_t num_handled_nested_signals = arraysize(handled_nested_signals);
-
- // Release the fault manager so that it will remove the signal chain for
- // SIGSEGV and we call the real sigaction.
- fault_manager.Release();
-
- // The action for SIGSEGV should be the default handler now.
-
- // Unblock the signals we allow so that they can be delivered in the signal handler.
- sigset_t sigset;
- sigemptyset(&sigset);
- for (int signal : handled_nested_signals) {
- sigaddset(&sigset, signal);
- }
- pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
-
- // If we get a signal in this code we want to invoke our nested signal
- // handler.
- struct sigaction action;
- struct sigaction oldactions[num_handled_nested_signals];
- action.sa_sigaction = art_nested_signal_handler;
-
- // Explicitly mask out SIGSEGV and SIGABRT from the nested signal handler. This
- // should be the default but we definitely don't want these happening in our
- // nested signal handler.
- sigemptyset(&action.sa_mask);
- for (int signal : handled_nested_signals) {
- sigaddset(&action.sa_mask, signal);
- }
-
- action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
- action.sa_restorer = nullptr;
-#endif
-
- // Catch handled signals to invoke our nested handler.
- bool success = true;
- for (size_t i = 0; i < num_handled_nested_signals; ++i) {
- success = sigaction(handled_nested_signals[i], &action, &oldactions[i]) == 0;
- if (!success) {
- PLOG(ERROR) << "Unable to set up nested signal handler";
- break;
+ for (const auto& handler : other_handlers_) {
+ if (handler->Action(sig, info, context)) {
+ return true;
}
}
-
- if (success) {
- // Save the current state and call the handlers. If anything causes a signal
- // our nested signal handler will be invoked and this will longjmp to the saved
- // state.
- if (setjmp(*self->GetNestedSignalState()) == 0) {
- for (const auto& handler : other_handlers_) {
- if (handler->Action(sig, info, context)) {
- // Restore the signal handlers, reinit the fault manager and return. Signal was
- // handled.
- for (size_t i = 0; i < num_handled_nested_signals; ++i) {
- success = sigaction(handled_nested_signals[i], &oldactions[i], nullptr) == 0;
- if (!success) {
- PLOG(ERROR) << "Unable to restore signal handler";
- }
- }
- fault_manager.Init();
- return true;
- }
- }
- } else {
- LOG(ERROR) << "Nested signal detected - original signal being reported";
- }
-
- // Restore the signal handlers.
- for (size_t i = 0; i < num_handled_nested_signals; ++i) {
- success = sigaction(handled_nested_signals[i], &oldactions[i], nullptr) == 0;
- if (!success) {
- PLOG(ERROR) << "Unable to restore signal handler";
- }
- }
- }
-
- // Now put the fault manager back in place.
- fault_manager.Init();
return false;
}
+class ScopedSignalUnblocker {
+ public:
+ explicit ScopedSignalUnblocker(const std::initializer_list<int>& signals) {
+ sigset_t new_mask;
+ sigemptyset(&new_mask);
+ for (int signal : signals) {
+ sigaddset(&new_mask, signal);
+ }
+ if (sigprocmask(SIG_UNBLOCK, &new_mask, &previous_mask_) != 0) {
+ PLOG(FATAL) << "failed to unblock signals";
+ }
+ }
+
+ ~ScopedSignalUnblocker() {
+ if (sigprocmask(SIG_SETMASK, &previous_mask_, nullptr) != 0) {
+ PLOG(FATAL) << "failed to unblock signals";
+ }
+ }
+
+ private:
+ sigset_t previous_mask_;
+};
+
+class ScopedHandlingSignalSetter {
+ public:
+ explicit ScopedHandlingSignalSetter(Thread* thread) : thread_(thread) {
+ CHECK(!thread->HandlingSignal());
+ thread_->SetHandlingSignal(true);
+ }
+
+ ~ScopedHandlingSignalSetter() {
+ CHECK(thread_->HandlingSignal());
+ thread_->SetHandlingSignal(false);
+ }
+
+ private:
+ Thread* thread_;
+};
+
void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
// BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
//
// If malloc calls abort, it will be holding its lock.
// If the handler tries to call malloc, it will deadlock.
- VLOG(signals) << "Handling fault";
- if (IsInGeneratedCode(info, context, true)) {
- VLOG(signals) << "in generated code, looking for handler";
- for (const auto& handler : generated_code_handlers_) {
- VLOG(signals) << "invoking Action on handler " << handler;
- if (handler->Action(sig, info, context)) {
-#ifdef TEST_NESTED_SIGNAL
- // In test mode we want to fall through to stack trace handler
- // on every signal (in reality this will cause a crash on the first
- // signal).
- break;
-#else
- // We have handled a signal so it's time to return from the
- // signal handler to the appropriate place.
- return;
-#endif
- }
- }
- // We hit a signal we didn't handle. This might be something for which
- // we can give more information about so call all registered handlers to see
- // if it is.
- if (HandleFaultByOtherHandlers(sig, info, context)) {
+ // Use a thread local field to track whether we're recursing, and fall back.
+ // (e.g.. if one of our handlers crashed)
+ Thread* thread = Thread::Current();
+
+ if (thread != nullptr && !thread->HandlingSignal()) {
+ // Unblock some signals and set thread->handling_signal_ to true,
+ // so that we can catch crashes in our signal handler.
+ ScopedHandlingSignalSetter setter(thread);
+ ScopedSignalUnblocker unblocker { SIGABRT, SIGBUS, SIGSEGV }; // NOLINT
+
+ VLOG(signals) << "Handling fault";
+
+#ifdef TEST_NESTED_SIGNAL
+ // Simulate a crash in a handler.
+ raise(SIGSEGV);
+#endif
+
+ if (IsInGeneratedCode(info, context, true)) {
+ VLOG(signals) << "in generated code, looking for handler";
+ for (const auto& handler : generated_code_handlers_) {
+ VLOG(signals) << "invoking Action on handler " << handler;
+ if (handler->Action(sig, info, context)) {
+ // We have handled a signal so it's time to return from the
+ // signal handler to the appropriate place.
+ return;
+ }
+ }
+
+ // We hit a signal we didn't handle. This might be something for which
+ // we can give more information about so call all registered handlers to
+ // see if it is.
+ if (HandleFaultByOtherHandlers(sig, info, context)) {
return;
+ }
}
}
@@ -417,11 +342,7 @@
bool JavaStackTraceHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* siginfo, void* context) {
// Make sure that we are in the generated code, but we may not have a dex pc.
-#ifdef TEST_NESTED_SIGNAL
- bool in_generated_code = true;
-#else
bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context, false);
-#endif
if (in_generated_code) {
LOG(ERROR) << "Dumping java stack trace for crash in generated code";
ArtMethod* method = nullptr;
@@ -432,12 +353,6 @@
manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
// Inside of generated code, sp[0] is the method, so sp is the frame.
self->SetTopOfStack(reinterpret_cast<ArtMethod**>(sp));
-#ifdef TEST_NESTED_SIGNAL
- // To test the nested signal handler we raise a signal here. This will cause the
- // nested signal handler to be called and perform a longjmp back to the setjmp
- // above.
- abort();
-#endif
self->DumpJavaStack(LOG_STREAM(ERROR));
}
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 56e0fb7..ce59ba7 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -45,7 +45,6 @@
void EnsureArtActionInFrontOfSignalChain();
void HandleFault(int sig, siginfo_t* info, void* context);
- void HandleNestedSignal(int sig, siginfo_t* info, void* context);
// Added handlers are owned by the fault handler and will be freed on Shutdown().
void AddHandler(FaultHandler* handler, bool generated_code);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 010ef11..568f8d6 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1259,17 +1259,18 @@
}
}
}
- ArtField** fields = dex_cache->GetResolvedFields();
+ mirror::FieldDexCacheType* fields = dex_cache->GetResolvedFields();
if (fields != nullptr) {
- ArtField** new_fields = fixup_adapter.ForwardObject(fields);
+ mirror::FieldDexCacheType* new_fields = fixup_adapter.ForwardObject(fields);
if (fields != new_fields) {
dex_cache->SetResolvedFields(new_fields);
}
for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
- ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
- ArtField* copy = fixup_adapter.ForwardObject(orig);
- if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
+ mirror::FieldDexCachePair orig =
+ mirror::DexCache::GetNativePairPtrSize(new_fields, j, pointer_size);
+ mirror::FieldDexCachePair copy(fixup_adapter.ForwardObject(orig.object), orig.index);
+ if (orig.object != copy.object) {
+ mirror::DexCache::SetNativePairPtrSize(new_fields, j, copy, pointer_size);
}
}
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 88f28f3..b153ea0 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '0', '\0' }; // Integer.valueOf intrinsic
+const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '3', '\0' }; // hash-based DexCache fields
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 5e901cd..ff0c20e 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -79,6 +79,151 @@
// java.lang.Math.atan(D)D
UNARY_SIMPLE_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD);
+// java.lang.String.charAt(I)C
+static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+ inst->GetVarArgs(arg, inst_data);
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString();
+ int length = str->GetLength();
+ int index = shadow_frame->GetVReg(arg[1]);
+ uint16_t res;
+ if (UNLIKELY(index < 0) || (index >= length)) {
+ return false; // Punt and let non-intrinsic version deal with the throw.
+ }
+ if (str->IsCompressed()) {
+ res = str->GetValueCompressed()[index];
+ } else {
+ res = str->GetValue()[index];
+ }
+ result_register->SetC(res);
+ return true;
+}
+
+// java.lang.String.compareTo(Ljava/lang/string)I
+static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+ inst->GetVarArgs(arg, inst_data);
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString();
+ mirror::Object* arg1 = shadow_frame->GetVRegReference(arg[1]);
+ if (arg1 == nullptr) {
+ return false;
+ }
+ result_register->SetI(str->CompareTo(arg1->AsString()));
+ return true;
+}
+
+#define STRING_INDEXOF_INTRINSIC(name, starting_pos) \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result_register) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
+ inst->GetVarArgs(arg, inst_data); \
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
+ int ch = shadow_frame->GetVReg(arg[1]); \
+ if (ch >= 0x10000) { \
+ /* Punt if supplementary char. */ \
+ return false; \
+ } \
+ result_register->SetI(str->FastIndexOf(ch, starting_pos)); \
+ return true; \
+}
+
+// java.lang.String.indexOf(I)I
+STRING_INDEXOF_INTRINSIC(StringIndexOf, 0);
+
+// java.lang.String.indexOf(II)I
+STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2]));
+
+#define SIMPLE_STRING_INTRINSIC(name, operation) \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result_register) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
+ inst->GetVarArgs(arg, inst_data); \
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
+ result_register->operation; \
+ return true; \
+}
+
+// java.lang.String.isEmpty()Z
+SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0))
+
+// java.lang.String.length()I
+SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength()))
+
+// java.lang.String.getCharsNoCheck(II[CI)V
+static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Start, end & index already checked by caller - won't throw. Destination is uncompressed.
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+ inst->GetVarArgs(arg, inst_data);
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString();
+ int32_t start = shadow_frame->GetVReg(arg[1]);
+ int32_t end = shadow_frame->GetVReg(arg[2]);
+ int32_t index = shadow_frame->GetVReg(arg[4]);
+ mirror::CharArray* array = shadow_frame->GetVRegReference(arg[3])->AsCharArray();
+ uint16_t* dst = array->GetData() + index;
+ int32_t len = (end - start);
+ if (str->IsCompressed()) {
+ const uint8_t* src_8 = str->GetValueCompressed() + start;
+ for (int i = 0; i < len; i++) {
+ dst[i] = src_8[i];
+ }
+ } else {
+ uint16_t* src_16 = str->GetValue() + start;
+ memcpy(dst, src_16, len * sizeof(uint16_t));
+ }
+ return true;
+}
+
+// java.lang.String.equalsLjava/lang/Object;)Z
+static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+ inst->GetVarArgs(arg, inst_data);
+ mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString();
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg[1]);
+ bool res = false; // Assume not equal.
+ if ((obj != nullptr) && obj->IsString()) {
+ mirror::String* str2 = obj->AsString();
+ if (str->GetCount() == str2->GetCount()) {
+ // Length & compression status are same. Can use block compare.
+ void* bytes1;
+ void* bytes2;
+ int len = str->GetLength();
+ if (str->IsCompressed()) {
+ bytes1 = str->GetValueCompressed();
+ bytes2 = str2->GetValueCompressed();
+ } else {
+ len *= sizeof(uint16_t);
+ bytes1 = str->GetValue();
+ bytes2 = str2->GetValue();
+ }
+ res = (memcmp(bytes1, bytes2, len) == 0);
+ }
+ }
+ result_register->SetZ(res);
+ return true;
+}
+
#define INTRINSIC_CASE(name) \
case Intrinsics::k##name: \
res = Mterp##name(shadow_frame, inst, inst_data, result_register); \
@@ -110,6 +255,14 @@
INTRINSIC_CASE(MathAsin)
INTRINSIC_CASE(MathAcos)
INTRINSIC_CASE(MathAtan)
+ INTRINSIC_CASE(StringCharAt)
+ INTRINSIC_CASE(StringCompareTo)
+ INTRINSIC_CASE(StringIndexOf)
+ INTRINSIC_CASE(StringIndexOfAfter)
+ INTRINSIC_CASE(StringEquals)
+ INTRINSIC_CASE(StringGetCharsNoCheck)
+ INTRINSIC_CASE(StringIsEmpty)
+ INTRINSIC_CASE(StringLength)
default:
res = false; // Punt
break;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 8b2a2b4..e7b23dc 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1262,6 +1262,7 @@
for (size_t i = 0; i < info->number_of_inline_caches_; ++i) {
std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes;
const InlineCache& cache = info->cache_[i];
+ ArtMethod* caller = info->GetMethod();
bool is_missing_types = false;
for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) {
mirror::Class* cls = cache.classes_[k].Read();
@@ -1269,6 +1270,15 @@
break;
}
+ // Check if the receiver is in the boot class path or if it's in the
+ // same class loader as the caller. If not, skip it, as there is not
+ // much we can do during AOT.
+ if (!cls->IsBootStrapClassLoaded() &&
+ caller->GetClassLoader() != cls->GetClassLoader()) {
+ is_missing_types = true;
+ continue;
+ }
+
const DexFile* class_dex_file = nullptr;
dex::TypeIndex type_index;
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index 398bfbc..f61a07c 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -39,7 +39,9 @@
}
uint16_t GetC() const { return c; }
- void SetC(uint16_t new_c) { c = new_c; }
+ void SetC(uint16_t new_c) {
+ j = static_cast<int64_t>(new_c); // Zero-extend to 64 bits.
+ }
double GetD() const { return d; }
void SetD(double new_d) { d = new_d; }
@@ -66,7 +68,9 @@
}
uint8_t GetZ() const { return z; }
- void SetZ(uint8_t new_z) { z = new_z; }
+ void SetZ(uint8_t new_z) {
+ j = static_cast<int64_t>(new_z); // Zero-extend to 64 bits.
+ }
mirror::Object** GetGCRoot() { return &l; }
diff --git a/runtime/linear_alloc.cc b/runtime/linear_alloc.cc
index f91b0ed..e9db9b8 100644
--- a/runtime/linear_alloc.cc
+++ b/runtime/linear_alloc.cc
@@ -33,6 +33,11 @@
return allocator_.Alloc(size);
}
+void* LinearAlloc::AllocAlign16(Thread* self, size_t size) {
+ MutexLock mu(self, lock_);
+ return allocator_.AllocAlign16(size);
+}
+
size_t LinearAlloc::GetUsedMemory() const {
MutexLock mu(Thread::Current(), lock_);
return allocator_.BytesUsed();
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index df7f17d..384b2e3 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -29,6 +29,7 @@
explicit LinearAlloc(ArenaPool* pool);
void* Alloc(Thread* self, size_t size) REQUIRES(!lock_);
+ void* AllocAlign16(Thread* self, size_t size) REQUIRES(!lock_);
// Realloc never frees the input pointer, it is the caller's job to do this if necessary.
void* Realloc(Thread* self, void* ptr, size_t old_size, size_t new_size) REQUIRES(!lock_);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 29bf6a0..582ecb2 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -24,6 +24,7 @@
#include "base/casts.h"
#include "base/enums.h"
#include "base/logging.h"
+#include "dex_file.h"
#include "gc_root.h"
#include "mirror/class.h"
#include "mirror/call_site.h"
@@ -36,6 +37,15 @@
namespace art {
namespace mirror {
+template <typename T>
+inline void NativeDexCachePair<T>::Initialize(std::atomic<NativeDexCachePair<T>>* dex_cache,
+ PointerSize pointer_size) {
+ NativeDexCachePair<T> first_elem;
+ first_elem.object = nullptr;
+ first_elem.index = InvalidIndexForSlot(0);
+ DexCache::SetNativePairPtrSize(dex_cache, 0, first_elem, pointer_size);
+}
+
inline uint32_t DexCache::ClassSize(PointerSize pointer_size) {
uint32_t vtable_entries = Object::kVTableLength + 5;
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
@@ -164,20 +174,36 @@
}
}
+inline uint32_t DexCache::FieldSlotIndex(uint32_t field_idx) {
+ DCHECK_LT(field_idx, GetDexFile()->NumFieldIds());
+ const uint32_t slot_idx = field_idx % kDexCacheFieldCacheSize;
+ DCHECK_LT(slot_idx, NumResolvedFields());
+ return slot_idx;
+}
+
inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
- ArtField* field = GetElementPtrSize(GetResolvedFields(), field_idx, ptr_size);
- if (field == nullptr || field->GetDeclaringClass()->IsErroneous()) {
- return nullptr;
- }
- return field;
+ auto pair = GetNativePairPtrSize(GetResolvedFields(), FieldSlotIndex(field_idx), ptr_size);
+ return pair.GetObjectForIndex(field_idx);
}
inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field, PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
- SetElementPtrSize(GetResolvedFields(), field_idx, field, ptr_size);
+ DCHECK(field != nullptr);
+ FieldDexCachePair pair(field, field_idx);
+ SetNativePairPtrSize(GetResolvedFields(), FieldSlotIndex(field_idx), pair, ptr_size);
+}
+
+inline void DexCache::ClearResolvedField(uint32_t field_idx, PointerSize ptr_size) {
+ DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
+ uint32_t slot_idx = FieldSlotIndex(field_idx);
+ auto* resolved_fields = GetResolvedFields();
+ // This is racy but should only be called from the single-threaded ImageWriter.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ if (GetNativePairPtrSize(resolved_fields, slot_idx, ptr_size).index == field_idx) {
+ FieldDexCachePair cleared(nullptr, FieldDexCachePair::InvalidIndexForSlot(slot_idx));
+ SetNativePairPtrSize(resolved_fields, slot_idx, cleared, ptr_size);
+ }
}
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size) {
@@ -225,6 +251,40 @@
}
}
+template <typename T>
+NativeDexCachePair<T> DexCache::GetNativePairPtrSize(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ PointerSize ptr_size) {
+ if (ptr_size == PointerSize::k64) {
+ auto* array = reinterpret_cast<std::atomic<ConversionPair64>*>(pair_array);
+ ConversionPair64 value = AtomicLoadRelaxed16B(&array[idx]);
+ return NativeDexCachePair<T>(reinterpret_cast64<T*>(value.first),
+ dchecked_integral_cast<size_t>(value.second));
+ } else {
+ auto* array = reinterpret_cast<std::atomic<ConversionPair32>*>(pair_array);
+ ConversionPair32 value = array[idx].load(std::memory_order_relaxed);
+ return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
+ }
+}
+
+template <typename T>
+void DexCache::SetNativePairPtrSize(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ NativeDexCachePair<T> pair,
+ PointerSize ptr_size) {
+ if (ptr_size == PointerSize::k64) {
+ auto* array = reinterpret_cast<std::atomic<ConversionPair64>*>(pair_array);
+ ConversionPair64 v(reinterpret_cast64<uint64_t>(pair.object), pair.index);
+ AtomicStoreRelease16B(&array[idx], v);
+ } else {
+ auto* array = reinterpret_cast<std::atomic<ConversionPair32>*>(pair_array);
+ ConversionPair32 v(
+ dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(pair.object)),
+ dchecked_integral_cast<uint32_t>(pair.index));
+ array[idx].store(v, std::memory_order_release);
+ }
+}
+
template <typename T,
ReadBarrierOption kReadBarrierOption,
typename Visitor>
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 1b8b391..c95d92e 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -52,8 +52,12 @@
dex_file->NumTypeIds() != 0u ||
dex_file->NumMethodIds() != 0u ||
dex_file->NumFieldIds() != 0u) {
+ static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8.");
+ DCHECK(layout.Alignment() == 8u || layout.Alignment() == 16u);
// Zero-initialized.
- raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
+ raw_arrays = (layout.Alignment() == 16u)
+ ? reinterpret_cast<uint8_t*>(linear_alloc->AllocAlign16(self, layout.Size()))
+ : reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
}
mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
@@ -62,17 +66,21 @@
reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
- ArtField** fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
- reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+ mirror::FieldDexCacheType* fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
+ reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
- size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
+ size_t num_strings = kDexCacheStringCacheSize;
if (dex_file->NumStringIds() < num_strings) {
num_strings = dex_file->NumStringIds();
}
- size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
+ size_t num_types = kDexCacheTypeCacheSize;
if (dex_file->NumTypeIds() < num_types) {
num_types = dex_file->NumTypeIds();
}
+ size_t num_fields = kDexCacheFieldCacheSize;
+ if (dex_file->NumFieldIds() < num_fields) {
+ num_fields = dex_file->NumFieldIds();
+ }
// Note that we allocate the method type dex caches regardless of this flag,
// and we make sure here that they're not used by the runtime. This is in the
@@ -80,17 +88,17 @@
//
// If this needs to be mitigated in a production system running this code,
// DexCache::kDexCacheMethodTypeCacheSize can be set to zero.
- mirror::MethodTypeDexCacheType* method_types = nullptr;
+ MethodTypeDexCacheType* method_types = nullptr;
size_t num_method_types = 0;
- if (dex_file->NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) {
+ if (dex_file->NumProtoIds() < kDexCacheMethodTypeCacheSize) {
num_method_types = dex_file->NumProtoIds();
} else {
- num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+ num_method_types = kDexCacheMethodTypeCacheSize;
}
if (num_method_types > 0) {
- method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+ method_types = reinterpret_cast<MethodTypeDexCacheType*>(
raw_arrays + layout.MethodTypesOffset());
}
@@ -98,13 +106,13 @@
? nullptr
: reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
- DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
+ DCHECK_ALIGNED(raw_arrays, alignof(StringDexCacheType)) <<
"Expected raw_arrays to align to StringDexCacheType.";
- DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
+ DCHECK_ALIGNED(layout.StringsOffset(), alignof(StringDexCacheType)) <<
"Expected StringsOffset() to align to StringDexCacheType.";
- DCHECK_ALIGNED(strings, alignof(mirror::StringDexCacheType)) <<
+ DCHECK_ALIGNED(strings, alignof(StringDexCacheType)) <<
"Expected strings to align to StringDexCacheType.";
- static_assert(alignof(mirror::StringDexCacheType) == 8u,
+ static_assert(alignof(StringDexCacheType) == 8u,
"Expected StringDexCacheType to have align of 8.");
if (kIsDebugBuild) {
// Sanity check to make sure all the dex cache arrays are empty. b/28992179
@@ -117,10 +125,11 @@
CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
}
for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
- CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
+ CHECK(GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
}
- for (size_t i = 0; i < dex_file->NumFieldIds(); ++i) {
- CHECK(mirror::DexCache::GetElementPtrSize(fields, i, image_pointer_size) == nullptr);
+ for (size_t i = 0; i < num_fields; ++i) {
+ CHECK_EQ(GetNativePairPtrSize(fields, i, image_pointer_size).index, 0u);
+ CHECK(GetNativePairPtrSize(fields, i, image_pointer_size).object == nullptr);
}
for (size_t i = 0; i < num_method_types; ++i) {
CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
@@ -136,6 +145,9 @@
if (types != nullptr) {
mirror::TypeDexCachePair::Initialize(types);
}
+ if (fields != nullptr) {
+ mirror::FieldDexCachePair::Initialize(fields, image_pointer_size);
+ }
if (method_types != nullptr) {
mirror::MethodTypeDexCachePair::Initialize(method_types);
}
@@ -148,7 +160,7 @@
methods,
dex_file->NumMethodIds(),
fields,
- dex_file->NumFieldIds(),
+ num_fields,
method_types,
num_method_types,
call_sites,
@@ -164,7 +176,7 @@
uint32_t num_resolved_types,
ArtMethod** resolved_methods,
uint32_t num_resolved_methods,
- ArtField** resolved_fields,
+ FieldDexCacheType* resolved_fields,
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
@@ -218,5 +230,23 @@
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
}
+#if !defined(__aarch64__) && !defined(__x86_64__)
+static pthread_mutex_t dex_cache_slow_atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+DexCache::ConversionPair64 DexCache::AtomicLoadRelaxed16B(std::atomic<ConversionPair64>* target) {
+ pthread_mutex_lock(&dex_cache_slow_atomic_mutex);
+ DexCache::ConversionPair64 value = *reinterpret_cast<ConversionPair64*>(target);
+ pthread_mutex_unlock(&dex_cache_slow_atomic_mutex);
+ return value;
+}
+
+void DexCache::AtomicStoreRelease16B(std::atomic<ConversionPair64>* target,
+ ConversionPair64 value) {
+ pthread_mutex_lock(&dex_cache_slow_atomic_mutex);
+ *reinterpret_cast<ConversionPair64*>(target) = value;
+ pthread_mutex_unlock(&dex_cache_slow_atomic_mutex);
+}
+#endif
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 0579198..35707ef 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -91,12 +91,44 @@
}
};
+template <typename T> struct PACKED(2 * __SIZEOF_POINTER__) NativeDexCachePair {
+ T* object;
+ size_t index;
+ // This is similar to DexCachePair except that we're storing a native pointer
+ // instead of a GC root. See DexCachePair for the details.
+ NativeDexCachePair(T* object, uint32_t index)
+ : object(object),
+ index(index) {}
+ NativeDexCachePair() : object(nullptr), index(0u) { }
+ NativeDexCachePair(const NativeDexCachePair<T>&) = default;
+ NativeDexCachePair& operator=(const NativeDexCachePair<T>&) = default;
+
+ static void Initialize(std::atomic<NativeDexCachePair<T>>* dex_cache, PointerSize pointer_size);
+
+ static uint32_t InvalidIndexForSlot(uint32_t slot) {
+ // Since the cache size is a power of two, 0 will always map to slot 0.
+ // Use 1 for slot 0 and 0 for all other slots.
+ return (slot == 0) ? 1u : 0u;
+ }
+
+ T* GetObjectForIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (idx != index) {
+ return nullptr;
+ }
+ DCHECK(object != nullptr);
+ return object;
+ }
+};
+
using TypeDexCachePair = DexCachePair<Class>;
using TypeDexCacheType = std::atomic<TypeDexCachePair>;
using StringDexCachePair = DexCachePair<String>;
using StringDexCacheType = std::atomic<StringDexCachePair>;
+using FieldDexCachePair = NativeDexCachePair<ArtField>;
+using FieldDexCacheType = std::atomic<FieldDexCachePair>;
+
using MethodTypeDexCachePair = DexCachePair<MethodType>;
using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
@@ -116,6 +148,11 @@
static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
"String dex cache size is not a power of 2.");
+ // Size of field dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
+ static constexpr size_t kDexCacheFieldCacheSize = 1024;
+ static_assert(IsPowerOfTwo(kDexCacheFieldCacheSize),
+ "Field dex cache size is not a power of 2.");
+
// Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions
// to hold.
static constexpr size_t kDexCacheMethodTypeCacheSize = 1024;
@@ -130,6 +167,10 @@
return kDexCacheStringCacheSize;
}
+ static constexpr size_t StaticArtFieldSize() {
+ return kDexCacheFieldCacheSize;
+ }
+
static constexpr size_t StaticMethodTypeSize() {
return kDexCacheMethodTypeCacheSize;
}
@@ -255,6 +296,8 @@
// Pointer sized variant, used for patching.
ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -299,11 +342,11 @@
SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods);
}
- ArtField** GetResolvedFields() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<ArtField**>(ResolvedFieldsOffset());
+ FieldDexCacheType* GetResolvedFields() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr<FieldDexCacheType*>(ResolvedFieldsOffset());
}
- void SetResolvedFields(ArtField** resolved_fields)
+ void SetResolvedFields(FieldDexCacheType* resolved_fields)
ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_) {
SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
@@ -376,6 +419,17 @@
template <typename PtrType>
static void SetElementPtrSize(PtrType* ptr_array, size_t idx, PtrType ptr, PointerSize ptr_size);
+ template <typename T>
+ static NativeDexCachePair<T> GetNativePairPtrSize(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ PointerSize ptr_size);
+
+ template <typename T>
+ static void SetNativePairPtrSize(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ NativeDexCachePair<T> pair,
+ PointerSize ptr_size);
+
private:
void Init(const DexFile* dex_file,
ObjPtr<String> location,
@@ -385,7 +439,7 @@
uint32_t num_resolved_types,
ArtMethod** resolved_methods,
uint32_t num_resolved_methods,
- ArtField** resolved_fields,
+ FieldDexCacheType* resolved_fields,
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
@@ -394,8 +448,22 @@
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // std::pair<> is not trivially copyable and as such it is unsuitable for atomic operations,
+ // so we use a custom pair class for loading and storing the NativeDexCachePair<>.
+ template <typename IntType>
+ struct PACKED(2 * sizeof(IntType)) ConversionPair {
+ ConversionPair(IntType f, IntType s) : first(f), second(s) { }
+ ConversionPair(const ConversionPair&) = default;
+ ConversionPair& operator=(const ConversionPair&) = default;
+ IntType first;
+ IntType second;
+ };
+ using ConversionPair32 = ConversionPair<uint32_t>;
+ using ConversionPair64 = ConversionPair<uint64_t>;
+
uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
// Visit instance fields of the dex cache as well as its associated arrays.
@@ -406,12 +474,55 @@
void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+ // Due to lack of 16-byte atomics support, we use hand-crafted routines.
+#if defined(__aarch64__)
+ // 16-byte atomics are supported on aarch64.
+ ALWAYS_INLINE static ConversionPair64 AtomicLoadRelaxed16B(
+ std::atomic<ConversionPair64>* target) {
+ return target->load(std::memory_order_relaxed);
+ }
+
+ ALWAYS_INLINE static void AtomicStoreRelease16B(
+ std::atomic<ConversionPair64>* target, ConversionPair64 value) {
+ target->store(value, std::memory_order_release);
+ }
+#elif defined(__x86_64__)
+ ALWAYS_INLINE static ConversionPair64 AtomicLoadRelaxed16B(
+ std::atomic<ConversionPair64>* target) {
+ uint64_t first, second;
+ __asm__ __volatile__(
+ "lock cmpxchg16b (%2)"
+ : "=&a"(first), "=&d"(second)
+ : "r"(target), "a"(0), "d"(0), "b"(0), "c"(0)
+ : "cc");
+ return ConversionPair64(first, second);
+ }
+
+ ALWAYS_INLINE static void AtomicStoreRelease16B(
+ std::atomic<ConversionPair64>* target, ConversionPair64 value) {
+ uint64_t first, second;
+ __asm__ __volatile__ (
+ "movq (%2), %%rax\n\t"
+ "movq 8(%2), %%rdx\n\t"
+ "1:\n\t"
+ "lock cmpxchg16b (%2)\n\t"
+ "jnz 1b"
+ : "=&a"(first), "=&d"(second)
+ : "r"(target), "b"(value.first), "c"(value.second)
+ : "cc");
+ }
+#else
+ static ConversionPair64 AtomicLoadRelaxed16B(std::atomic<ConversionPair64>* target);
+ static void AtomicStoreRelease16B(std::atomic<ConversionPair64>* target, ConversionPair64 value);
+#endif
+
HeapReference<Object> dex_;
HeapReference<String> location_;
uint64_t dex_file_; // const DexFile*
uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
// elements.
- uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements.
+ uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
+ // num_resolved_fields_ elements.
uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
// num_resolved_method_types_ elements.
uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index ef0aaaa..71a47f6 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -54,7 +54,8 @@
EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
|| java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
- EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
+ EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
+ || java_lang_dex_file_->NumFieldIds() == dex_cache->NumResolvedFields());
EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
|| java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
}
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index f6b6489..54034c2 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -68,8 +68,16 @@
}
}
mirror::DexCache* const dex_cache = declaring_class->GetDexCache();
- ArtField* const art_field = dex_cache->GetResolvedField(GetDexFieldIndex(), kRuntimePointerSize);
- CHECK(art_field != nullptr);
+ ArtField* art_field = dex_cache->GetResolvedField(GetDexFieldIndex(), kRuntimePointerSize);
+ if (UNLIKELY(art_field == nullptr)) {
+ if (IsStatic()) {
+ art_field = declaring_class->FindDeclaredStaticField(dex_cache, GetDexFieldIndex());
+ } else {
+ art_field = declaring_class->FindInstanceField(dex_cache, GetDexFieldIndex());
+ }
+ CHECK(art_field != nullptr);
+ dex_cache->SetResolvedField(GetDexFieldIndex(), art_field, kRuntimePointerSize);
+ }
CHECK_EQ(declaring_class, art_field->GetDeclaringClass());
return art_field;
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 9b707f8..d81c13d 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -444,6 +444,7 @@
if (!kPreloadDexCachesCollectStats) {
return;
}
+ // TODO: Update for hash-based DexCache arrays.
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
Thread* const self = Thread::Current();
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
@@ -463,7 +464,7 @@
}
}
for (size_t j = 0; j < dex_cache->NumResolvedFields(); j++) {
- ArtField* field = class_linker->GetResolvedField(j, dex_cache);
+ ArtField* field = dex_cache->GetResolvedField(j, class_linker->GetImagePointerSize());
if (field != nullptr) {
filled->num_fields++;
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 1544121..df43107 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '1', '4', '\0' }; // hash-based DexCache types.
+ static constexpr uint8_t kOatVersion[] = { '1', '1', '5', '\0' }; // hash-based DexCache fields
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 48bf1e7..1735045 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -750,32 +750,18 @@
// same as kRuntimeISA, so this behavior is suspect (b/35659889).
if (isa == kRuntimeISA) {
const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
+ info->oat_checksum = image_header.GetOatChecksum();
info->oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
info->patch_delta = image_header.GetPatchDelta();
-
- info->oat_checksum = 0;
- for (gc::space::ImageSpace* image_space : image_spaces) {
- info->oat_checksum ^= image_space->GetImageHeader().GetOatChecksum();
- }
} else {
std::unique_ptr<ImageHeader> image_header(
gc::space::ImageSpace::ReadImageHeader(info->location.c_str(), isa, error_msg));
if (image_header == nullptr) {
return nullptr;
}
+ info->oat_checksum = image_header->GetOatChecksum();
info->oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
info->patch_delta = image_header->GetPatchDelta();
-
- info->oat_checksum = 0;
- for (gc::space::ImageSpace* image_space : image_spaces) {
- std::string location = image_space->GetImageLocation();
- image_header.reset(
- gc::space::ImageSpace::ReadImageHeader(location.c_str(), isa, error_msg));
- if (image_header == nullptr) {
- return nullptr;
- }
- info->oat_checksum ^= image_header->GetOatChecksum();
- }
}
return info;
}
@@ -792,16 +778,6 @@
return cached_image_info_.get();
}
-uint32_t OatFileAssistant::CalculateCombinedImageChecksum(InstructionSet isa) {
- std::string error_msg;
- std::unique_ptr<ImageInfo> info = ImageInfo::GetRuntimeImageInfo(isa, &error_msg);
- if (info == nullptr) {
- LOG(WARNING) << "Unable to get runtime image info for checksum: " << error_msg;
- return 0;
- }
- return info->oat_checksum;
-}
-
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
return use_oat ? oat_ : odex_;
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index eec87f0..d61e994 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -276,8 +276,6 @@
std::string* oat_filename,
std::string* error_msg);
- static uint32_t CalculateCombinedImageChecksum(InstructionSet isa = kRuntimeISA);
-
private:
struct ImageInfo {
uint32_t oat_checksum = 0;
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index c01e3f4..dd49ad0 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -13,6 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_library_headers {
+ name: "libopenjdkjvmti_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_defaults {
name: "libopenjdkjvmti_defaults",
defaults: ["art_defaults"],
@@ -40,6 +46,7 @@
"ti_timers.cc",
"transform.cc"],
include_dirs: ["art/runtime"],
+ header_libs: ["libopenjdkjvmti_headers"],
shared_libs: [
"libbase",
"libnativehelper",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5e0d4bd..5401e5c 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -35,7 +35,7 @@
#include <jni.h>
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "art_jvmti.h"
#include "base/logging.h"
diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/include/jvmti.h
similarity index 100%
rename from runtime/openjdkjvmti/jvmti.h
rename to runtime/openjdkjvmti/include/jvmti.h
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 4282e38..2d1b25e 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -62,6 +62,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_class_loader.h"
+#include "ti_phase.h"
#include "ti_redefine.h"
#include "utils.h"
@@ -142,6 +143,18 @@
// It is a primitive or array. Just return
return;
}
+ jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+ if (UNLIKELY(phase != JVMTI_PHASE_START && phase != JVMTI_PHASE_LIVE)) {
+ // We want to wait until we are at least in the START phase so that all WellKnownClasses and
+ // mirror classes have been initialized and loaded. The runtime relies on these classes having
+ // specific fields and methods present. Since PreDefine hooks don't need to abide by this
+ // restriction we will simply not send the event for these classes.
+ LOG(WARNING) << "Ignoring load of class <" << descriptor << "> as it is being loaded during "
+ << "runtime initialization.";
+ return;
+ }
+
+ // Strip the 'L' and ';' from the descriptor
std::string name(std::string(descriptor).substr(1, strlen(descriptor) - 2));
art::Thread* self = art::Thread::Current();
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index a173a4a..9c1d6ef 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -178,7 +178,7 @@
art::ClassLinker* cl = runtime->GetClassLinker();
auto ptr_size = cl->GetImagePointerSize();
const size_t method_size = art::ArtMethod::Size(ptr_size);
- auto* method_storage = allocator_->Alloc(GetThread(), method_size);
+ auto* method_storage = allocator_->Alloc(art::Thread::Current(), method_size);
CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '"
<< old_method->PrettyMethod() << "'";
new_obsolete_method = new (method_storage) art::ArtMethod();
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index b009b47..3347070 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -215,9 +215,8 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsInstructionIPut(new_iput->Opcode()));
uint32_t field_index = new_iput->VRegC_22c();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::DexCache* dex_cache = method->GetDexCache();
- ArtField* field = dex_cache->GetResolvedField(field_index, pointer_size);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* field = class_linker->LookupResolvedField(field_index, method, /* is_static */ false);
if (UNLIKELY(field == nullptr)) {
return false;
}
@@ -227,7 +226,9 @@
if (iputs[old_pos].field_index == DexFile::kDexNoIndex16) {
break;
}
- ArtField* f = dex_cache->GetResolvedField(iputs[old_pos].field_index, pointer_size);
+ ArtField* f = class_linker->LookupResolvedField(iputs[old_pos].field_index,
+ method,
+ /* is_static */ false);
DCHECK(f != nullptr);
if (f == field) {
auto back_it = std::copy(iputs + old_pos + 1, iputs + arraysize(iputs), iputs + old_pos);
@@ -732,9 +733,9 @@
if (method == nullptr) {
return false;
}
- mirror::DexCache* dex_cache = method->GetDexCache();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size);
+ ObjPtr<mirror::DexCache> dex_cache = method->GetDexCache();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* field = class_linker->LookupResolvedField(field_idx, method, /* is_static */ false);
if (field == nullptr || field->IsStatic()) {
return false;
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ff66cc1..30a4046 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1934,7 +1934,6 @@
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
- tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
static_assert((sizeof(Thread) % 4) == 0U,
"art::Thread has a size which is not a multiple of 4.");
@@ -2118,7 +2117,6 @@
delete tlsPtr_.instrumentation_stack;
delete tlsPtr_.name;
delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
- free(tlsPtr_.nested_signal_state);
Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
diff --git a/runtime/thread.h b/runtime/thread.h
index d5fd9e9..de0b892 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1115,21 +1115,12 @@
return tlsPtr_.mterp_alt_ibase;
}
- // Notify that a signal is being handled. This is to protect us from doing recursive
- // NPE handling after a SIGSEGV.
- void NoteSignalBeingHandled() {
- if (tls32_.handling_signal_) {
- LOG(FATAL) << "Detected signal while processing a signal";
- }
- tls32_.handling_signal_ = true;
+ bool HandlingSignal() const {
+ return tls32_.handling_signal_;
}
- void NoteSignalHandlerDone() {
- tls32_.handling_signal_ = false;
- }
-
- jmp_buf* GetNestedSignalState() {
- return tlsPtr_.nested_signal_state;
+ void SetHandlingSignal(bool handling_signal) {
+ tls32_.handling_signal_ = handling_signal;
}
bool IsTransitioningToRunnable() const {
@@ -1460,7 +1451,7 @@
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),
+ thread_local_alloc_stack_end(nullptr),
flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr) {
std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
}
@@ -1606,9 +1597,6 @@
// Support for Mutex lock hierarchy bug detection.
BaseMutex* held_mutexes[kLockLevelCount];
- // Recorded thread state for nested signals.
- jmp_buf* nested_signal_state;
-
// The function used for thread flip.
Closure* flip_function;
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index f9a1405..95904af 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -51,7 +51,11 @@
: DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
}
-constexpr size_t DexCacheArraysLayout::Alignment() {
+inline size_t DexCacheArraysLayout::Alignment() const {
+ return Alignment(pointer_size_);
+}
+
+inline constexpr size_t DexCacheArraysLayout::Alignment(PointerSize pointer_size) {
// mirror::Type/String/MethodTypeDexCacheType alignment is 8,
// i.e. higher than or equal to the pointer alignment.
static_assert(alignof(mirror::TypeDexCacheType) == 8,
@@ -60,8 +64,8 @@
"Expecting alignof(StringDexCacheType) == 8");
static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
"Expecting alignof(MethodTypeDexCacheType) == 8");
- // This is the same as alignof(MethodTypeDexCacheType).
- return alignof(mirror::StringDexCacheType);
+ // This is the same as alignof(FieldDexCacheType) for the given pointer size.
+ return 2u * static_cast<size_t>(pointer_size);
}
template <typename T>
@@ -100,8 +104,8 @@
}
inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
- return strings_offset_ + ElementOffset(PointerSize::k64,
- string_idx % mirror::DexCache::kDexCacheStringCacheSize);
+ uint32_t string_hash = string_idx % mirror::DexCache::kDexCacheStringCacheSize;
+ return strings_offset_ + ElementOffset(PointerSize::k64, string_hash);
}
inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
@@ -119,15 +123,20 @@
}
inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
- return fields_offset_ + ElementOffset(pointer_size_, field_idx);
+ uint32_t field_hash = field_idx % mirror::DexCache::kDexCacheFieldCacheSize;
+ return fields_offset_ + 2u * static_cast<size_t>(pointer_size_) * field_hash;
}
inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const {
- return ArraySize(pointer_size_, num_elements);
+ size_t cache_size = mirror::DexCache::kDexCacheFieldCacheSize;
+ if (num_elements < cache_size) {
+ cache_size = num_elements;
+ }
+ return 2u * static_cast<size_t>(pointer_size_) * num_elements;
}
inline size_t DexCacheArraysLayout::FieldsAlignment() const {
- return static_cast<size_t>(pointer_size_);
+ return 2u * static_cast<size_t>(pointer_size_);
}
inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const {
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index ed677ed..377a374 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -57,7 +57,9 @@
return size_;
}
- static constexpr size_t Alignment();
+ size_t Alignment() const;
+
+ static constexpr size_t Alignment(PointerSize pointer_size);
size_t TypesOffset() const {
return types_offset_;
@@ -125,8 +127,6 @@
const size_t call_sites_offset_;
const size_t size_;
- static size_t Alignment(PointerSize pointer_size);
-
static size_t ElementOffset(PointerSize element_size, uint32_t idx);
static size_t ArraySize(PointerSize element_size, uint32_t num_elements);
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 7daf2f8..898d07d 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -61,7 +61,7 @@
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- static constexpr uint8_t kVdexVersion[] = { '0', '0', '3', '\0' }; // Remove verify-profile
+ static constexpr uint8_t kVdexVersion[] = { '0', '0', '5', '\0' }; // access flags
uint8_t magic_[4];
uint8_t version_[4];
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 52f7e34..740b7dd 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -309,6 +309,7 @@
// Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
// would be thrown at runtime, but we need to continue verification and *not* create a
// hard failure or abort.
+ CheckConstructorInvariants(this);
}
std::string UnresolvedMergedType::Dump() const {
@@ -789,7 +790,7 @@
if (!klass_.IsNull()) {
CHECK(!descriptor_.empty()) << *this;
std::string temp;
- CHECK_EQ(descriptor_.ToString(), klass_.Read()->GetDescriptor(&temp)) << *this;
+ CHECK_EQ(descriptor_, klass_.Read()->GetDescriptor(&temp)) << *this;
}
}
@@ -820,9 +821,7 @@
reg_type_cache_(reg_type_cache),
resolved_part_(resolved),
unresolved_types_(unresolved, false, unresolved.GetAllocator()) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
void UnresolvedMergedType::CheckInvariants() const {
CHECK(reg_type_cache_ != nullptr);
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 472381d..dedf77f 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -274,14 +274,17 @@
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: descriptor_(descriptor),
klass_(klass),
- cache_id_(cache_id) {
+ cache_id_(cache_id) {}
+
+ template <typename Class>
+ void CheckConstructorInvariants(Class* this_ ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ static_assert(std::is_final<Class>::value, "Class must be final.");
if (kIsDebugBuild) {
CheckInvariants();
}
}
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
-
const StringPiece descriptor_;
mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
@@ -289,6 +292,8 @@
friend class RegTypeCache;
private:
+ virtual void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+
/*
* A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
* S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
@@ -339,7 +344,9 @@
private:
ConflictType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {}
+ : RegType(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const ConflictType* instance_;
};
@@ -368,7 +375,9 @@
private:
UndefinedType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {}
+ : RegType(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const UndefinedType* instance_;
};
@@ -387,7 +396,7 @@
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
-class IntegerType : public Cat1Type {
+class IntegerType FINAL : public Cat1Type {
public:
bool IsInteger() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -401,7 +410,9 @@
private:
IntegerType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const IntegerType* instance_;
};
@@ -419,7 +430,9 @@
private:
BooleanType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const BooleanType* instance_;
};
@@ -438,7 +451,9 @@
private:
ByteType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const ByteType* instance_;
};
@@ -456,7 +471,9 @@
private:
ShortType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const ShortType* instance_;
};
@@ -474,7 +491,9 @@
private:
CharType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const CharType* instance_;
};
@@ -492,7 +511,9 @@
private:
FloatType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {}
+ : Cat1Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const FloatType* instance_;
};
@@ -517,7 +538,9 @@
private:
LongLoType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {}
+ : Cat2Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const LongLoType* instance_;
};
@@ -535,7 +558,9 @@
private:
LongHiType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {}
+ : Cat2Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const LongHiType* instance_;
};
@@ -554,7 +579,9 @@
private:
DoubleLoType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {}
+ : Cat2Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const DoubleLoType* instance_;
};
@@ -572,7 +599,9 @@
private:
DoubleHiType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {}
+ : Cat2Type(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
static const DoubleHiType* instance_;
};
@@ -637,7 +666,9 @@
public:
PreciseConstType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : ConstantType(constant, cache_id) {}
+ : ConstantType(constant, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsPreciseConstant() const OVERRIDE { return true; }
@@ -648,7 +679,9 @@
public:
PreciseConstLoType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : ConstantType(constant, cache_id) {}
+ : ConstantType(constant, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsPreciseConstantLo() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -657,7 +690,9 @@
public:
PreciseConstHiType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : ConstantType(constant, cache_id) {}
+ : ConstantType(constant, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsPreciseConstantHi() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -667,6 +702,7 @@
ImpreciseConstType(uint32_t constat, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constat, cache_id) {
+ CheckConstructorInvariants(this);
}
bool IsImpreciseConstant() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -676,7 +712,9 @@
public:
ImpreciseConstLoType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : ConstantType(constant, cache_id) {}
+ : ConstantType(constant, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsImpreciseConstantLo() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -685,7 +723,9 @@
public:
ImpreciseConstHiType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : ConstantType(constant, cache_id) {}
+ : ConstantType(constant, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsImpreciseConstantHi() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -718,7 +758,9 @@
const StringPiece& descriptor,
uint32_t allocation_pc, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
- : UninitializedType(klass, descriptor, allocation_pc, cache_id) {}
+ : UninitializedType(klass, descriptor, allocation_pc, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsUninitializedReference() const OVERRIDE { return true; }
@@ -735,9 +777,7 @@
uint32_t allocation_pc, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, allocation_pc, cache_id) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
bool IsUnresolvedAndUninitializedReference() const OVERRIDE { return true; }
@@ -747,7 +787,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
};
// Similar to UninitializedReferenceType but special case for the this argument
@@ -759,9 +799,7 @@
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, 0, cache_id) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
virtual bool IsUninitializedThisReference() const OVERRIDE { return true; }
@@ -771,7 +809,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
};
class UnresolvedUninitializedThisRefType FINAL : public UninitializedType {
@@ -780,9 +818,7 @@
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, 0, cache_id) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
bool IsUnresolvedAndUninitializedThisReference() const OVERRIDE { return true; }
@@ -792,7 +828,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
};
// A type of register holding a reference to an Object of type GetClass or a
@@ -801,7 +837,9 @@
public:
ReferenceType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {}
+ : RegType(klass, descriptor, cache_id) {
+ CheckConstructorInvariants(this);
+ }
bool IsReference() const OVERRIDE { return true; }
@@ -848,9 +886,7 @@
UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UnresolvedType(descriptor, cache_id) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
bool IsUnresolvedReference() const OVERRIDE { return true; }
@@ -860,7 +896,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
};
// Type representing the super-class of an unresolved type.
@@ -872,9 +908,7 @@
: UnresolvedType("", cache_id),
unresolved_child_id_(child_id),
reg_type_cache_(reg_type_cache) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
+ CheckConstructorInvariants(this);
}
bool IsUnresolvedSuperClass() const OVERRIDE { return true; }
@@ -889,7 +923,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
const uint16_t unresolved_child_id_;
const RegTypeCache* const reg_type_cache_;
@@ -925,7 +959,7 @@
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
const RegTypeCache* const reg_type_cache_;
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 000cf7c..d477ecd 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -68,13 +68,17 @@
return (it == dex_deps_.end()) ? nullptr : it->second.get();
}
+// Access flags that impact vdex verification.
+static constexpr uint32_t kAccVdexAccessFlags =
+ kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccInterface;
+
template <typename T>
uint16_t VerifierDeps::GetAccessFlags(T* element) {
static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
if (element == nullptr) {
return VerifierDeps::kUnresolvedMarker;
} else {
- uint16_t access_flags = Low16Bits(element->GetAccessFlags());
+ uint16_t access_flags = Low16Bits(element->GetAccessFlags()) & kAccVdexAccessFlags;
CHECK_NE(access_flags, VerifierDeps::kUnresolvedMarker);
return access_flags;
}
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 0dd82ab..194f4a1 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -127,6 +127,9 @@
Assert.assertEquals("I", /* Small latin dotless i */ "\u0131".toUpperCase());
Assert.assertEquals("abc", "a\u0131c".replace('\u0131', 'b'));
Assert.assertEquals("a\u0131c", "abc".replace('b', '\u0131'));
+
+ // Regression test for scratch register exhaustion in String.equals() intrinsic on arm64.
+ Assert.assertFalse(result.equals("Very long constant string, so that the known constant count field cannot be embedded in a CMP immediate instruction on arm64. Since it can hold 12-bit values, optionally shifted left by 12, let's go somewhere over 2^12, i.e. 4096. That should trigger the bug with or without string compression. 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"));
}
public static void testCompareToAndEquals() {
diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java
index 56b8c3e..8f79bd7 100644
--- a/test/155-java-set-resolved-type/src/Main.java
+++ b/test/155-java-set-resolved-type/src/Main.java
@@ -57,8 +57,8 @@
// we need to find TestInterface.
clearResolvedTypes(timpl);
- // Force intialization of TestClass2. This expects the interface type to be
- // resolved and found through simple lookup.
+ // Force intialization of TestImplementation. This expects the interface type
+ // to be resolved and found through simple lookup.
timpl.newInstance();
} catch (Throwable t) {
t.printStackTrace();
diff --git a/test/158-app-image-class-table/expected.txt b/test/158-app-image-class-table/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/158-app-image-class-table/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/158-app-image-class-table/info.txt b/test/158-app-image-class-table/info.txt
new file mode 100644
index 0000000..c844c8e
--- /dev/null
+++ b/test/158-app-image-class-table/info.txt
@@ -0,0 +1,3 @@
+Regression test for app image class table being erroneously omitted
+when it contains only boot image class loader classes while dex caches
+were written with references to these classes.
diff --git a/test/158-app-image-class-table/profile b/test/158-app-image-class-table/profile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/158-app-image-class-table/profile
diff --git a/test/158-app-image-class-table/run b/test/158-app-image-class-table/run
new file mode 100644
index 0000000..146e180
--- /dev/null
+++ b/test/158-app-image-class-table/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/158-app-image-class-table/src/Main.java b/test/158-app-image-class-table/src/Main.java
new file mode 100644
index 0000000..804468f
--- /dev/null
+++ b/test/158-app-image-class-table/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static String TEST_NAME = "158-app-image-class-table";
+
+ public static void main(String[] args) {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+ System.loadLibrary(args[0]);
+ } catch (ClassNotFoundException e) {
+ usingRI = true;
+ // Add expected JNI_OnLoad log line to match expected.txt.
+ System.out.println("JNI_OnLoad called");
+ }
+ try {
+ // Resolve but do not initialize TestImplementation. During the resolution,
+ // we see the Cloneable in the dex cache, so we do not try to look it up
+ // or resolve it.
+ Class<?> timpl =
+ Class.forName("TestImplementation", false, Main.class.getClassLoader());
+ // Clear the dex cache resolved types to force a proper lookup the next time
+ // we need to find TestInterface.
+ clearResolvedTypes(timpl);
+ // Force intialization of TestImplementation. This expects the interface type
+ // to be resolved and found through simple lookup.
+ timpl.newInstance();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public static void clearResolvedTypes(Class<?> c) {
+ if (!usingRI) {
+ nativeClearResolvedTypes(c);
+ }
+ }
+
+ private static boolean usingRI = false;
+
+ public static native void nativeClearResolvedTypes(Class<?> c);
+}
diff --git a/test/158-app-image-class-table/src/TestImplementation.java b/test/158-app-image-class-table/src/TestImplementation.java
new file mode 100644
index 0000000..558e587
--- /dev/null
+++ b/test/158-app-image-class-table/src/TestImplementation.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TestImplementation implements Cloneable {
+ public Object clone() {
+ return new TestImplementation();
+ }
+}
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index 4f34ec9..94aad9d 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -37,13 +37,20 @@
}
/// CHECK-START-ARM: int Main.and511(int) disassembly (after)
- /// CHECK: mov {{r\d+}}, #511
- /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK: ubfx {{r\d+}}, {{r\d+}}, #0, #9
public static int and511(int arg) {
return arg & 511;
}
+ /// CHECK-START-ARM: int Main.andF00D(int) disassembly (after)
+ /// CHECK: mov {{r\d+}}, #61453
+ /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+
+ public static int andF00D(int arg) {
+ return arg & 0xF00D;
+ }
+
/// CHECK-START-ARM: int Main.andNot15(int) disassembly (after)
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK: bic {{r\d+}}, {{r\d+}}, #0xf
@@ -114,19 +121,31 @@
}
/// CHECK-START-ARM: long Main.and511(long) disassembly (after)
- /// CHECK: mov {{r\d+}}, #511
+ /// CHECK: ubfx {{r\d+}}, {{r\d+}}, #0, #9
/// CHECK-NEXT: mov{{s?}} {{r\d+}}, #0
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
- /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
- /// CHECK-NEXT: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
- /// CHECK-NOT: and{{(\.w)?}}
- /// CHECK-NOT: bic{{(\.w)?}}
public static long and511(long arg) {
return arg & 511L;
}
+ /// CHECK-START-ARM: long Main.andF00D(long) disassembly (after)
+ /// CHECK: mov {{r\d+}}, #61453
+ /// CHECK-NEXT: mov{{s?}} {{r\d+}}, #0
+ /// CHECK-NOT: and{{(\.w)?}}
+ /// CHECK-NOT: bic{{(\.w)?}}
+ /// CHECK-NOT: ubfx
+ /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NEXT: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+ /// CHECK-NOT: and{{(\.w)?}}
+ /// CHECK-NOT: bic{{(\.w)?}}
+ /// CHECK-NOT: ubfx
+
+ public static long andF00D(long arg) {
+ return arg & 0xF00DL;
+ }
+
/// CHECK-START-ARM: long Main.andNot15(long) disassembly (after)
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK-NOT: and{{(\.w)?}}
@@ -631,6 +650,7 @@
int arg = 0x87654321;
assertIntEquals(and255(arg), 0x21);
assertIntEquals(and511(arg), 0x121);
+ assertIntEquals(andF00D(arg), 0x4001);
assertIntEquals(andNot15(arg), 0x87654320);
assertIntEquals(or255(arg), 0x876543ff);
assertIntEquals(or511(arg), 0x876543ff);
@@ -642,6 +662,7 @@
long longArg = 0x1234567887654321L;
assertLongEquals(and255(longArg), 0x21L);
assertLongEquals(and511(longArg), 0x121L);
+ assertLongEquals(andF00D(longArg), 0x4001L);
assertLongEquals(andNot15(longArg), 0x1234567887654320L);
assertLongEquals(and0xfffffff00000000f(longArg), 0x1234567000000001L);
assertLongEquals(or255(longArg), 0x12345678876543ffL);
diff --git a/test/638-checker-inline-caches/expected.txt b/test/638-checker-inline-caches/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/638-checker-inline-caches/expected.txt
diff --git a/test/638-checker-inline-caches/info.txt b/test/638-checker-inline-caches/info.txt
new file mode 100644
index 0000000..1fac628
--- /dev/null
+++ b/test/638-checker-inline-caches/info.txt
@@ -0,0 +1 @@
+Verify the use of inline caches in AOT mode.
diff --git a/test/638-checker-inline-caches/multidex.jpp b/test/638-checker-inline-caches/multidex.jpp
new file mode 100644
index 0000000..69a2cc1
--- /dev/null
+++ b/test/638-checker-inline-caches/multidex.jpp
@@ -0,0 +1,12 @@
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
+Super:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Super
+SubA:
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubA
+SubB
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubB
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
new file mode 100644
index 0000000..1ca6d7b
--- /dev/null
+++ b/test/638-checker-inline-caches/profile
@@ -0,0 +1,6 @@
+LMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
+LMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
+LMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
+LMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
+LMain;->inlineMissingTypes(LSuper;)I+missing_types
+LMain;->noInlineCache(LSuper;)I
diff --git a/test/638-checker-inline-caches/run b/test/638-checker-inline-caches/run
new file mode 100644
index 0000000..146e180
--- /dev/null
+++ b/test/638-checker-inline-caches/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/638-checker-inline-caches/src-multidex/SubC.java b/test/638-checker-inline-caches/src-multidex/SubC.java
new file mode 100644
index 0000000..f7e3c08
--- /dev/null
+++ b/test/638-checker-inline-caches/src-multidex/SubC.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SubC extends Super {
+ public int getValue() { return 24; }
+}
diff --git a/test/638-checker-inline-caches/src/Main.java b/test/638-checker-inline-caches/src/Main.java
new file mode 100644
index 0000000..2cee47e
--- /dev/null
+++ b/test/638-checker-inline-caches/src/Main.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SubA extends Super {
+ int getValue() { return 42; }
+}
+
+class SubB extends Super {
+ int getValue() { return 38; }
+}
+
+class SubD extends Super {
+ int getValue() { return 10; }
+}
+
+class SubE extends Super {
+ int getValue() { return -4; }
+}
+
+public class Main {
+
+ /// CHECK-START: int Main.inlineMonomorphicSubA(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlineMonomorphicSubA(Super) inliner (after)
+ /// CHECK-NOT: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlineMonomorphicSubA(Super) inliner (after)
+ /// CHECK: <<SubARet:i\d+>> IntConstant 42
+ /// CHECK: <<ObjClass:l\d+>> InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
+ /// CHECK: <<InlineClass:l\d+>> LoadClass class_name:SubA
+ /// CHECK: <<Test:z\d+>> NotEqual [<<InlineClass>>,<<ObjClass>>]
+ /// CHECK: Deoptimize [<<Test>>]
+ /// CHECK: Return [<<SubARet>>]
+ public static int inlineMonomorphicSubA(Super a) {
+ return a.getValue();
+ }
+
+ /// CHECK-START: int Main.inlinePolymophicSubASubB(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlinePolymophicSubASubB(Super) inliner (after)
+ /// CHECK-NOT: InvokeVirtual method_name:Super.getValue
+
+ // Note that the order in which the types are added to the inline cache in the profile matters.
+
+ /// CHECK-START: int Main.inlinePolymophicSubASubB(Super) inliner (after)
+ /// CHECK-DAG: <<SubARet:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<SubBRet:i\d+>> IntConstant 38
+ /// CHECK: <<ObjClassSubA:l\d+>> InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
+ /// CHECK: <<InlineClassSubA:l\d+>> LoadClass class_name:SubA
+ /// CHECK: <<TestSubA:z\d+>> NotEqual [<<InlineClassSubA>>,<<ObjClassSubA>>]
+ /// CHECK: If [<<TestSubA>>]
+
+ /// CHECK: <<ObjClassSubB:l\d+>> InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
+ /// CHECK: <<InlineClassSubB:l\d+>> LoadClass class_name:SubB
+ /// CHECK: <<TestSubB:z\d+>> NotEqual [<<InlineClassSubB>>,<<ObjClassSubB>>]
+ /// CHECK: Deoptimize [<<TestSubB>>]
+
+ /// CHECK: <<Ret:i\d+>> Phi [<<SubARet>>,<<SubBRet>>]
+ /// CHECK: Return [<<Ret>>]
+ public static int inlinePolymophicSubASubB(Super a) {
+ return a.getValue();
+ }
+
+ /// CHECK-START: int Main.inlinePolymophicCrossDexSubASubC(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlinePolymophicCrossDexSubASubC(Super) inliner (after)
+ /// CHECK-NOT: InvokeVirtual method_name:Super.getValue
+
+ // Note that the order in which the types are added to the inline cache in the profile matters.
+
+ /// CHECK-START: int Main.inlinePolymophicCrossDexSubASubC(Super) inliner (after)
+ /// CHECK-DAG: <<SubARet:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<SubCRet:i\d+>> IntConstant 24
+ /// CHECK: <<ObjClassSubA:l\d+>> InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
+ /// CHECK: <<InlineClassSubA:l\d+>> LoadClass class_name:SubA
+ /// CHECK: <<TestSubA:z\d+>> NotEqual [<<InlineClassSubA>>,<<ObjClassSubA>>]
+ /// CHECK: If [<<TestSubA>>]
+
+ /// CHECK: <<ObjClassSubC:l\d+>> InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
+ /// CHECK: <<InlineClassSubC:l\d+>> LoadClass class_name:SubC
+ /// CHECK: <<TestSubC:z\d+>> NotEqual [<<InlineClassSubC>>,<<ObjClassSubC>>]
+ /// CHECK: Deoptimize [<<TestSubC>>]
+
+ /// CHECK: <<Ret:i\d+>> Phi [<<SubARet>>,<<SubCRet>>]
+ /// CHECK: Return [<<Ret>>]
+ public static int inlinePolymophicCrossDexSubASubC(Super a) {
+ return a.getValue();
+ }
+
+ /// CHECK-START: int Main.inlineMegamorphic(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlineMegamorphic(Super) inliner (after)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+ public static int inlineMegamorphic(Super a) {
+ return a.getValue();
+ }
+
+ /// CHECK-START: int Main.inlineMissingTypes(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.inlineMissingTypes(Super) inliner (after)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+ public static int inlineMissingTypes(Super a) {
+ return a.getValue();
+ }
+
+ /// CHECK-START: int Main.noInlineCache(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+
+ /// CHECK-START: int Main.noInlineCache(Super) inliner (after)
+ /// CHECK: InvokeVirtual method_name:Super.getValue
+ public static int noInlineCache(Super a) {
+ return a.getValue();
+ }
+
+ public static void testInlineMonomorphic() {
+ if (inlineMonomorphicSubA(new SubA()) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ // Call with a different type than the one from the inline cache.
+ if (inlineMonomorphicSubA(new SubB()) != 38) {
+ throw new Error("Expected 38");
+ }
+ }
+
+ public static void testInlinePolymorhic() {
+ if (inlinePolymophicSubASubB(new SubA()) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (inlinePolymophicSubASubB(new SubB()) != 38) {
+ throw new Error("Expected 38");
+ }
+
+ // Call with a different type than the one from the inline cache.
+ if (inlinePolymophicSubASubB(new SubC()) != 24) {
+ throw new Error("Expected 25");
+ }
+
+ if (inlinePolymophicCrossDexSubASubC(new SubA()) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (inlinePolymophicCrossDexSubASubC(new SubC()) != 24) {
+ throw new Error("Expected 24");
+ }
+
+ // Call with a different type than the one from the inline cache.
+ if (inlinePolymophicCrossDexSubASubC(new SubB()) != 38) {
+ throw new Error("Expected 38");
+ }
+ }
+
+ public static void testInlineMegamorphic() {
+ if (inlineMegamorphic(new SubA()) != 42) {
+ throw new Error("Expected 42");
+ }
+ }
+
+
+ public static void testNoInlineCache() {
+ if (noInlineCache(new SubA()) != 42) {
+ throw new Error("Expected 42");
+ }
+ }
+
+ public static void main(String[] args) {
+ testInlineMonomorphic();
+ testInlinePolymorhic();
+ testInlineMegamorphic();
+ testNoInlineCache();
+ }
+
+}
diff --git a/test/638-checker-inline-caches/src/Super.java b/test/638-checker-inline-caches/src/Super.java
new file mode 100644
index 0000000..30cdf30
--- /dev/null
+++ b/test/638-checker-inline-caches/src/Super.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class Super {
+ abstract int getValue();
+}
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 0b17656..9166277 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -20,7 +20,7 @@
#include <stdio.h>
#include <string.h>
#include "base/macros.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 6177263..b85ed48 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -25,7 +25,7 @@
#include "art_method-inl.h"
#include "base/logging.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
#include "utils.h"
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 95eab0c..cc6f681 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -21,7 +21,7 @@
#include "base/logging.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index 7b26d79..5eed472 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -21,7 +21,7 @@
#include "base/logging.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 13c3562..f2532de 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -26,7 +26,7 @@
#include "android-base/stringprintf.h"
#include "base/logging.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedPrimitiveArray.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index 5bda7eb..48ce2e2 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -21,7 +21,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc
index 8f96ee6..45148f8 100644
--- a/test/908-gc-start-finish/gc_callbacks.cc
+++ b/test/908-gc-start-finish/gc_callbacks.cc
@@ -19,7 +19,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc
index adae844..67c7567 100644
--- a/test/909-attach-agent/attach.cc
+++ b/test/909-attach-agent/attach.cc
@@ -20,7 +20,7 @@
#include <stdio.h>
#include <string.h>
#include "base/macros.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
namespace art {
namespace Test909AttachAgent {
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index f60fabb..fdc4cdb 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -18,7 +18,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 68f6d8d..5a3a311 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -24,7 +24,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 3ccfe86..5bd34f6 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -20,7 +20,7 @@
#include "class_linker.h"
#include "jni.h"
#include "mirror/class_loader.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 39fa000..66fc7be 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -28,7 +28,7 @@
#include "jit/jit.h"
#include "jni.h"
#include "native_stack_dump.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
index 7d29912..c659126 100644
--- a/test/918-fields/fields.cc
+++ b/test/918-fields/fields.cc
@@ -18,7 +18,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc
index 0553a9d..ad1431e 100644
--- a/test/920-objects/objects.cc
+++ b/test/920-objects/objects.cc
@@ -18,7 +18,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc
index cb732c7..3fd274e 100644
--- a/test/922-properties/properties.cc
+++ b/test/922-properties/properties.cc
@@ -18,7 +18,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc
index 4baa530..131fc6a 100644
--- a/test/923-monitors/monitors.cc
+++ b/test/923-monitors/monitors.cc
@@ -18,7 +18,7 @@
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index f18d70e..716f59e 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -135,8 +135,12 @@
synchronized(cdl3_2) {
cdl3_1.countDown();
cdl3_2.await();
- Thread.yield();
- Thread.sleep(100);
+ // While the latch improves the chances to make good progress, scheduling might still be
+ // messy. Wait till we get the right Java-side Thread state.
+ do {
+ Thread.yield();
+ } while (t.getState() != Thread.State.BLOCKED);
+ Thread.sleep(10);
printThreadState(t);
}
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 0380433..14ea5af 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -20,7 +20,7 @@
#include "base/macros.h"
#include "base/logging.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc
index 6c6e835..2feaab0 100644
--- a/test/925-threadgroups/threadgroups.cc
+++ b/test/925-threadgroups/threadgroups.cc
@@ -20,7 +20,7 @@
#include "base/macros.h"
#include "base/logging.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc
index 58d5c27..7b1d5c3 100644
--- a/test/927-timers/timers.cc
+++ b/test/927-timers/timers.cc
@@ -20,7 +20,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
index 5123d3a..b5c0efd 100644
--- a/test/928-jni-table/jni_table.cc
+++ b/test/928-jni-table/jni_table.cc
@@ -17,7 +17,7 @@
#include <stdio.h>
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "base/logging.h"
#include "base/macros.h"
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
index d1c6984..ad7a053 100644
--- a/test/929-search/search.cc
+++ b/test/929-search/search.cc
@@ -20,7 +20,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc
index a488d9a..f8f9e48 100644
--- a/test/931-agent-thread/agent_thread.cc
+++ b/test/931-agent-thread/agent_thread.cc
@@ -21,7 +21,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
#include "thread-inl.h"
diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc
index 860d4b5..7043350 100644
--- a/test/933-misc-events/misc_events.cc
+++ b/test/933-misc-events/misc_events.cc
@@ -21,7 +21,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc
index 2286a46..3b19ca5 100644
--- a/test/936-search-onload/search_onload.cc
+++ b/test/936-search-onload/search_onload.cc
@@ -22,7 +22,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedUtfChars.h"
#include "ti-agent/common_helper.h"
diff --git a/test/944-transform-classloaders/classloader.cc b/test/944-transform-classloaders/classloader.cc
index 5fbd8e1..7cb3c08 100644
--- a/test/944-transform-classloaders/classloader.cc
+++ b/test/944-transform-classloaders/classloader.cc
@@ -16,8 +16,8 @@
#include "base/macros.h"
#include "jni.h"
+#include "jvmti.h"
#include "mirror/class-inl.h"
-#include "openjdkjvmti/jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
index 061e7af..442836b 100644
--- a/test/945-obsolete-native/obsolete_native.cc
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -24,7 +24,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
diff --git a/test/951-threaded-obsolete/expected.txt b/test/951-threaded-obsolete/expected.txt
new file mode 100644
index 0000000..83efda1
--- /dev/null
+++ b/test/951-threaded-obsolete/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/951-threaded-obsolete/info.txt b/test/951-threaded-obsolete/info.txt
new file mode 100644
index 0000000..e7ef4a2
--- /dev/null
+++ b/test/951-threaded-obsolete/info.txt
@@ -0,0 +1,4 @@
+Tests basic obsolete method support
+
+This test ensures that obsolete methods will work even if the obsolete method is
+on a different thread then where the redefinition was triggered.
diff --git a/test/951-threaded-obsolete/run b/test/951-threaded-obsolete/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/951-threaded-obsolete/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/951-threaded-obsolete/src/Main.java b/test/951-threaded-obsolete/src/Main.java
new file mode 100644
index 0000000..98e7236
--- /dev/null
+++ b/test/951-threaded-obsolete/src/Main.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+import java.util.concurrent.Semaphore;
+
+public class Main {
+ // class Transform {
+ // public void sayHi(Runnable r) {
+ // System.out.println("Hello - Transformed");
+ // r.run();
+ // System.out.println("Goodbye - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+ "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+ "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+ "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+ "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+ "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
+ "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+ "AQAPAAAAAgAQ");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+ "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+ "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+ "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+ "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+ "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
+ "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+ "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+ "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+ "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+ "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
+ "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+ "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+ "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+ public static void main(String[] args) {
+ // Semaphores to let each thread know where the other is. We could use barriers but semaphores
+ // mean we don't need to have the worker thread be waiting around.
+ final Semaphore sem_redefine_start = new Semaphore(0);
+ final Semaphore sem_redefine_end = new Semaphore(0);
+ // Create a thread to do the actual redefinition. We will just communicate through an
+ // atomic-integer.
+ new Thread(() -> {
+ try {
+ // Wait for the other thread to ask for redefinition.
+ sem_redefine_start.acquire();
+ // Do the redefinition.
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ // Allow the other thread to wake up if it is waiting.
+ sem_redefine_end.release();
+ } catch (InterruptedException e) {
+ throw new Error("unable to do redefinition", e);
+ }
+ }).start();
+
+ Transform t = new Transform();
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ try {
+ System.out.println("transforming calling function");
+ // Wake up the waiting thread.
+ sem_redefine_start.release();
+ // Wait for the other thread to finish with redefinition.
+ sem_redefine_end.acquire();
+ } catch (InterruptedException e) {
+ throw new Error("unable to do redefinition", e);
+ }
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+}
diff --git a/test/951-threaded-obsolete/src/Transform.java b/test/951-threaded-obsolete/src/Transform.java
new file mode 100644
index 0000000..8cda6cd
--- /dev/null
+++ b/test/951-threaded-obsolete/src/Transform.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+ public void sayHi(Runnable r) {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Hello" < "LTransform;" < "hello".
+ System.out.println("hello");
+ r.run();
+ System.out.println("goodbye");
+ }
+}
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
new file mode 100755
index 0000000..987066f
--- /dev/null
+++ b/test/980-redefine-object/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The number of paused background threads (and therefore InterruptedExceptions)
+# can change so we will just delete their lines from the log.
+
+sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt
new file mode 100644
index 0000000..6e9bce0
--- /dev/null
+++ b/test/980-redefine-object/expected.txt
@@ -0,0 +1,52 @@
+ Initializing and loading the TestWatcher class that will (eventually) be notified of object allocations
+ Allocating an j.l.Object before redefining Object class
+ Allocating a Transform before redefining Object class
+ Redefining the Object class to add a hook into the <init> method
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Allocating an j.l.Object after redefining Object class
+Object allocated of type 'Ljava/lang/Object;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Allocating a Transform after redefining Object class
+Object allocated of type 'LTransform;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Allocating an int[] after redefining Object class
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Allocating an array list
+Object allocated of type 'Ljava/util/ArrayList;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Adding a bunch of stuff to the array list
+Object allocated of type 'Ljava/lang/Object;'
+Object allocated of type 'Ljava/lang/Object;'
+Object allocated of type 'LTransform;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Allocating a linked list
+Object allocated of type 'Ljava/util/LinkedList;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Adding a bunch of stuff to the linked list
+Object allocated of type 'Ljava/lang/Object;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/lang/Object;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'LTransform;'
+Object allocated of type 'Ljava/util/LinkedList$Node;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Throwing from down 4 stack frames
+Object allocated of type 'Ljava/lang/Exception;'
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Exception caught.
+Object allocated of type 'Ljava/lang/StringBuilder;'
+Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+ Finishing test!
diff --git a/test/980-redefine-object/info.txt b/test/980-redefine-object/info.txt
new file mode 100644
index 0000000..f3e01b5
--- /dev/null
+++ b/test/980-redefine-object/info.txt
@@ -0,0 +1,23 @@
+Tests basic functions in the jvmti plugin.
+
+This tests that we are able to redefine methods/constructors on the
+java.lang.Object class at runtime.
+
+This also (indirectly) tests that we correctly handle reading annotations on
+obsolete methods. This is something that is not normally done since there is no
+way to get a reference to an obsolete method outside of the runtime but some
+annotations on the Object class are read by the runtime directly.
+
+NB This test cannot be run on the RI at the moment.
+
+If this test starts failing during the doCommonClassRedefinition call it is
+possible that the definition of Object contained in the base64 DEX_BYTES array
+has become stale and will need to be recreated. The only difference from the
+normal Object dex bytes is that (a) it contains only the bytes of the Object
+class itself, and (b) it adds an
+'invoke-static {p0}, Ljava/lang/Object;->NotifyConstructed(Ljava/lang/Object;)V'
+to the <init> function.
+
+It is also possible it could fail due to the pattern of allocations caused by
+doing string concatenation or printing changing. In this case you should simply
+update the expected.txt file.
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
new file mode 100644
index 0000000..daae087
--- /dev/null
+++ b/test/980-redefine-object/redefine_object.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <iostream>
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test980RedefineObjects {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) {
+ BindFunctionsOnClass(jvmti_env, env, target);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed(
+ JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
+ char* sig = nullptr;
+ char* generic_sig = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
+ &sig,
+ &generic_sig))) {
+ // Exception.
+ return;
+ }
+ std::cout << "Object allocated of type '" << sig << "'" << std::endl;
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig));
+}
+
+} // namespace Test980RedefineObjects
+} // namespace art
diff --git a/test/980-redefine-object/run b/test/980-redefine-object/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/980-redefine-object/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java
new file mode 100644
index 0000000..d15e688
--- /dev/null
+++ b/test/980-redefine-object/src-ex/TestWatcher.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.test;
+
+public class TestWatcher {
+ // NB This function is native since it is called in the Object.<init> method and so cannot cause
+ // any java allocations at all. The normal System.out.print* functions will cause allocations to
+ // occur so we cannot use them. This means the easiest way to report the object as being created
+ // is to go into native code and do it there.
+ public static native void NotifyConstructed(Object o);
+}
diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java
new file mode 100644
index 0000000..348951c
--- /dev/null
+++ b/test/980-redefine-object/src/Main.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.LinkedList;
+
+public class Main {
+
+ // TODO We should make this run on the RI.
+ /**
+ * This test cannot be run on the RI.
+ */
+ private static final byte[] CLASS_BYTES = new byte[0];
+
+ // TODO It might be a good idea to replace this hard-coded Object definition with a
+ // retransformation based test.
+ /**
+ * Base64 encoding of the following smali file.
+ *
+ * .class public Ljava/lang/Object;
+ * .source "Object.java"
+ * # instance fields
+ * .field private transient shadow$_klass_:Ljava/lang/Class;
+ * .annotation system Ldalvik/annotation/Signature;
+ * value = {
+ * "Ljava/lang/Class",
+ * "<*>;"
+ * }
+ * .end annotation
+ * .end field
+ *
+ * .field private transient shadow$_monitor_:I
+ * # direct methods
+ * .method public constructor <init>()V
+ * .registers 1
+ * .prologue
+ * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V
+ * return-void
+ * .end method
+ *
+ * .method static identityHashCode(Ljava/lang/Object;)I
+ * .registers 7
+ * .prologue
+ * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I
+ * const/high16 v3, -0x40000000 # -2.0f
+ * const/high16 v2, -0x80000000
+ * const v1, 0xfffffff
+ * const/high16 v4, -0x40000000 # -2.0f
+ * and-int/2addr v4, v0
+ * const/high16 v5, -0x80000000
+ * if-ne v4, v5, :cond_15
+ * const v4, 0xfffffff
+ * and-int/2addr v4, v0
+ * return v4
+ * :cond_15
+ * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I
+ * move-result v4
+ * return v4
+ * .end method
+ *
+ * .method private static native identityHashCodeNative(Ljava/lang/Object;)I
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ *
+ * .method private native internalClone()Ljava/lang/Object;
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ *
+ *
+ * # virtual methods
+ * .method protected clone()Ljava/lang/Object;
+ * .registers 4
+ * .annotation system Ldalvik/annotation/Throws;
+ * value = {
+ * Ljava/lang/CloneNotSupportedException;
+ * }
+ * .end annotation
+ *
+ * .prologue
+ * instance-of v0, p0, Ljava/lang/Cloneable;
+ * if-nez v0, :cond_2d
+ * new-instance v0, Ljava/lang/CloneNotSupportedException;
+ * new-instance v1, Ljava/lang/StringBuilder;
+ * invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
+ * const-string/jumbo v2, "Class "
+ * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v1
+ * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+ * move-result-object v2
+ * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;
+ * move-result-object v2
+ * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v1
+ * const-string/jumbo v2, " doesn\'t implement Cloneable"
+ * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v1
+ * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+ * move-result-object v1
+ * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V
+ * throw v0
+ * :cond_2d
+ * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object;
+ * move-result-object v0
+ * return-object v0
+ * .end method
+ *
+ * .method public equals(Ljava/lang/Object;)Z
+ * .registers 3
+ * .prologue
+ * if-ne p0, p1, :cond_4
+ * const/4 v0, 0x1
+ * :goto_3
+ * return v0
+ * :cond_4
+ * const/4 v0, 0x0
+ * goto :goto_3
+ * .end method
+ *
+ * .method protected finalize()V
+ * .registers 1
+ * .annotation system Ldalvik/annotation/Throws;
+ * value = {
+ * Ljava/lang/Throwable;
+ * }
+ * .end annotation
+ * .prologue
+ * return-void
+ * .end method
+ *
+ * .method public final getClass()Ljava/lang/Class;
+ * .registers 2
+ * .annotation system Ldalvik/annotation/Signature;
+ * value = {
+ * "()",
+ * "Ljava/lang/Class",
+ * "<*>;"
+ * }
+ * .end annotation
+ * .prologue
+ * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class;
+ * return-object v0
+ * .end method
+ *
+ * .method public hashCode()I
+ * .registers 2
+ * .prologue
+ * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
+ * move-result v0
+ * return v0
+ * .end method
+ *
+ * .method public final native notify()V
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ *
+ * .method public final native notifyAll()V
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ *
+ * .method public toString()Ljava/lang/String;
+ * .registers 3
+ * .prologue
+ * new-instance v0, Ljava/lang/StringBuilder;
+ * invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
+ * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+ * move-result-object v1
+ * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String;
+ * move-result-object v1
+ * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v0
+ * const-string/jumbo v1, "@"
+ * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v0
+ * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I
+ * move-result v1
+ * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String;
+ * move-result-object v1
+ * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+ * move-result-object v0
+ * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+ * move-result-object v0
+ * return-object v0
+ * .end method
+ *
+ * .method public final native wait()V
+ * .annotation system Ldalvik/annotation/Throws;
+ * value = {
+ * Ljava/lang/InterruptedException;
+ * }
+ * .end annotation
+ *
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ *
+ * .method public final wait(J)V
+ * .registers 4
+ * .annotation system Ldalvik/annotation/Throws;
+ * value = {
+ * Ljava/lang/InterruptedException;
+ * }
+ * .end annotation
+ * .prologue
+ * const/4 v0, 0x0
+ * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V
+ * return-void
+ * .end method
+ *
+ * .method public final native wait(JI)V
+ * .annotation system Ldalvik/annotation/Throws;
+ * value = {
+ * Ljava/lang/InterruptedException;
+ * }
+ * .end annotation
+ *
+ * .annotation build Ldalvik/annotation/optimization/FastNative;
+ * .end annotation
+ * .end method
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" +
+ "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" +
+ "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" +
+ "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" +
+ "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" +
+ "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" +
+ "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" +
+ "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" +
+ "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" +
+ "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" +
+ "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" +
+ "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" +
+ "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" +
+ "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" +
+ "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" +
+ "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" +
+ "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" +
+ "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" +
+ "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" +
+ "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" +
+ "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" +
+ "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" +
+ "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" +
+ "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" +
+ "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" +
+ "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" +
+ "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" +
+ "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" +
+ "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" +
+ "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" +
+ "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" +
+ "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" +
+ "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" +
+ "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" +
+ "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" +
+ "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" +
+ "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" +
+ "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" +
+ "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" +
+ "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" +
+ "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" +
+ "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" +
+ "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" +
+ "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" +
+ "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" +
+ "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" +
+ "AAAAEAAAAQAAAIgJAAA=");
+
+ private static final String LISTENER_LOCATION =
+ System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
+
+ public static void main(String[] args) {
+ doTest();
+ }
+
+ private static void ensureTestWatcherInitialized() {
+ try {
+ // Make sure the TestWatcher class can be found from the Object <init> function.
+ addToBootClassLoader(LISTENER_LOCATION);
+ // Load TestWatcher from the bootclassloader and make sure it is initialized.
+ Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
+ // Bind the native functions of testwatcher_class.
+ bindFunctionsForClass(testwatcher_class);
+ } catch (Exception e) {
+ throw new Error("Exception while making testwatcher", e);
+ }
+ }
+
+ // NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
+ // "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
+ private static void safePrintln(Object o) {
+ System.out.flush();
+ System.out.print("\t" + o + "\n");
+ System.out.flush();
+ }
+
+ private static void throwFrom(int depth) throws Exception {
+ if (depth <= 0) {
+ throw new Exception("Throwing the exception");
+ } else {
+ throwFrom(depth - 1);
+ }
+ }
+
+ public static void doTest() {
+ safePrintln("Initializing and loading the TestWatcher class that will (eventually) be " +
+ "notified of object allocations");
+ // Make sure the TestWatcher class is initialized before we do anything else.
+ ensureTestWatcherInitialized();
+ safePrintln("Allocating an j.l.Object before redefining Object class");
+ // Make sure these aren't shown.
+ Object o = new Object();
+ safePrintln("Allocating a Transform before redefining Object class");
+ Transform t = new Transform();
+
+ // Redefine the Object Class.
+ safePrintln("Redefining the Object class to add a hook into the <init> method");
+ doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES);
+
+ safePrintln("Allocating an j.l.Object after redefining Object class");
+ Object o2 = new Object();
+ safePrintln("Allocating a Transform after redefining Object class");
+ Transform t2 = new Transform();
+
+ // This shouldn't cause the Object constructor to be run.
+ safePrintln("Allocating an int[] after redefining Object class");
+ int[] abc = new int[12];
+
+ // Try adding stuff to an array list.
+ safePrintln("Allocating an array list");
+ ArrayList<Object> al = new ArrayList<>();
+ safePrintln("Adding a bunch of stuff to the array list");
+ al.add(new Object());
+ al.add(new Object());
+ al.add(o2);
+ al.add(o);
+ al.add(t);
+ al.add(t2);
+ al.add(new Transform());
+
+ // Try adding stuff to a LinkedList
+ safePrintln("Allocating a linked list");
+ LinkedList<Object> ll = new LinkedList<>();
+ safePrintln("Adding a bunch of stuff to the linked list");
+ ll.add(new Object());
+ ll.add(new Object());
+ ll.add(o2);
+ ll.add(o);
+ ll.add(t);
+ ll.add(t2);
+ ll.add(new Transform());
+
+ // Try making an exception.
+ safePrintln("Throwing from down 4 stack frames");
+ try {
+ throwFrom(4);
+ } catch (Exception e) {
+ safePrintln("Exception caught.");
+ }
+
+ safePrintln("Finishing test!");
+ }
+
+ private static native void addToBootClassLoader(String s);
+
+ private static native void bindFunctionsForClass(Class<?> target);
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/test/980-redefine-object/src/Transform.java b/test/980-redefine-object/src/Transform.java
new file mode 100644
index 0000000..23f67d9
--- /dev/null
+++ b/test/980-redefine-object/src/Transform.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform { }
diff --git a/test/Android.bp b/test/Android.bp
index 00c890a..3bb3ef8 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -275,10 +275,12 @@
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
"945-obsolete-native/obsolete_native.cc",
+ "980-redefine-object/redefine_object.cc",
],
shared_libs: [
"libbase",
],
+ header_libs: ["libopenjdkjvmti_headers"],
}
art_cc_test_library {
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 9d1f8a2..e4e571c 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -63,6 +63,7 @@
TEST_IS_NDEBUG="n"
APP_IMAGE="y"
VDEX_FILTER=""
+PROFILE="n"
# if "y", run 'sync' before dalvikvm to make sure all files from
# build step (e.g. dex2oat) were finished writing.
@@ -269,6 +270,9 @@
elif [ "x$1" = "x--sync" ]; then
SYNC_BEFORE_RUN="y"
shift
+ elif [ "x$1" = "x--profile" ]; then
+ PROFILE="y"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -511,12 +515,23 @@
exit 1
fi
+profman_cmdline="true"
dex2oat_cmdline="true"
vdex_cmdline="true"
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
sync_cmdline="true"
+if [ "$PROFILE" = "y" ]; then
+ profman_cmdline="${ANDROID_ROOT}/bin/profman \
+ --apk=$DEX_LOCATION/$TEST_NAME.jar \
+ --dex-location=$DEX_LOCATION/$TEST_NAME.jar \
+ --create-profile-from=$DEX_LOCATION/profile \
+ --reference-profile-file=$DEX_LOCATION/$TEST_NAME.prof"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
+ FLAGS="${FLAGS} -Xcompiler-option --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
+fi
+
if [ "$PREBUILD" = "y" ]; then
mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
@@ -594,6 +609,7 @@
dex2oat_cmdline=$(echo $dex2oat_cmdline)
dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
vdex_cmdline=$(echo $vdex_cmdline)
+profman_cmdline=$(echo $profman_cmdline)
if [ "$HOST" = "n" ]; then
adb root > /dev/null
@@ -603,11 +619,18 @@
adb shell mkdir -p $DEX_LOCATION
adb push $TEST_NAME.jar $DEX_LOCATION
adb push $TEST_NAME-ex.jar $DEX_LOCATION
+ if [ "$PROFILE" = "y" ]; then
+ adb push profile $DEX_LOCATION
+ fi
else
adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+ if [ "$PROFILE" = "y" ]; then
+ adb push profile $DEX_LOCATION >/dev/null 2>&1
+ fi
+
fi
LD_LIBRARY_PATH=/data/$TEST_DIRECTORY/art/$ISA
@@ -634,6 +657,7 @@
mkdir -p ${mkdir_locations} && \
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
export PATH=$ANDROID_ROOT/bin:$PATH && \
+ $profman_cmdline && \
$dex2oat_cmdline && \
$vdex_cmdline && \
$strip_cmdline && \
@@ -710,13 +734,14 @@
fi
if [ "$DEV_MODE" = "y" ]; then
- echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
+ echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
rm -rf ${DEX_LOCATION}/dalvik-cache/
mkdir -p ${mkdir_locations} || exit 1
+ $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 50d70f1..535b94f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -103,7 +103,8 @@
{
"test": "152-dead-large-object",
"variant": "gcstress",
- "description": ["152-dead-large-object requires a heap larger than what gcstress uses."]
+ "description": ["152-dead-large-object requires a heap larger than what gcstress uses."],
+ "bug": "http://b/35800768"
},
{
"tests": ["908-gc-start-finish",
@@ -128,9 +129,10 @@
"variant": "gcstress"
},
{
- "tests": "154-gc-loop",
- "variant": "gcstress | jit",
- "description": ["154-gc-loop depends GC not happening too often"]
+ "test": "154-gc-loop",
+ "variant": "gcstress | jit & debug",
+ "description": ["154-gc-loop depends GC not happening too often"],
+ "bug": "http://b/35917229"
},
{
"test": "115-native-bridge",
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 0cd1dde..4c519ae 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -14,15 +14,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+"""Build and run go/ab/git_master-art-host target
+
+Provided with a target name, the script setup the environment for
+building the test target by taking config information from
+from target_config.py.
+
+If the target field is defined in the configuration for the target, it
+invokes `make` to build the target, otherwise, it assumes
+that the its is a run-test target, and invokes testrunner.py
+script for building and running the run-tests.
+"""
+
import argparse
import os
import subprocess
+import sys
from target_config import target_config
import env
parser = argparse.ArgumentParser()
-parser.add_argument('--build-target', required=True, dest='build_target')
+parser.add_argument('build_target')
parser.add_argument('-j', default='1', dest='n_threads')
options = parser.parse_args()
@@ -49,6 +62,7 @@
run_test_command += target.get('flags', [])
run_test_command += ['-j', str(n_threads)]
run_test_command += ['-b']
+ run_test_command += ['--host']
run_test_command += ['--verbose']
print run_test_command
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 5387d6a..1af2ae7 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -51,7 +51,8 @@
'flags': ['--jit',
'--gcstress'],
'env' : {
- 'ART_USE_READ_BARRIER' : 'false'
+ 'ART_USE_READ_BARRIER' : 'false',
+ 'ART_DEFAULT_GC_TYPE' : 'SS'
}
},
'art-read-barrier' : {
@@ -185,20 +186,20 @@
}
},
'art-gtest' : {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env' : {
'ART_USE_READ_BARRIER' : 'true'
}
},
'art-gtest-read-barrier': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env' : {
'ART_USE_READ_BARRIER' : 'true',
'ART_HEAP_POISONING' : 'true'
}
},
'art-gtest-read-barrier-table-lookup': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env': {
'ART_USE_READ_BARRIER' : 'true',
'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP',
@@ -206,21 +207,21 @@
}
},
'art-gtest-ss-gc': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-gss-gc': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env' : {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-ss-gc-tlab': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_TLAB' : 'true',
@@ -228,13 +229,20 @@
}
},
'art-gtest-gss-gc-tlab': {
- 'target' : 'test-art-gtest',
+ 'target' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_TLAB' : 'true',
'ART_USE_READ_BARRIER' : 'false'
}
},
+ 'art-gtest-debug-gc' : {
+ 'target' : 'test-art-host-gtest',
+ 'env' : {
+ 'ART_TEST_DEBUG_GC' : 'true',
+ 'ART_USE_READ_BARRIER' : 'false'
+ }
+ },
'art-gtest-valgrind32': {
'target' : 'valgrind-test-art-host32',
'env': {
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index ea6359e..6316a9c 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -25,7 +25,7 @@
#include "art_method.h"
#include "jni.h"
#include "jni_internal.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "scoped_thread_state_change-inl.h"
#include "ScopedLocalRef.h"
#include "stack.h"
@@ -520,11 +520,14 @@
LOG(FATAL) << "Could not load " << class_name;
}
}
+ BindFunctionsOnClass(jenv, env, klass.get());
+}
+void BindFunctionsOnClass(jvmtiEnv* jenv, JNIEnv* env, jclass klass) {
// Use JVMTI to get the methods.
jint method_count;
jmethodID* methods;
- jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods);
+ jvmtiError methods_result = jenv->GetClassMethods(klass, &method_count, &methods);
if (methods_result != JVMTI_ERROR_NONE) {
LOG(FATAL) << "Could not get methods";
}
@@ -538,7 +541,7 @@
}
constexpr jint kNative = static_cast<jint>(kAccNative);
if ((modifiers & kNative) != 0) {
- BindMethod(jenv, env, klass.get(), methods[i]);
+ BindMethod(jenv, env, klass, methods[i]);
}
}
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index 0318501..f10356d 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -18,7 +18,7 @@
#define ART_TEST_TI_AGENT_COMMON_HELPER_H_
#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
#include "ScopedLocalRef.h"
namespace art {
@@ -81,6 +81,7 @@
//
// This will abort on failure.
void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name);
+void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass);
} // namespace art
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 351857d..fddae3a 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -18,8 +18,6 @@
#include <jni.h>
#include <stdio.h>
-// TODO I don't know?
-#include "openjdkjvmti/jvmti.h"
#include "art_method-inl.h"
#include "base/logging.h"
diff --git a/test/ti-agent/common_load.h b/test/ti-agent/common_load.h
index d254421..e79a006 100644
--- a/test/ti-agent/common_load.h
+++ b/test/ti-agent/common_load.h
@@ -17,8 +17,7 @@
#ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_
#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
-#include "jni.h"
-#include "openjdkjvmti/jvmti.h"
+#include "jvmti.h"
namespace art {