Merge "Revert^2 "Remove support for Valgrind in ART.""
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index f2002a0..f2a942f 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -210,8 +210,7 @@
code_info.reset(new CodeInfo(mi->code_info));
for (size_t s = 0; s < code_info->GetNumberOfStackMaps(); ++s) {
const StackMap stack_map = code_info->GetStackMapAt(s);
- dex_reg_maps.push_back(code_info->GetDexRegisterMapOf(
- stack_map, accessor.RegistersSize()));
+ dex_reg_maps.push_back(code_info->GetDexRegisterMapOf(stack_map));
}
}
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 8cb4e55..4009acb 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -147,7 +147,7 @@
DexRegisterLocation reg_hi = DexRegisterLocation::None();
DCHECK_LT(stack_map_index, dex_register_maps.size());
DexRegisterMap dex_register_map = dex_register_maps[stack_map_index];
- DCHECK(dex_register_map.IsValid());
+ DCHECK(!dex_register_map.empty());
CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item);
reg_lo = dex_register_map.GetDexRegisterLocation(vreg);
if (is64bitValue) {
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 1fe42ad..d4a9ba5 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -82,9 +82,8 @@
ASSERT_NE(0, cmp);
// Unquicken the dex file.
- for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
+ for (ClassAccessor accessor : updated_dex_file->GetClasses()) {
// Unquicken each method.
- ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i));
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(
method.GetReference());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 653e9ed..6cb3936 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1685,16 +1685,14 @@
bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file,
uint16_t class_def_idx) const {
- ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx));
- bool has_is_final = false;
+ ClassAccessor accessor(dex_file, class_def_idx);
// We require a constructor barrier if there are final instance fields.
- accessor.VisitFields(/*static*/ VoidFunctor(),
- [&](const ClassAccessor::Field& field) {
+ for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
if (field.IsFinal()) {
- has_is_final = true;
+ return true;
}
- });
- return has_is_final;
+ }
+ return false;
}
class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor {
@@ -1744,7 +1742,7 @@
// fields are assigned within the lock held for class initialization.
bool requires_constructor_barrier = false;
- ClassAccessor accessor(dex_file, class_def);
+ ClassAccessor accessor(dex_file, class_def_index);
// Optionally resolve fields and methods and figure out if we need a constructor barrier.
auto method_visitor = [&](const ClassAccessor::Method& method)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1926,13 +1924,12 @@
// Fetch the list of unverified classes.
const std::set<dex::TypeIndex>& unverified_classes =
verifier_deps->GetUnverifiedClasses(*dex_file);
- uint32_t class_def_idx = 0u;
for (ClassAccessor accessor : dex_file->GetClasses()) {
if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) {
if (compiler_only_verifies) {
// Just update the compiled_classes_ map. The compiler doesn't need to resolve
// the type.
- ClassReference ref(dex_file, class_def_idx);
+ ClassReference ref(dex_file, accessor.GetClassDefIndex());
const ClassStatus existing = ClassStatus::kNotReady;
ClassStateTable::InsertResult result =
compiled_classes_.Insert(ref, existing, ClassStatus::kVerified);
@@ -1959,7 +1956,6 @@
class_loader,
soa.Self());
}
- ++class_def_idx;
}
}
return true;
@@ -2700,7 +2696,7 @@
jobject jclass_loader = context.GetClassLoader();
ClassReference ref(&dex_file, class_def_index);
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- ClassAccessor accessor(dex_file, class_def);
+ ClassAccessor accessor(dex_file, class_def_index);
// Skip compiling classes with generic verifier failures since they will still fail at runtime
if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
return;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 4791fa3..b3feb78 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -516,7 +516,7 @@
locations->AddTemp(visitor->GetMethodLocation());
break;
}
- } else {
+ } else if (!invoke->IsInvokePolymorphic()) {
locations->AddTemp(visitor->GetMethodLocation());
}
}
@@ -544,6 +544,7 @@
case kVirtual:
case kInterface:
case kPolymorphic:
+ case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
@@ -572,6 +573,7 @@
entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck;
break;
case kPolymorphic:
+ case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
@@ -579,11 +581,19 @@
}
void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) {
- MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast<int32_t>(invoke->GetType()));
+ // invoke-polymorphic does not use a temporary to convey any additional information (e.g. a
+ // method index) since it requires multiple info from the instruction (registers A, B, H). Not
+ // using the reservation has no effect on the registers used in the runtime call.
QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic;
InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
}
+void CodeGenerator::GenerateInvokeCustomCall(HInvokeCustom* invoke) {
+ MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetCallSiteIndex());
+ QuickEntrypointEnum entrypoint = kQuickInvokeCustom;
+ InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
+}
+
void CodeGenerator::CreateUnresolvedFieldLocationSummary(
HInstruction* field_access,
DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index a340446..b3c29aa 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -542,10 +542,13 @@
void GenerateInvokeStaticOrDirectRuntimeCall(
HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path);
+
void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke);
void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke);
+ void GenerateInvokeCustomCall(HInvokeCustom* invoke);
+
void CreateUnresolvedFieldLocationSummary(
HInstruction* field_access,
DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 6f173e1..5f0533c 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4695,6 +4695,15 @@
codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
+void LocationsBuilderARM64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
+}
+
vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch(
uint32_t boot_image_offset,
vixl::aarch64::Label* adrp_label) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 859e159..91c1315 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3742,6 +3742,15 @@
codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
}
+void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
+}
+
void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
@@ -5493,7 +5502,7 @@
codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
}
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
}
void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
@@ -5513,7 +5522,7 @@
codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
DCHECK(!codegen_->IsLeafMethod());
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
}
void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
@@ -7084,7 +7093,7 @@
return;
}
GenerateSuspendCheck(instruction, nullptr);
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
}
void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
@@ -7437,7 +7446,7 @@
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
codegen_->GenerateLoadClassRuntimeCall(cls);
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -7523,7 +7532,7 @@
} else {
__ Bind(slow_path->GetExitLabel());
}
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
}
}
@@ -7732,7 +7741,7 @@
codegen_->AddSlowPath(slow_path);
__ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
return;
}
case HLoadString::LoadKind::kJitTableAddress: {
@@ -7754,7 +7763,7 @@
__ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
}
static int32_t GetExceptionTlsOffset() {
@@ -8384,7 +8393,7 @@
} else {
CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
}
- codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
}
void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
@@ -8883,7 +8892,7 @@
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
- MaybeGenerateMarkingRegisterCheck(/* code */ 18);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 19);
}
void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -8963,7 +8972,7 @@
narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
: BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
}
- MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip));
+ MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
return;
}
@@ -9041,7 +9050,7 @@
DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
}
- MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
+ MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip));
return;
}
@@ -9095,7 +9104,7 @@
// Fast path: the GC is not marking: just load the reference.
GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
__ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ 21);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 22);
}
void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
@@ -9150,7 +9159,7 @@
// Fast path: the GC is not marking: nothing to do (the field is
// up-to-date, and we don't need to load the reference).
__ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ 22);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 23);
}
void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8be84a1..507db36 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -7795,6 +7795,14 @@
codegen_->GenerateInvokePolymorphicCall(invoke);
}
+void LocationsBuilderMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorMIPS::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+}
+
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
if (invoke->GetLocations()->Intrinsified()) {
IntrinsicCodeGeneratorMIPS intrinsic(codegen);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index cd9e0e5..08a6512 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -5908,6 +5908,14 @@
codegen_->GenerateInvokePolymorphicCall(invoke);
}
+void LocationsBuilderMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+}
+
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
if (invoke->GetLocations()->Intrinsified()) {
IntrinsicCodeGeneratorMIPS64 intrinsic(codegen);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 9e31538..9f42ac7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2311,6 +2311,14 @@
codegen_->GenerateInvokePolymorphicCall(invoke);
}
+void LocationsBuilderX86::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+}
+
void LocationsBuilderX86::VisitNeg(HNeg* neg) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index f739704..05194b1 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2501,6 +2501,14 @@
codegen_->GenerateInvokePolymorphicCall(invoke);
}
+void LocationsBuilderX86_64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeCustom(HInvokeCustom* invoke) {
+ codegen_->GenerateInvokeCustomCall(invoke);
+}
+
void LocationsBuilderX86_64::VisitNeg(HNeg* neg) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 6900cd8..7dd756e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -460,9 +460,10 @@
bool HInliner::TryInline(HInvoke* invoke_instruction) {
if (invoke_instruction->IsInvokeUnresolved() ||
- invoke_instruction->IsInvokePolymorphic()) {
- return false; // Don't bother to move further if we know the method is unresolved or an
- // invoke-polymorphic.
+ invoke_instruction->IsInvokePolymorphic() ||
+ invoke_instruction->IsInvokeCustom()) {
+ return false; // Don't bother to move further if we know the method is unresolved or the
+ // invocation is polymorphic (invoke-{polymorphic,custom}).
}
ScopedObjectAccess soa(Thread::Current());
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 24dc2ee..8a347df 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -449,11 +449,7 @@
target_method,
HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs);
- HandleInvoke(invoke,
- operands,
- dex_file_->GetMethodShorty(method_idx),
- /* clinit_check */ nullptr,
- /* is_unresolved */ false);
+ HandleInvoke(invoke, operands, dex_file_->GetMethodShorty(method_idx), /* is_unresolved */ false);
// Add the return instruction.
if (return_type_ == DataType::Type::kVoid) {
@@ -916,11 +912,11 @@
uint32_t method_idx,
const InstructionOperands& operands) {
InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
- const char* descriptor = dex_file_->GetMethodShorty(method_idx);
- DataType::Type return_type = DataType::FromShorty(descriptor[0]);
+ const char* shorty = dex_file_->GetMethodShorty(method_idx);
+ DataType::Type return_type = DataType::FromShorty(shorty[0]);
// Remove the return type from the 'proto'.
- size_t number_of_arguments = strlen(descriptor) - 1;
+ size_t number_of_arguments = strlen(shorty) - 1;
if (invoke_type != kStatic) { // instance call
// One extra argument for 'this'.
number_of_arguments++;
@@ -937,11 +933,7 @@
dex_pc,
method_idx,
invoke_type);
- return HandleInvoke(invoke,
- operands,
- descriptor,
- nullptr /* clinit_check */,
- true /* is_unresolved */);
+ return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ true);
}
// Replace calls to String.<init> with StringFactory.
@@ -968,7 +960,7 @@
invoke_type,
target_method,
HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
- return HandleStringInit(invoke, operands, descriptor);
+ return HandleStringInit(invoke, operands, shorty);
}
// Potential class initialization check, in the case of a static method call.
@@ -1028,29 +1020,39 @@
resolved_method,
ImTable::GetImtIndex(resolved_method));
}
-
- return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */);
+ return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false, clinit_check);
}
-bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED,
- uint32_t dex_pc,
+bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc,
uint32_t method_idx,
dex::ProtoIndex proto_idx,
const InstructionOperands& operands) {
- const char* descriptor = dex_file_->GetShorty(proto_idx);
- DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands());
- DataType::Type return_type = DataType::FromShorty(descriptor[0]);
- size_t number_of_arguments = strlen(descriptor);
+ const char* shorty = dex_file_->GetShorty(proto_idx);
+ DCHECK_EQ(1 + ArtMethod::NumArgRegisters(shorty), operands.GetNumberOfOperands());
+ DataType::Type return_type = DataType::FromShorty(shorty[0]);
+ size_t number_of_arguments = strlen(shorty);
HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_,
number_of_arguments,
return_type,
dex_pc,
method_idx);
- return HandleInvoke(invoke,
- operands,
- descriptor,
- nullptr /* clinit_check */,
- false /* is_unresolved */);
+ return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false);
+}
+
+
+bool HInstructionBuilder::BuildInvokeCustom(uint32_t dex_pc,
+ uint32_t call_site_idx,
+ const InstructionOperands& operands) {
+ dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx);
+ const char* shorty = dex_file_->GetShorty(proto_idx);
+ DataType::Type return_type = DataType::FromShorty(shorty[0]);
+ size_t number_of_arguments = strlen(shorty) - 1;
+ HInvoke* invoke = new (allocator_) HInvokeCustom(allocator_,
+ number_of_arguments,
+ call_site_idx,
+ return_type,
+ dex_pc);
+ return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false);
}
HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) {
@@ -1197,10 +1199,10 @@
bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor,
+ const char* shorty,
size_t start_index,
size_t* argument_index) {
- uint32_t descriptor_index = 1; // Skip the return type.
+ uint32_t shorty_index = 1; // Skip the return type.
const size_t number_of_operands = operands.GetNumberOfOperands();
for (size_t i = start_index;
// Make sure we don't go over the expected arguments or over the number of
@@ -1208,7 +1210,7 @@
// it hasn't been properly checked.
(i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments());
i++, (*argument_index)++) {
- DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]);
+ DataType::Type type = DataType::FromShorty(shorty[shorty_index++]);
bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64);
if (is_wide && ((i + 1 == number_of_operands) ||
(operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) {
@@ -1250,9 +1252,9 @@
bool HInstructionBuilder::HandleInvoke(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor,
- HClinitCheck* clinit_check,
- bool is_unresolved) {
+ const char* shorty,
+ bool is_unresolved,
+ HClinitCheck* clinit_check) {
DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
size_t start_index = 0;
@@ -1267,7 +1269,7 @@
argument_index = 1;
}
- if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
+ if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) {
return false;
}
@@ -1288,13 +1290,13 @@
bool HInstructionBuilder::HandleStringInit(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor) {
+ const char* shorty) {
DCHECK(invoke->IsInvokeStaticOrDirect());
DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
size_t start_index = 1;
size_t argument_index = 0;
- if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) {
+ if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) {
return false;
}
@@ -1313,7 +1315,8 @@
// The only reason a HPhi can flow in a String.<init> is when there is an
// irreducible loop, which will create HPhi for all dex registers at loop entry.
DCHECK(arg_this->IsPhi());
- DCHECK(graph_->HasIrreducibleLoops());
+ // TODO(b/109666561): Re-enable.
+ // DCHECK(graph_->HasIrreducibleLoops());
// Don't bother compiling a method in that situation. While we could look at all
// phis related to the HNewInstance, it's not worth the trouble.
MaybeRecordStat(compilation_stats_,
@@ -2144,14 +2147,28 @@
uint32_t args[5];
uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
- return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
+ return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands);
}
case Instruction::INVOKE_POLYMORPHIC_RANGE: {
uint16_t method_idx = instruction.VRegB_4rcc();
dex::ProtoIndex proto_idx(instruction.VRegH_4rcc());
RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc());
- return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands);
+ return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands);
+ }
+
+ case Instruction::INVOKE_CUSTOM: {
+ uint16_t call_site_idx = instruction.VRegB_35c();
+ uint32_t args[5];
+ uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
+ VarArgsInstructionOperands operands(args, number_of_vreg_arguments);
+ return BuildInvokeCustom(dex_pc, call_site_idx, operands);
+ }
+
+ case Instruction::INVOKE_CUSTOM_RANGE: {
+ uint16_t call_site_idx = instruction.VRegB_3rc();
+ RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc());
+ return BuildInvokeCustom(dex_pc, call_site_idx, operands);
}
case Instruction::NEG_INT: {
@@ -2933,7 +2950,21 @@
break;
}
- default:
+ case Instruction::UNUSED_3E:
+ case Instruction::UNUSED_3F:
+ case Instruction::UNUSED_40:
+ case Instruction::UNUSED_41:
+ case Instruction::UNUSED_42:
+ case Instruction::UNUSED_43:
+ case Instruction::UNUSED_79:
+ case Instruction::UNUSED_7A:
+ case Instruction::UNUSED_F3:
+ case Instruction::UNUSED_F4:
+ case Instruction::UNUSED_F5:
+ case Instruction::UNUSED_F6:
+ case Instruction::UNUSED_F7:
+ case Instruction::UNUSED_F8:
+ case Instruction::UNUSED_F9: {
VLOG(compiler) << "Did not compile "
<< dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
<< " because of unhandled instruction "
@@ -2941,6 +2972,7 @@
MaybeRecordStat(compilation_stats_,
MethodCompilationStat::kNotCompiledUnhandledInstruction);
return false;
+ }
}
return true;
} // NOLINT(readability/fn_size)
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 2218a69..af7092a 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -173,12 +173,17 @@
// Builds an invocation node for invoke-polymorphic and returns whether the
// instruction is supported.
- bool BuildInvokePolymorphic(const Instruction& instruction,
- uint32_t dex_pc,
+ bool BuildInvokePolymorphic(uint32_t dex_pc,
uint32_t method_idx,
dex::ProtoIndex proto_idx,
const InstructionOperands& operands);
+ // Builds an invocation node for invoke-custom and returns whether the
+ // instruction is supported.
+ bool BuildInvokeCustom(uint32_t dex_pc,
+ uint32_t call_site_idx,
+ const InstructionOperands& operands);
+
// Builds a new array node and the instructions that fill it.
HNewArray* BuildFilledNewArray(uint32_t dex_pc,
dex::TypeIndex type_index,
@@ -253,19 +258,19 @@
bool SetupInvokeArguments(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor,
+ const char* shorty,
size_t start_index,
size_t* argument_index);
bool HandleInvoke(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor,
- HClinitCheck* clinit_check,
- bool is_unresolved);
+ const char* shorty,
+ bool is_unresolved,
+ HClinitCheck* clinit_check = nullptr);
bool HandleStringInit(HInvoke* invoke,
const InstructionOperands& operands,
- const char* descriptor);
+ const char* shorty);
void HandleStringInitResult(HInvokeStaticOrDirect* invoke);
HClinitCheck* ProcessClinitCheckForInvoke(
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 056f533..f0c91f3 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -142,6 +142,7 @@
case kSuper:
case kInterface:
case kPolymorphic:
+ case kCustom:
return false;
}
LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type;
@@ -272,34 +273,33 @@
ClassLinker* class_linker = runtime->GetClassLinker();
gc::Heap* heap = runtime->GetHeap();
IntegerValueOfInfo info;
- info.integer_cache =
- class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;").Ptr();
- if (info.integer_cache == nullptr) {
- self->ClearException();
+ info.integer_cache = class_linker->LookupClass(self,
+ "Ljava/lang/Integer$IntegerCache;",
+ /* class_loader */ nullptr).Ptr();
+ if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) {
+ // Optimization only works if the class is initialized.
return info;
}
- if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
- // Optimization only works if the class is initialized and in the boot image.
+ if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) {
+ // Optimization only works if the class is in the boot image.
+ // TODO: Implement the intrinsic for boot image compilation.
return info;
}
- info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;").Ptr();
- if (info.integer == nullptr) {
- self->ClearException();
- return info;
- }
- if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
- // Optimization only works if the class is initialized and in the boot image.
+ info.integer =
+ class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr();
+ DCHECK(info.integer != nullptr);
+ DCHECK(info.integer->IsInitialized()); // Must be initialized since IntegerCache is initialized.
+ if (!heap->ObjectIsInBootImageSpace(info.integer)) {
+ // Optimization only works if the class is in the boot image.
return info;
}
ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
- if (field == nullptr) {
- return info;
- }
+ CHECK(field != nullptr);
info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
field->GetObject(info.integer_cache).Ptr());
if (info.cache == nullptr) {
- return info;
+ return info; // Did someone mess up the IntegerCache using reflection?
}
if (!heap->ObjectIsInBootImageSpace(info.cache)) {
@@ -308,21 +308,15 @@
}
field = info.integer->FindDeclaredInstanceField("value", "I");
- if (field == nullptr) {
- return info;
- }
+ CHECK(field != nullptr);
info.value_offset = field->GetOffset().Int32Value();
field = info.integer_cache->FindDeclaredStaticField("low", "I");
- if (field == nullptr) {
- return info;
- }
+ CHECK(field != nullptr);
info.low = field->GetInt(info.integer_cache);
field = info.integer_cache->FindDeclaredStaticField("high", "I");
- if (field == nullptr) {
- return info;
- }
+ CHECK(field != nullptr);
info.high = field->GetInt(info.integer_cache);
DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3fd5b6b..2037879 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1380,6 +1380,7 @@
M(InvokeStaticOrDirect, Invoke) \
M(InvokeVirtual, Invoke) \
M(InvokePolymorphic, Invoke) \
+ M(InvokeCustom, Invoke) \
M(LessThan, Condition) \
M(LessThanOrEqual, Condition) \
M(LoadClass, Instruction) \
@@ -4382,6 +4383,38 @@
DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic);
};
+class HInvokeCustom FINAL : public HInvoke {
+ public:
+ HInvokeCustom(ArenaAllocator* allocator,
+ uint32_t number_of_arguments,
+ uint32_t call_site_index,
+ DataType::Type return_type,
+ uint32_t dex_pc)
+ : HInvoke(kInvokeCustom,
+ allocator,
+ number_of_arguments,
+ /* number_of_other_inputs */ 0u,
+ return_type,
+ dex_pc,
+ /* dex_method_index */ dex::kDexNoIndex,
+ /* resolved_method */ nullptr,
+ kStatic),
+ call_site_index_(call_site_index) {
+ }
+
+ uint32_t GetCallSiteIndex() const { return call_site_index_; }
+
+ bool IsClonable() const OVERRIDE { return true; }
+
+ DECLARE_INSTRUCTION(InvokeCustom);
+
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokeCustom);
+
+ private:
+ uint32_t call_site_index_;
+};
+
class HInvokeStaticOrDirect FINAL : public HInvoke {
public:
// Requirements of this method call regarding the class
@@ -6510,9 +6543,9 @@
class HLoadMethodHandle FINAL : public HInstruction {
public:
HLoadMethodHandle(HCurrentMethod* current_method,
- uint16_t method_handle_idx,
- const DexFile& dex_file,
- uint32_t dex_pc)
+ uint16_t method_handle_idx,
+ const DexFile& dex_file,
+ uint32_t dex_pc)
: HInstruction(kLoadMethodHandle,
DataType::Type::kReference,
SideEffectsForArchRuntimeCalls(),
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index d80e2fc..094b75d 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -46,6 +46,13 @@
uint8_t inlining_depth) {
DCHECK(!in_stack_map_) << "Mismatched Begin/End calls";
in_stack_map_ = true;
+ // num_dex_registers_ is the constant per-method number of registers.
+ // However we initially don't know what the value is, so lazily initialize it.
+ if (num_dex_registers_ == 0) {
+ num_dex_registers_ = num_dex_registers;
+ } else if (num_dex_registers > 0) {
+ DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count";
+ }
current_stack_map_ = StackMapEntry {
.packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_),
@@ -85,7 +92,6 @@
}
CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0));
CHECK_EQ(code_info.GetInlineDepthOf(stack_map), inlining_depth);
- CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0));
});
}
}
@@ -102,16 +108,14 @@
inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size());
}
+ // Generate delta-compressed dex register map.
+ CreateDexRegisterMap();
+
stack_maps_.Add(current_stack_map_);
}
void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
current_dex_registers_.push_back(DexRegisterLocation(kind, value));
-
- // We have collected all the dex registers for StackMap/InlineInfo - create the map.
- if (current_dex_registers_.size() == expected_num_dex_registers_) {
- CreateDexRegisterMap();
- }
}
void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
@@ -129,7 +133,7 @@
CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_),
StackMap::UnpackNativePc(packed_native_pc, instruction_set_));
CHECK_EQ(invoke_info.GetInvokeType(), invoke_type);
- CHECK_EQ(method_infos_[invoke_info.GetMethodIndexIdx()], dex_method_index);
+ CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()], dex_method_index);
});
}
}
@@ -142,14 +146,15 @@
in_inline_info_ = true;
DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size());
+ expected_num_dex_registers_ += num_dex_registers;
+
InlineInfoEntry entry = {
.is_last = InlineInfo::kMore,
.dex_pc = dex_pc,
.method_info_index = kNoValue,
.art_method_hi = kNoValue,
.art_method_lo = kNoValue,
- .dex_register_mask_index = kNoValue,
- .dex_register_map_index = kNoValue,
+ .num_dex_registers = static_cast<uint32_t>(expected_num_dex_registers_),
};
if (EncodeArtMethodInInlineInfo(method)) {
entry.art_method_hi = High32Bits(reinterpret_cast<uintptr_t>(method));
@@ -164,9 +169,6 @@
}
current_inline_infos_.push_back(entry);
- current_dex_registers_.clear();
- expected_num_dex_registers_ = num_dex_registers;
-
if (kVerifyStackMaps) {
size_t stack_map_index = stack_maps_.size();
size_t depth = current_inline_infos_.size() - 1;
@@ -179,10 +181,9 @@
if (encode_art_method) {
CHECK_EQ(inline_info.GetArtMethod(), method);
} else {
- CHECK_EQ(method_infos_[inline_info.GetMethodIndexIdx()],
+ CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()],
method->GetDexMethodIndexUnchecked());
}
- CHECK_EQ(inline_info.HasDexRegisterMap(), (num_dex_registers != 0));
});
}
}
@@ -193,56 +194,68 @@
DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size());
}
-// Create dex register map (bitmap + indices + catalogue entries)
-// based on the currently accumulated list of DexRegisterLocations.
+// Create delta-compressed dex register map based on the current list of DexRegisterLocations.
+// All dex registers for a stack map are concatenated - inlined registers are just appended.
void StackMapStream::CreateDexRegisterMap() {
- // Create mask and map based on current registers.
+ // These are fields rather than local variables so that we can reuse the reserved memory.
temp_dex_register_mask_.ClearAllBits();
temp_dex_register_map_.clear();
+
+ // Ensure that the arrays that hold previous state are big enough to be safely indexed below.
+ if (previous_dex_registers_.size() < current_dex_registers_.size()) {
+ previous_dex_registers_.resize(current_dex_registers_.size(), DexRegisterLocation::None());
+ dex_register_timestamp_.resize(current_dex_registers_.size(), 0u);
+ }
+
+ // Set bit in the mask for each register that has been changed since the previous stack map.
+ // Modified registers are stored in the catalogue and the catalogue index added to the list.
for (size_t i = 0; i < current_dex_registers_.size(); i++) {
DexRegisterLocation reg = current_dex_registers_[i];
- if (reg.IsLive()) {
- DexRegisterEntry entry = DexRegisterEntry {
+ // Distance is difference between this index and the index of last modification.
+ uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i];
+ if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) {
+ DexRegisterEntry entry = DexRegisterEntry{
.kind = static_cast<uint32_t>(reg.GetKind()),
.packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()),
};
+ uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue;
temp_dex_register_mask_.SetBit(i);
- temp_dex_register_map_.push_back(dex_register_catalog_.Dedup(&entry));
+ temp_dex_register_map_.push_back(index);
+ previous_dex_registers_[i] = reg;
+ dex_register_timestamp_[i] = stack_maps_.size();
}
}
- // Set the mask and map for the current StackMap/InlineInfo.
- uint32_t mask_index = StackMap::kNoValue; // Represents mask with all zero bits.
+ // Set the mask and map for the current StackMap (which includes inlined registers).
if (temp_dex_register_mask_.GetNumberOfBits() != 0) {
- mask_index = dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(),
- temp_dex_register_mask_.GetNumberOfBits());
+ current_stack_map_.dex_register_mask_index =
+ dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(),
+ temp_dex_register_mask_.GetNumberOfBits());
}
- uint32_t map_index = dex_register_maps_.Dedup(temp_dex_register_map_.data(),
- temp_dex_register_map_.size());
- if (!current_inline_infos_.empty()) {
- current_inline_infos_.back().dex_register_mask_index = mask_index;
- current_inline_infos_.back().dex_register_map_index = map_index;
- } else {
- current_stack_map_.dex_register_mask_index = mask_index;
- current_stack_map_.dex_register_map_index = map_index;
+ if (!current_dex_registers_.empty()) {
+ current_stack_map_.dex_register_map_index =
+ dex_register_maps_.Dedup(temp_dex_register_map_.data(),
+ temp_dex_register_map_.size());
}
if (kVerifyStackMaps) {
size_t stack_map_index = stack_maps_.size();
- int32_t depth = current_inline_infos_.size() - 1;
+ uint32_t depth = current_inline_infos_.size();
// We need to make copy of the current registers for later (when the check is run).
- auto expected_dex_registers = std::make_shared<std::vector<DexRegisterLocation>>(
+ auto expected_dex_registers = std::make_shared<dchecked_vector<DexRegisterLocation>>(
current_dex_registers_.begin(), current_dex_registers_.end());
dchecks_.emplace_back([=](const CodeInfo& code_info) {
StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
- size_t num_dex_registers = expected_dex_registers->size();
- DexRegisterMap map = (depth == -1)
- ? code_info.GetDexRegisterMapOf(stack_map, num_dex_registers)
- : code_info.GetDexRegisterMapAtDepth(depth, stack_map, num_dex_registers);
- CHECK_EQ(map.size(), num_dex_registers);
- for (size_t r = 0; r < num_dex_registers; r++) {
- CHECK_EQ(expected_dex_registers->at(r), map.Get(r));
+ uint32_t expected_reg = 0;
+ for (DexRegisterLocation reg : code_info.GetDexRegisterMapOf(stack_map)) {
+ CHECK_EQ((*expected_dex_registers)[expected_reg++], reg);
}
+ for (uint32_t d = 0; d < depth; d++) {
+ for (DexRegisterLocation reg : code_info.GetDexRegisterMapAtDepth(d, stack_map)) {
+ CHECK_EQ((*expected_dex_registers)[expected_reg++], reg);
+ }
+ }
+ CHECK_EQ(expected_reg, expected_dex_registers->size());
});
}
}
@@ -290,6 +303,7 @@
dex_register_masks_.Encode(&out_, &bit_offset);
dex_register_maps_.Encode(&out_, &bit_offset);
dex_register_catalog_.Encode(&out_, &bit_offset);
+ EncodeVarintBits(&out_, &bit_offset, num_dex_registers_);
return UnsignedLeb128Size(out_.size()) + out_.size();
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index d634c70..02fb6cb 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -55,6 +55,8 @@
in_inline_info_(false),
current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
+ previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
+ dex_register_timestamp_(allocator->Adapter(kArenaAllocStackMapStream)),
temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream),
temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) {
}
@@ -113,8 +115,7 @@
uint32_t method_info_index;
uint32_t art_method_hi;
uint32_t art_method_lo;
- uint32_t dex_register_mask_index;
- uint32_t dex_register_map_index;
+ uint32_t num_dex_registers;
};
// The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h!
@@ -147,6 +148,7 @@
BitmapTableBuilder dex_register_masks_;
BitTableBuilder<uint32_t> dex_register_maps_;
BitTableBuilder<DexRegisterEntry> dex_register_catalog_;
+ uint32_t num_dex_registers_ = 0; // TODO: Make this const and get the value in constructor.
ScopedArenaVector<uint8_t> out_;
BitTableBuilder<uint32_t> method_infos_;
@@ -159,6 +161,8 @@
StackMapEntry current_stack_map_;
ScopedArenaVector<InlineInfoEntry> current_inline_infos_;
ScopedArenaVector<DexRegisterLocation> current_dex_registers_;
+ ScopedArenaVector<DexRegisterLocation> previous_dex_registers_;
+ ScopedArenaVector<uint32_t> dex_register_timestamp_; // Stack map index of last change.
size_t expected_num_dex_registers_;
// Temporary variables used in CreateDexRegisterMap.
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 77aa3ef..9adc4c5 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -81,8 +81,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -170,8 +170,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -210,8 +210,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -243,8 +243,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -276,8 +276,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -342,7 +342,8 @@
ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers));
+ DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map));
+ ASSERT_EQ(number_of_dex_registers, map.size());
ASSERT_TRUE(map.IsDexRegisterLive(0));
ASSERT_TRUE(map.IsDexRegisterLive(1));
ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters());
@@ -358,13 +359,6 @@
ASSERT_EQ(Kind::kConstant, location1.GetKind());
ASSERT_EQ(0, location0.GetValue());
ASSERT_EQ(-2, location1.GetValue());
-
- // Test that the inline info dex register map deduplicated to the same offset as the stack map
- // one.
- ASSERT_TRUE(stack_map.HasInlineInfo());
- InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, 0);
- EXPECT_EQ(inline_info.GetDexRegisterMapIndex(),
- stack_map.GetDexRegisterMapIndex());
}
}
@@ -400,8 +394,8 @@
ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map));
ASSERT_TRUE(stack_map.HasDexRegisterMap());
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ ASSERT_EQ(number_of_dex_registers, dex_register_map.size());
ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters());
@@ -450,29 +444,28 @@
// Verify first stack map.
StackMap sm0 = ci.GetStackMapAt(0);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0);
+ ASSERT_EQ(number_of_dex_registers, dex_registers0.size());
ASSERT_EQ(0, dex_registers0.GetMachineRegister(0));
ASSERT_EQ(-2, dex_registers0.GetConstant(1));
// Verify second stack map.
StackMap sm1 = ci.GetStackMapAt(1);
- DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1);
+ ASSERT_EQ(number_of_dex_registers, dex_registers1.size());
ASSERT_EQ(0, dex_registers1.GetMachineRegister(0));
ASSERT_EQ(-2, dex_registers1.GetConstant(1));
// Verify third stack map.
StackMap sm2 = ci.GetStackMapAt(2);
- DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2);
+ ASSERT_EQ(number_of_dex_registers, dex_registers2.size());
ASSERT_EQ(2, dex_registers2.GetMachineRegister(0));
ASSERT_EQ(-2, dex_registers2.GetConstant(1));
- // Verify dex register map offsets.
- ASSERT_EQ(sm0.GetDexRegisterMapIndex(),
- sm1.GetDexRegisterMapIndex());
- ASSERT_NE(sm0.GetDexRegisterMapIndex(),
- sm2.GetDexRegisterMapIndex());
- ASSERT_NE(sm1.GetDexRegisterMapIndex(),
- sm2.GetDexRegisterMapIndex());
+ // Verify dex register mask offsets.
+ ASSERT_FALSE(sm1.HasDexRegisterMaskIndex()); // No delta.
+ ASSERT_TRUE(sm2.HasDexRegisterMaskIndex()); // Has delta.
}
TEST(StackMapTest, TestNoDexRegisterMap) {
@@ -602,7 +595,8 @@
// Verify first stack map.
StackMap sm0 = ci.GetStackMapAt(0);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0);
+ ASSERT_EQ(2u, dex_registers0.size());
ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0));
ASSERT_EQ(4, dex_registers0.GetConstant(1));
@@ -614,10 +608,12 @@
ASSERT_EQ(3u, if0_1.GetDexPc());
ASSERT_TRUE(if0_1.EncodesArtMethod());
- DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm0, 1);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm0);
+ ASSERT_EQ(1u, dex_registers1.size());
ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0));
- DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm0, 3);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm0);
+ ASSERT_EQ(3u, dex_registers2.size());
ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0));
ASSERT_EQ(20, dex_registers2.GetConstant(1));
ASSERT_EQ(15, dex_registers2.GetMachineRegister(2));
@@ -627,7 +623,8 @@
// Verify second stack map.
StackMap sm1 = ci.GetStackMapAt(1);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1);
+ ASSERT_EQ(2u, dex_registers0.size());
ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0));
ASSERT_EQ(0, dex_registers0.GetConstant(1));
@@ -642,22 +639,23 @@
ASSERT_EQ(5u, if1_2.GetDexPc());
ASSERT_TRUE(if1_2.EncodesArtMethod());
- DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm1, 1);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm1);
+ ASSERT_EQ(1u, dex_registers1.size());
ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0));
- DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm1, 3);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm1);
+ ASSERT_EQ(3u, dex_registers2.size());
ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0));
ASSERT_EQ(10, dex_registers2.GetConstant(1));
ASSERT_EQ(5, dex_registers2.GetMachineRegister(2));
-
- ASSERT_FALSE(if1_2.HasDexRegisterMap());
}
{
// Verify third stack map.
StackMap sm2 = ci.GetStackMapAt(2);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2);
+ ASSERT_EQ(2u, dex_registers0.size());
ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0));
ASSERT_EQ(4, dex_registers0.GetConstant(1));
ASSERT_FALSE(sm2.HasInlineInfo());
@@ -667,7 +665,8 @@
// Verify fourth stack map.
StackMap sm3 = ci.GetStackMapAt(3);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3);
+ ASSERT_EQ(2u, dex_registers0.size());
ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0));
ASSERT_EQ(0, dex_registers0.GetConstant(1));
@@ -682,12 +681,12 @@
ASSERT_EQ(10u, if2_2.GetDexPc());
ASSERT_TRUE(if2_2.EncodesArtMethod());
- ASSERT_FALSE(if2_0.HasDexRegisterMap());
-
- DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3, 1);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3);
+ ASSERT_EQ(1u, dex_registers1.size());
ASSERT_EQ(2, dex_registers1.GetMachineRegister(0));
- DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, sm3, 2);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, sm3);
+ ASSERT_EQ(2u, dex_registers2.size());
ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0));
ASSERT_EQ(3, dex_registers2.GetMachineRegister(1));
}
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 19c405e..e76e98a 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
" 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n",
" 220: 4770 bx lr\n",
" 222: 4660 mov r0, ip\n",
- " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n",
+ " 224: f8d9 c2d0 ldr.w ip, [r9, #720] ; 0x2d0\n",
" 228: 47e0 blx ip\n",
nullptr
};
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 5f5930f..da69b83 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1261,15 +1261,8 @@
return nullptr;
}
-
-ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- Thread* self = Thread::Current();
- StackHandleScope<3> hs(self);
- Handle<Class> object_array_class(hs.NewHandle(
- class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
-
+ObjPtr<mirror::ObjectArray<mirror::Object>> ImageWriter::CollectDexCaches(Thread* self,
+ size_t oat_index) const {
std::unordered_set<const DexFile*> image_dex_files;
for (auto& pair : dex_file_oat_index_map_) {
const DexFile* image_dex_file = pair.first;
@@ -1284,6 +1277,7 @@
// ObjectArray, we lock the dex lock twice, first to get the number
// of dex caches first and then lock it again to copy the dex
// caches. We check that the number of dex caches does not change.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
size_t dex_cache_count = 0;
{
ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1300,8 +1294,8 @@
}
}
}
- Handle<ObjectArray<Object>> dex_caches(
- hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
+ ObjPtr<ObjectArray<Object>> dex_caches = ObjectArray<Object>::Alloc(
+ self, GetClassRoot<ObjectArray<Object>>(class_linker), dex_cache_count);
CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
{
ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1335,11 +1329,62 @@
}
}
}
+ return dex_caches;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(Thread* self,
+ ClassLinker* class_linker)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> integer_cache_class = class_linker->LookupClass(
+ self, "Ljava/lang/Integer$IntegerCache;", /* class_linker */ nullptr);
+ if (integer_cache_class == nullptr || !integer_cache_class->IsInitialized()) {
+ return nullptr;
+ }
+ ArtField* cache_field =
+ integer_cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
+ CHECK(cache_field != nullptr);
+ ObjPtr<ObjectArray<mirror::Object>> integer_cache =
+ ObjPtr<ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(integer_cache_class));
+ CHECK(integer_cache != nullptr);
+ return integer_cache;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> CollectBootImageLiveObjects(
+ Thread* self,
+ ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // The objects used for the Integer.valueOf() intrinsic must remain live even if references
+ // to them are removed using reflection. Image roots are not accessible through reflection,
+ // so the array we construct here shall keep them alive.
+ StackHandleScope<1> hs(self);
+ Handle<ObjectArray<mirror::Object>> integer_cache =
+ hs.NewHandle(LookupIntegerCache(self, class_linker));
+ size_t live_objects_size =
+ (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u;
+ ObjPtr<mirror::ObjectArray<mirror::Object>> live_objects = ObjectArray<Object>::Alloc(
+ self, GetClassRoot<ObjectArray<Object>>(class_linker), live_objects_size);
+ int32_t index = 0;
+ if (integer_cache != nullptr) {
+ live_objects->Set(index++, integer_cache.Get());
+ for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) {
+ live_objects->Set(index++, integer_cache->Get(i));
+ }
+ }
+ CHECK_EQ(index, live_objects->GetLength());
+ return live_objects;
+}
+
+ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ Thread* self = Thread::Current();
+ StackHandleScope<2> hs(self);
+
+ Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
// build an Object[] of the roots needed to restore the runtime
int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
- auto image_roots(hs.NewHandle(
- ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
+ Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
+ self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
image_roots->Set<false>(ImageHeader::kOomeWhenThrowingException,
@@ -1350,10 +1395,16 @@
runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
runtime->GetPreAllocatedNoClassDefFoundError());
- // image_roots[ImageHeader::kClassLoader] will be set later for app image.
- static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
- "Class loader should be the last image root.");
- for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
+ if (!compile_app_image_) {
+ ObjPtr<ObjectArray<Object>> boot_image_live_objects =
+ CollectBootImageLiveObjects(self, class_linker);
+ image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects);
+ }
+ for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
+ if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+ // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
+ continue;
+ }
CHECK(image_roots->Get(i) != nullptr);
}
return image_roots.Get();
@@ -1781,7 +1832,7 @@
CHECK_EQ(class_loaders_.size(), 1u);
CHECK_EQ(image_roots.size(), 1u);
CHECK(*class_loaders_.begin() != nullptr);
- image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
+ image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin());
}
// Verify that all objects have assigned image bin slots.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 960d698..c282a2a 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -450,6 +450,8 @@
REQUIRES_SHARED(Locks::mutator_lock_);
void CreateHeader(size_t oat_index)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<mirror::ObjectArray<mirror::Object>> CollectDexCaches(Thread* self, size_t oat_index) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
REQUIRES_SHARED(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index d0a6eb9..2bc286a 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -497,7 +497,7 @@
EXPECT_EQ(76U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(165 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 85778b6..8261035 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -612,7 +612,7 @@
pClassDef.class_data_off_, pClassDef.class_data_off_);
// Fields and methods.
- ClassAccessor accessor(*pDexFile, pClassDef);
+ ClassAccessor accessor(*pDexFile, idx);
fprintf(gOutFile, "static_fields_size : %d\n", accessor.NumStaticFields());
fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields());
fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods());
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index a3d3ee4..b4764fd 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -151,6 +151,20 @@
StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits);
}
+ // Count the number of set bits within the given bit range.
+ ALWAYS_INLINE size_t PopCount(size_t bit_offset, size_t bit_length) const {
+ DCHECK_LE(bit_offset, bit_size_);
+ DCHECK_LE(bit_length, bit_size_ - bit_offset);
+ size_t count = 0;
+ size_t bit = 0;
+ constexpr size_t kNumBits = BitSizeOf<uint32_t>();
+ for (; bit + kNumBits <= bit_length; bit += kNumBits) {
+ count += POPCOUNT(LoadBits(bit_offset + bit, kNumBits));
+ }
+ count += POPCOUNT(LoadBits(bit_offset + bit, bit_length - bit));
+ return count;
+ }
+
ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const {
return data_ == other.data_ &&
bit_start_ == other.bit_start_ &&
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index bf3d3b0..0ae60b9 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -68,8 +68,10 @@
public:
class Accessor {
public:
+ static constexpr uint32_t kCount = kNumColumns;
static constexpr uint32_t kNoValue = std::numeric_limits<uint32_t>::max();
+ Accessor() {}
Accessor(const BitTable* table, uint32_t row) : table_(table), row_(row) {}
ALWAYS_INLINE uint32_t Row() const { return row_; }
@@ -86,14 +88,27 @@
return this->table_ == other.table_ && this->row_ == other.row_;
}
- Accessor& operator++() {
- row_++;
- return *this;
- }
+// Helper macro to create constructors and per-table utilities in derived class.
+#define BIT_TABLE_HEADER() \
+ using BitTable<kCount>::Accessor::Accessor; /* inherit the constructors */ \
+ template<int COLUMN, int UNUSED /*needed to compile*/> struct ColumnName; \
+
+// Helper macro to create named column accessors in derived class.
+#define BIT_TABLE_COLUMN(COLUMN, NAME) \
+ static constexpr uint32_t k##NAME = COLUMN; \
+ ALWAYS_INLINE uint32_t Get##NAME() const { \
+ return table_->Get(row_, COLUMN); \
+ } \
+ ALWAYS_INLINE bool Has##NAME() const { \
+ return table_->Get(row_, COLUMN) != kNoValue; \
+ } \
+ template<int UNUSED> struct ColumnName<COLUMN, UNUSED> { \
+ static constexpr const char* Value = #NAME; \
+ }; \
protected:
- const BitTable* table_;
- uint32_t row_;
+ const BitTable* table_ = nullptr;
+ uint32_t row_ = -1;
};
static constexpr uint32_t kValueBias = -1;
@@ -152,11 +167,17 @@
uint16_t column_offset_[kNumColumns + 1] = {};
};
-template<uint32_t kNumColumns>
-constexpr uint32_t BitTable<kNumColumns>::Accessor::kNoValue;
+// Template meta-programming helper.
+template<typename Accessor, size_t... Columns>
+static const char** GetBitTableColumnNamesImpl(std::index_sequence<Columns...>) {
+ static const char* names[] = { Accessor::template ColumnName<Columns, 0>::Value... };
+ return names;
+}
-template<uint32_t kNumColumns>
-constexpr uint32_t BitTable<kNumColumns>::kValueBias;
+template<typename Accessor>
+static const char** GetBitTableColumnNames() {
+ return GetBitTableColumnNamesImpl<Accessor>(std::make_index_sequence<Accessor::kCount>());
+}
// Helper class for encoding BitTable. It can optionally de-duplicate the inputs.
// Type 'T' must be POD type consisting of uint32_t fields (one for each column).
@@ -209,18 +230,6 @@
return index;
}
- // Check if the table already contains given values starting at the given index.
- bool RangeEquals(uint32_t index, T* values, size_t count = 1) {
- DCHECK_LE(index, size());
- DCHECK_LE(count, size() - index);
- for (uint32_t i = 0; i < count; i++) {
- if (memcmp(&values[i], &rows_[index + i], sizeof(T)) != 0) {
- return false;
- }
- }
- return true;
- }
-
ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const {
DCHECK_LT(row, size());
DCHECK_LT(column, kNumColumns);
@@ -290,9 +299,6 @@
ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_; // Hash -> row index.
};
-template<typename T>
-constexpr size_t BitTableBuilder<T>::kNumColumns;
-
// Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits).
class BitmapTableBuilder {
public:
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index 3bb9e93..a335f08 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -26,12 +26,15 @@
namespace art {
inline ClassAccessor::ClassAccessor(const ClassIteratorData& data)
- : ClassAccessor(data.dex_file_, data.dex_file_.GetClassDef(data.class_def_idx_)) {}
+ : ClassAccessor(data.dex_file_, data.class_def_idx_) {}
inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def)
+ : ClassAccessor(dex_file, dex_file.GetIndexForClassDef(class_def)) {}
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, uint32_t class_def_index)
: dex_file_(dex_file),
- descriptor_index_(class_def.class_idx_),
- ptr_pos_(dex_file.GetClassData(class_def)),
+ class_def_index_(class_def_index),
+ ptr_pos_(dex_file.GetClassData(dex_file.GetClassDef(class_def_index))),
num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
@@ -108,7 +111,7 @@
}
inline const char* ClassAccessor::GetDescriptor() const {
- return dex_file_.StringByTypeIdx(descriptor_index_);
+ return dex_file_.StringByTypeIdx(GetClassIdx());
}
inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const {
@@ -175,6 +178,10 @@
DexFile::UnHideAccessFlags(const_cast<uint8_t*>(ptr_pos_), GetAccessFlags(), /*is_method*/ true);
}
+inline dex::TypeIndex ClassAccessor::GetClassIdx() const {
+ return dex_file_.GetClassDef(class_def_index_).class_idx_;
+}
+
} // namespace art
#endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
index 4f0fd32..34c7e07 100644
--- a/libdexfile/dex/class_accessor.h
+++ b/libdexfile/dex/class_accessor.h
@@ -248,6 +248,8 @@
ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def);
+ ClassAccessor(const DexFile& dex_file, uint32_t class_def_index);
+
// Return the code item for a method.
const DexFile::CodeItem* GetCodeItem(const Method& method) const;
@@ -315,9 +317,7 @@
const char* GetDescriptor() const;
- dex::TypeIndex GetClassIdx() const {
- return descriptor_index_;
- }
+ dex::TypeIndex GetClassIdx() const;
const DexFile& GetDexFile() const {
return dex_file_;
@@ -327,6 +327,10 @@
return ptr_pos_ != nullptr;
}
+ uint32_t GetClassDefIndex() const {
+ return class_def_index_;
+ }
+
protected:
// Template visitor to reduce copy paste for visiting elements.
// No thread safety analysis since the visitor may require capabilities.
@@ -341,7 +345,7 @@
IterationRange<DataIterator<Method>> GetMethodsInternal(size_t count) const;
const DexFile& dex_file_;
- const dex::TypeIndex descriptor_index_ = {};
+ const uint32_t class_def_index_;
const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item.
const uint32_t num_static_fields_ = 0u;
const uint32_t num_instance_fields_ = 0u;
diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc
index d0533c1..1f30ae5 100644
--- a/libdexfile/dex/class_accessor_test.cc
+++ b/libdexfile/dex/class_accessor_test.cc
@@ -30,8 +30,9 @@
uint32_t class_def_idx = 0u;
ASSERT_GT(dex_file->NumClassDefs(), 0u);
for (ClassAccessor accessor : dex_file->GetClasses()) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(accessor.GetClassDefIndex());
EXPECT_EQ(accessor.GetDescriptor(), dex_file->StringByTypeIdx(class_def.class_idx_));
+ EXPECT_EQ(class_def_idx, accessor.GetClassDefIndex());
++class_def_idx;
// Check iterators against visitors.
auto methods = accessor.GetMethods();
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index f570158..f1f8960 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -605,6 +605,15 @@
return PrettyDescriptor(GetTypeDescriptor(type_id));
}
+dex::ProtoIndex DexFile::GetProtoIndexForCallSite(uint32_t call_site_idx) const {
+ const DexFile::CallSiteIdItem& csi = GetCallSiteId(call_site_idx);
+ CallSiteArrayValueIterator it(*this, csi);
+ it.Next();
+ it.Next();
+ DCHECK_EQ(EncodedArrayValueIterator::ValueType::kMethodType, it.GetValueType());
+ return dex::ProtoIndex(it.GetJavaValue().i);
+}
+
// Checks that visibility is as expected. Includes special behavior for M and
// before to allow runtime and build visibility when expecting runtime.
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index ed21980..a8cd187 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -737,6 +737,8 @@
return DataBegin() + call_site_id.data_off_;
}
+ dex::ProtoIndex GetProtoIndexForCallSite(uint32_t call_site_idx) const;
+
static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset);
// Get the base of the encoded data for the given DexCode.
diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h
index 9b3af67..1740c07 100644
--- a/libdexfile/dex/invoke_type.h
+++ b/libdexfile/dex/invoke_type.h
@@ -28,7 +28,8 @@
kSuper, // <<super>>
kInterface, // <<interface>>
kPolymorphic, // <<polymorphic>>
- kMaxInvokeType = kPolymorphic
+ kCustom, // <<custom>>
+ kMaxInvokeType = kCustom
};
std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7b72e18..453e9da 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -274,7 +274,7 @@
void WalkOatClass(const OatFile::OatClass& oat_class,
const DexFile& dex_file,
uint32_t class_def_index) {
- ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_index));
+ ClassAccessor accessor(dex_file, class_def_index);
// Note: even if this is an interface or a native class, we still have to walk it, as there
// might be a static initializer.
uint32_t class_method_idx = 0;
@@ -757,7 +757,7 @@
kByteKindInlineInfoMethodIndexIdx,
kByteKindInlineInfoDexPc,
kByteKindInlineInfoArtMethod,
- kByteKindInlineInfoDexRegisterMap,
+ kByteKindInlineInfoNumDexRegisters,
kByteKindInlineInfoIsLast,
kByteKindCount,
// Special ranges for std::accumulate convenience.
@@ -859,8 +859,8 @@
inline_info_bits,
"inline info");
Dump(os,
- "InlineInfoDexRegisterMap ",
- bits[kByteKindInlineInfoDexRegisterMap],
+ "InlineInfoNumDexRegisters ",
+ bits[kByteKindInlineInfoNumDexRegisters],
inline_info_bits,
"inline info");
Dump(os,
@@ -905,15 +905,13 @@
continue;
}
offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader()));
- uint32_t class_def_index = 0u;
for (ClassAccessor accessor : dex_file->GetClasses()) {
- const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(accessor.GetClassDefIndex());
for (uint32_t class_method_index = 0;
class_method_index < accessor.NumMethods();
++class_method_index) {
AddOffsets(oat_class.GetOatMethod(class_method_index));
}
- ++class_def_index;
}
}
@@ -1429,7 +1427,7 @@
DCHECK(code_item_accessor.HasCodeItem());
ScopedIndentation indent1(vios);
MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo();
- DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info);
+ DumpCodeInfo(vios, code_info, oat_method, method_info);
}
} else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) {
// We don't encode the size in the table, so just emit that we have quickened
@@ -1445,11 +1443,9 @@
void DumpCodeInfo(VariableIndentationOutputStream* vios,
const CodeInfo& code_info,
const OatFile::OatMethod& oat_method,
- const CodeItemDataAccessor& code_item_accessor,
const MethodInfo& method_info) {
code_info.Dump(vios,
oat_method.GetCodeOffset(),
- code_item_accessor.RegistersSize(),
options_.dump_code_info_stack_maps_,
instruction_set_,
method_info);
@@ -1752,7 +1748,7 @@
if (num_inline_infos > 0u) {
stats_.AddBits(
Stats::kByteKindInlineInfoMethodIndexIdx,
- inline_infos.NumColumnBits(InlineInfo::kMethodIndexIdx) * num_inline_infos);
+ inline_infos.NumColumnBits(InlineInfo::kMethodInfoIndex) * num_inline_infos);
stats_.AddBits(
Stats::kByteKindInlineInfoDexPc,
inline_infos.NumColumnBits(InlineInfo::kDexPc) * num_inline_infos);
@@ -1761,8 +1757,8 @@
inline_infos.NumColumnBits(InlineInfo::kArtMethodHi) * num_inline_infos +
inline_infos.NumColumnBits(InlineInfo::kArtMethodLo) * num_inline_infos);
stats_.AddBits(
- Stats::kByteKindInlineInfoDexRegisterMap,
- inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapIndex) * num_inline_infos);
+ Stats::kByteKindInlineInfoNumDexRegisters,
+ inline_infos.NumColumnBits(InlineInfo::kNumberOfDexRegisters) * num_inline_infos);
stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos);
}
}
@@ -1779,7 +1775,6 @@
helper.GetCodeInfo(),
method_info,
oat_method.GetCodeOffset(),
- code_item_accessor.RegistersSize(),
instruction_set_);
do {
helper.Next();
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 108c753..0beca1f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -363,6 +363,10 @@
uint32_t offset_delta = 0;
if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) {
offset += offset_delta;
+ if (static_cast<int64_t>(offset) + static_cast<int64_t>(sizeof(uint32_t)) > image_size) {
+ *error_msg = StringPrintf("Relocation out of bounds in %s", relocated_filename.c_str());
+ return false;
+ }
uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset);
*image_value -= expected_diff;
} else {
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 69728ae..08bf31c 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -445,103 +445,173 @@
#endif
}
-TEST_F(PatchoatTest, RelFileVerification) {
- // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel
- // file created by patchoat.
+// These tests check that a boot image relocated using patchoat can be unrelocated
+// using the .rel file created by patchoat.
+//
+// The tests don't work when heap poisoning is enabled because some of the
+// references are negated. b/72117833 is tracking the effort to have patchoat
+// and its tests support heap poisoning.
+class PatchoatVerificationTest : public PatchoatTest {
+ protected:
+ void CreateRelocatedBootImage() {
+ // Compile boot image into a random directory using dex2oat
+ ScratchFile dex2oat_orig_scratch;
+ dex2oat_orig_scratch.Unlink();
+ dex2oat_orig_dir_ = dex2oat_orig_scratch.GetFilename();
+ ASSERT_EQ(0, mkdir(dex2oat_orig_dir_.c_str(), 0700));
+ const uint32_t orig_base_addr = 0x60000000;
+ std::vector<std::string> dex2oat_extra_args;
+ std::string error_msg;
+ if (!CompileBootImageToDir(dex2oat_orig_dir_, dex2oat_extra_args, orig_base_addr, &error_msg)) {
+ FAIL() << "CompileBootImage1 failed: " << error_msg;
+ }
- // This test doesn't work when heap poisoning is enabled because some of the
- // references are negated. b/72117833 is tracking the effort to have patchoat
- // and its tests support heap poisoning.
+ // Generate image relocation file for the original boot image
+ std::string dex2oat_orig_with_arch_dir =
+ dex2oat_orig_dir_ + "/" + GetInstructionSetString(kRuntimeISA);
+ // The arch-including symlink is needed by patchoat
+ ASSERT_EQ(0, symlink(dex2oat_orig_dir_.c_str(), dex2oat_orig_with_arch_dir.c_str()));
+ base_addr_delta_ = 0x100000;
+ if (!GenerateBootImageRelFile(
+ dex2oat_orig_dir_ + "/boot.art",
+ dex2oat_orig_dir_,
+ base_addr_delta_,
+ &error_msg)) {
+ FAIL() << "RelocateBootImage failed: " << error_msg;
+ }
+
+ // Relocate the original boot image using patchoat
+ ScratchFile relocated_scratch;
+ relocated_scratch.Unlink();
+ relocated_dir_ = relocated_scratch.GetFilename();
+ ASSERT_EQ(0, mkdir(relocated_dir_.c_str(), 0700));
+ // Use a different relocation delta from the one used when generating .rel files above. This is
+ // to make sure .rel files are not specific to a particular relocation delta.
+ base_addr_delta_ -= 0x10000;
+ if (!RelocateBootImage(
+ dex2oat_orig_dir_ + "/boot.art",
+ relocated_dir_,
+ base_addr_delta_,
+ &error_msg)) {
+ FAIL() << "RelocateBootImage failed: " << error_msg;
+ }
+
+ // Assert that patchoat created the same set of .art and .art.rel files
+ std::vector<std::string> rel_basenames;
+ std::vector<std::string> relocated_image_basenames;
+ if (!ListDirFilesEndingWith(dex2oat_orig_dir_, ".rel", &rel_basenames, &error_msg)) {
+ FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir_ << ": " << error_msg;
+ }
+ if (!ListDirFilesEndingWith(relocated_dir_, ".art", &relocated_image_basenames, &error_msg)) {
+ FAIL() << "Failed to list *.art files in " << relocated_dir_ << ": " << error_msg;
+ }
+ std::sort(rel_basenames.begin(), rel_basenames.end());
+ std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end());
+
+ // .art and .art.rel file names output by patchoat look like
+ // tmp@art-data-<random>-<random>@boot*.art, encoding the name of the directory in their name.
+ // To compare these with each other, we retain only the part of the file name after the last @,
+ // and we also drop the extension.
+ std::vector<std::string> rel_shortened_basenames(rel_basenames.size());
+ std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size());
+ for (size_t i = 0; i < rel_basenames.size(); i++) {
+ rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1);
+ rel_shortened_basenames[i] =
+ rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find("."));
+ }
+ for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
+ relocated_image_shortened_basenames[i] =
+ relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1);
+ relocated_image_shortened_basenames[i] =
+ relocated_image_shortened_basenames[i].substr(
+ 0, relocated_image_shortened_basenames[i].find("."));
+ }
+ ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames);
+ }
+
+ virtual void TearDown() {
+ if (!dex2oat_orig_dir_.empty()) {
+ ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true);
+ rmdir(dex2oat_orig_dir_.c_str());
+ }
+ if (!relocated_dir_.empty()) {
+ ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true);
+ rmdir(relocated_dir_.c_str());
+ }
+ PatchoatTest::TearDown();
+ }
+
+ std::string dex2oat_orig_dir_;
+ std::string relocated_dir_;
+ off_t base_addr_delta_;
+};
+
+// Assert that verification works with the .rel files.
+TEST_F(PatchoatVerificationTest, Sucessful) {
TEST_DISABLED_FOR_HEAP_POISONING();
+ CreateRelocatedBootImage();
- // Compile boot image into a random directory using dex2oat
- ScratchFile dex2oat_orig_scratch;
- dex2oat_orig_scratch.Unlink();
- std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename();
- ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700));
- const uint32_t orig_base_addr = 0x60000000;
- std::vector<std::string> dex2oat_extra_args;
std::string error_msg;
- if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) {
- FAIL() << "CompileBootImage1 failed: " << error_msg;
- }
-
- // Generate image relocation file for the original boot image
- std::string dex2oat_orig_with_arch_dir =
- dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA);
- // The arch-including symlink is needed by patchoat
- ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str()));
- off_t base_addr_delta = 0x100000;
- if (!GenerateBootImageRelFile(
- dex2oat_orig_dir + "/boot.art",
- dex2oat_orig_dir,
- base_addr_delta,
- &error_msg)) {
- FAIL() << "RelocateBootImage failed: " << error_msg;
- }
-
- // Relocate the original boot image using patchoat
- ScratchFile relocated_scratch;
- relocated_scratch.Unlink();
- std::string relocated_dir = relocated_scratch.GetFilename();
- ASSERT_EQ(0, mkdir(relocated_dir.c_str(), 0700));
- // Use a different relocation delta from the one used when generating .rel files above. This is
- // to make sure .rel files are not specific to a particular relocation delta.
- base_addr_delta -= 0x10000;
- if (!RelocateBootImage(
- dex2oat_orig_dir + "/boot.art",
- relocated_dir,
- base_addr_delta,
- &error_msg)) {
- FAIL() << "RelocateBootImage failed: " << error_msg;
- }
-
- // Assert that patchoat created the same set of .art and .art.rel files
- std::vector<std::string> rel_basenames;
- std::vector<std::string> relocated_image_basenames;
- if (!ListDirFilesEndingWith(dex2oat_orig_dir, ".rel", &rel_basenames, &error_msg)) {
- FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir << ": " << error_msg;
- }
- if (!ListDirFilesEndingWith(relocated_dir, ".art", &relocated_image_basenames, &error_msg)) {
- FAIL() << "Failed to list *.art files in " << relocated_dir << ": " << error_msg;
- }
- std::sort(rel_basenames.begin(), rel_basenames.end());
- std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end());
-
- // .art and .art.rel file names output by patchoat look like
- // tmp@art-data-<random>-<random>@boot*.art, encoding the name of the directory in their name.
- // To compare these with each other, we retain only the part of the file name after the last @,
- // and we also drop the extension.
- std::vector<std::string> rel_shortened_basenames(rel_basenames.size());
- std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size());
- for (size_t i = 0; i < rel_basenames.size(); i++) {
- rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1);
- rel_shortened_basenames[i] =
- rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find("."));
- }
- for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
- relocated_image_shortened_basenames[i] =
- relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1);
- relocated_image_shortened_basenames[i] =
- relocated_image_shortened_basenames[i].substr(
- 0, relocated_image_shortened_basenames[i].find("."));
- }
- ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames);
-
- // Assert that verification works with the .rel files.
if (!VerifyBootImage(
- dex2oat_orig_dir + "/boot.art",
- relocated_dir,
- base_addr_delta,
+ dex2oat_orig_dir_ + "/boot.art",
+ relocated_dir_,
+ base_addr_delta_,
&error_msg)) {
FAIL() << "VerifyBootImage failed: " << error_msg;
}
+}
- ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true);
- ClearDirectory(relocated_dir.c_str(), /*recursive*/ true);
+// Corrupt the image file and check that the verification fails gracefully.
+TEST_F(PatchoatVerificationTest, CorruptedImage) {
+ TEST_DISABLED_FOR_HEAP_POISONING();
+ CreateRelocatedBootImage();
- rmdir(dex2oat_orig_dir.c_str());
- rmdir(relocated_dir.c_str());
+ std::string error_msg;
+ std::string relocated_image_filename;
+ if (!GetDalvikCacheFilename((dex2oat_orig_dir_ + "/boot.art").c_str(),
+ relocated_dir_.c_str(),
+ &relocated_image_filename,
+ &error_msg)) {
+ FAIL() << "Failed to find relocated image file name: " << error_msg;
+ }
+ ASSERT_EQ(truncate(relocated_image_filename.c_str(), sizeof(ImageHeader)), 0)
+ << relocated_image_filename;
+
+ if (VerifyBootImage(
+ dex2oat_orig_dir_ + "/boot.art",
+ relocated_dir_,
+ base_addr_delta_,
+ &error_msg)) {
+ FAIL() << "VerifyBootImage should have failed since the image was intentionally corrupted";
+ }
+}
+
+// Corrupt the relocation file and check that the verification fails gracefully.
+TEST_F(PatchoatVerificationTest, CorruptedRelFile) {
+ TEST_DISABLED_FOR_HEAP_POISONING();
+ CreateRelocatedBootImage();
+
+ std::string error_msg;
+ std::string art_filename = dex2oat_orig_dir_ + "/boot.art";
+ std::string rel_filename = dex2oat_orig_dir_ + "/boot.art.rel";
+ std::unique_ptr<File> art_file(OS::OpenFileForReading(art_filename.c_str()));
+ std::unique_ptr<File> rel_file(OS::OpenFileReadWrite(rel_filename.c_str()));
+ rel_file->ClearContent();
+ uint8_t buffer[64] = {};
+ ASSERT_TRUE(rel_file->WriteFully(&buffer, SHA256_DIGEST_LENGTH));
+ // Encode single relocation which is just past the end of the image file.
+ size_t leb_size = EncodeUnsignedLeb128(buffer, art_file->GetLength()) - buffer;
+ ASSERT_TRUE(rel_file->WriteFully(&buffer, leb_size));
+ ASSERT_EQ(rel_file->FlushClose(), 0);
+ ASSERT_EQ(art_file->Close(), 0);
+
+ if (VerifyBootImage(
+ dex2oat_orig_dir_ + "/boot.art",
+ relocated_dir_,
+ base_addr_delta_,
+ &error_msg)) {
+ FAIL() << "VerifyBootImage should have failed since the rel file was intentionally corrupted";
+ }
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 096e5dc..5fbce66 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -930,7 +930,9 @@
dex_resolved_classes.first->AddClass(class_ref.TypeIndex());
std::vector<ProfileMethodInfo> methods;
if (method_str == kClassAllMethods) {
- ClassAccessor accessor(*dex_file, *dex_file->FindClassDef(class_ref.TypeIndex()));
+ ClassAccessor accessor(
+ *dex_file,
+ dex_file->GetIndexForClassDef(*dex_file->FindClassDef(class_ref.TypeIndex())));
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
if (method.GetCodeItemOffset() != 0) {
// Add all of the methods that have code to the profile.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 311e838..ccff9f6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -2686,84 +2686,31 @@
.extern artInvokePolymorphic
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME r2
- mov r2, rSELF @ pass Thread::Current
- mov r3, sp @ pass SP
- mov r0, #0 @ initialize 64-bit JValue as zero.
- str r0, [sp, #-4]!
- .cfi_adjust_cfa_offset 4
- str r0, [sp, #-4]!
- .cfi_adjust_cfa_offset 4
- mov r0, sp @ pass JValue for return result as first argument.
- bl artInvokePolymorphic @ artInvokePolymorphic(JValue, receiver, Thread*, SP)
- sub r0, 'A' @ return value is descriptor of handle's return type.
- cmp r0, 'Z' - 'A' @ check if value is in bounds of handler table
- bgt .Lcleanup_and_return @ and clean-up if not.
- adr r1, .Lhandler_table
- tbb [r0, r1] @ branch to handler for return value based on return type.
-
-.Lstart_of_handlers:
-.Lstore_boolean_result:
- ldrb r0, [sp] @ Copy boolean value to return value of this function.
- b .Lcleanup_and_return
-.Lstore_char_result:
- ldrh r0, [sp] @ Copy char value to return value of this function.
- b .Lcleanup_and_return
-.Lstore_float_result:
- vldr s0, [sp] @ Copy float value from JValue result to the context restored by
- vstr s0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
- b .Lcleanup_and_return
-.Lstore_double_result:
- vldr d0, [sp] @ Copy double value from JValue result to the context restored by
- vstr d0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
- b .Lcleanup_and_return
-.Lstore_long_result:
- ldr r1, [sp, #4] @ Copy the upper bits from JValue result to the context restored by
- str r1, [sp, #80] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME.
- // Fall-through for lower bits.
-.Lstore_int_result:
- ldr r0, [sp] @ Copy int value to return value of this function.
- // Fall-through to clean up and return.
-.Lcleanup_and_return:
- add sp, #8
- .cfi_adjust_cfa_offset -8
+ mov r0, r1 @ r0 := receiver
+ mov r1, rSELF @ r1 := Thread::Current
+ mov r2, sp @ r2 := SP
+ bl artInvokePolymorphic @ artInvokePolymorphic(receiver, Thread*, SP)
+ str r1, [sp, 72] @ r0:r1 := Result. Copy r1 to context.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
+ vmov d0, r0, r1 @ Put result r0:r1 into floating point return register.
RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
-
-.macro HANDLER_TABLE_OFFSET handler_label
- .byte (\handler_label - .Lstart_of_handlers) / 2
-.endm
-
-.Lhandler_table:
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A
- HANDLER_TABLE_OFFSET(.Lstore_int_result) // B (byte)
- HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char)
- HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E
- HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H
- HANDLER_TABLE_OFFSET(.Lstore_int_result) // I (int)
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K
- HANDLER_TABLE_OFFSET(.Lstore_int_result) // L (object)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R
- HANDLER_TABLE_OFFSET(.Lstore_int_result) // S (short)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y
- HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean)
-.purgem HANDLER_TABLE_OFFSET
END art_quick_invoke_polymorphic
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME r1
+ @ r0 := call_site_idx
+ mov r1, rSELF @ r1 := Thread::Current
+ mov r2, sp @ r2 := SP
+ bl artInvokeCustom @ artInvokeCustom(call_site_idx, Thread*, SP)
+ str r1, [sp, #72] @ Save r1 to context (r0:r1 = result)
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ REFRESH_MARKING_REGISTER
+ vmov d0, r0, r1 @ Put result r0:r1 into floating point return register.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
+END art_quick_invoke_custom
+
// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
// Argument 0: r0: The context pointer for ExecuteSwitchImpl.
// Argument 1: r1: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 14d0cc7..80d5fce 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2844,84 +2844,30 @@
.extern artInvokePolymorphic
ENTRY art_quick_invoke_polymorphic
- SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
- mov x2, xSELF
- mov x3, sp
- INCREASE_FRAME 16 // Reserve space for JValue result.
- str xzr, [sp, #0] // Initialize result to zero.
- mov x0, sp // Set r0 to point to result.
- bl artInvokePolymorphic // artInvokePolymorphic(result, receiver, thread, save_area)
- uxtb w0, w0 // Result is the return type descriptor as a char.
- sub w0, w0, 'A' // Convert to zero based index.
- cmp w0, 'Z' - 'A'
- bhi .Lcleanup_and_return // Clean-up if out-of-bounds.
- adrp x1, .Lhandler_table // Compute address of handler table.
- add x1, x1, :lo12:.Lhandler_table
- ldrb w0, [x1, w0, uxtw] // Lookup handler offset in handler table.
- adr x1, .Lstart_of_handlers
- add x0, x1, w0, sxtb #2 // Convert relative offset to absolute address.
- br x0 // Branch to handler.
-
-.Lstart_of_handlers:
-.Lstore_boolean_result:
- ldrb w0, [sp]
- b .Lcleanup_and_return
-.Lstore_char_result:
- ldrh w0, [sp]
- b .Lcleanup_and_return
-.Lstore_float_result:
- ldr s0, [sp]
- str s0, [sp, #32]
- b .Lcleanup_and_return
-.Lstore_double_result:
- ldr d0, [sp]
- str d0, [sp, #32]
- b .Lcleanup_and_return
-.Lstore_long_result:
- ldr x0, [sp]
- // Fall-through
-.Lcleanup_and_return:
- DECREASE_FRAME 16
+ SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
+ mov x0, x1 // x0 := receiver
+ mov x1, xSELF // x1 := Thread::Current()
+ mov x2, sp // x2 := SP
+ bl artInvokePolymorphic // artInvokePolymorphic(receiver, thread, save_area)
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
- RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-
- .section .rodata // Place handler table in read-only section away from text.
- .align 2
-.macro HANDLER_TABLE_OFFSET handler_label
- .byte (\handler_label - .Lstart_of_handlers) / 4
-.endm
-.Lhandler_table:
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte)
- HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char)
- HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E
- HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int)
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y
- HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean)
- .text
-
+ fmov d0, x0 // Result is in x0. Copy to floating return register.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_invoke_polymorphic
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
+ // x0 := call_site_idx
+ mov x1, xSELF // x1 := Thread::Current()
+ mov x2, sp // x2 := SP
+ bl artInvokeCustom // artInvokeCustom(call_site_idx, thread, save_area)
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ REFRESH_MARKING_REGISTER
+ fmov d0, x0 // Copy result to double result register.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
+END art_quick_invoke_custom
+
// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
// Argument 0: x0: The context pointer for ExecuteSwitchImpl.
// Argument 1: x1: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5d6e410..2b69c17 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -410,6 +410,9 @@
static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
"Non-direct C stub marked direct.");
qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
+ static_assert(!IsDirectEntrypoint(kQuickInvokePolymorphic), "Non-direct C stub marked direct.");
+ qpoints->pInvokeCustom = art_quick_invoke_custom;
+ static_assert(!IsDirectEntrypoint(kQuickInvokeCustom), "Non-direct C stub marked direct.");
// Thread
qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index c367ea6..508a201 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -3246,59 +3246,48 @@
BRB_FIELD_EXIT_BREAK
END art_quick_read_barrier_mark_introspection
+ /*
+ * Polymorphic method invocation.
+ * On entry:
+ * a0 = unused
+ * a1 = receiver
+ */
.extern artInvokePolymorphic
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME
- move $a2, rSELF # Make $a2 an alias for the current Thread.
- addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context.
- sw $zero, 20($sp) # Initialize JValue result.
- sw $zero, 16($sp)
- la $t9, artInvokePolymorphic
- jalr $t9 # artInvokePolymorphic(result, receiver, Thread*, context)
- addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
-.macro MATCH_RETURN_TYPE c, handler
- li $t0, \c
- beq $v0, $t0, \handler
-.endm
- MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
- MATCH_RETURN_TYPE 'L', .Lstore_int_result
- MATCH_RETURN_TYPE 'I', .Lstore_int_result
- MATCH_RETURN_TYPE 'J', .Lstore_long_result
- MATCH_RETURN_TYPE 'B', .Lstore_int_result
- MATCH_RETURN_TYPE 'C', .Lstore_char_result
- MATCH_RETURN_TYPE 'D', .Lstore_double_result
- MATCH_RETURN_TYPE 'F', .Lstore_float_result
- MATCH_RETURN_TYPE 'S', .Lstore_int_result
- MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
-.purgem MATCH_RETURN_TYPE
- nop
- b .Lcleanup_and_return
- nop
-.Lstore_boolean_result:
- b .Lcleanup_and_return
- lbu $v0, 16($sp) # Move byte from JValue result to return value register.
-.Lstore_char_result:
- b .Lcleanup_and_return
- lhu $v0, 16($sp) # Move char from JValue result to return value register.
-.Lstore_double_result:
-.Lstore_float_result:
- CHECK_ALIGNMENT $sp, $t0
- ldc1 $f0, 16($sp) # Move double/float from JValue result to return value register.
- b .Lcleanup_and_return
- nop
-.Lstore_long_result:
- lw $v1, 20($sp) # Move upper bits from JValue result to return value register.
- // Fall-through for lower bits.
-.Lstore_int_result:
- lw $v0, 16($sp) # Move lower bits from JValue result to return value register.
- // Fall-through to clean up and return.
-.Lcleanup_and_return:
- lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
+ move $a0, $a1 # Make $a0 the receiver.
+ move $a1, rSELF # Make $a1 an alias for the current Thread.
+ la $t9, artInvokePolymorphic # Invoke artInvokePolymorphic
+ jalr $t9 # with args (receiver, Thread*, context).
+ addiu $a2, $sp, ARG_SLOT_SIZE # Make $a2 a pointer to the saved frame context.
+ lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
RESTORE_SAVE_REFS_AND_ARGS_FRAME
- bnez $t7, 1f # Success if no exception is pending.
- nop
- jalr $zero, $ra
+ bnez $t7, 1f
+ # don't care if $v0 and/or $v1 are modified, when exception branch taken
+ MTD $v0, $v1, $f0, $f1 # move float value to return value
+ jalr $zero, $ra
nop
1:
DELIVER_PENDING_EXCEPTION
END art_quick_invoke_polymorphic
+
+ /*
+ * InvokeCustom invocation.
+ * On entry:
+ * a0 = call_site_idx
+ */
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME
+ move $a1, rSELF # Make $a1 an alias for the current Thread.
+ la $t9, artInvokeCustom # Invoke artInvokeCustom
+ jalr $t9 # with args (call_site_idx, Thread*, context).
+ addiu $a2, $sp, ARG_SLOT_SIZE # Make $a2 a pointer to the saved frame context.
+ lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ bnez $t7, 1f
+ # don't care if $v0 and/or $v1 are modified, when exception branch taken
+ MTD $v0, $v1, $f0, $f1 # move float value to return value
+ jalr $zero, $ra
+ nop
+END art_quick_invoke_custom
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 1f4f174..258acdd 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -3046,61 +3046,49 @@
BRB_FIELD_EXIT_BREAK
END art_quick_read_barrier_mark_introspection
+ /*
+ * Polymorphic method invocation.
+ * On entry:
+ * a0 = unused
+ * a1 = receiver
+ */
.extern artInvokePolymorphic
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME
- move $a2, rSELF # Make $a2 an alias for the current Thread.
- move $a3, $sp # Make $a3 a pointer to the saved frame context.
- daddiu $sp, $sp, -8 # Reserve space for JValue result.
- .cfi_adjust_cfa_offset 8
- sd $zero, 0($sp) # Initialize JValue result.
- jal artInvokePolymorphic # artInvokePolymorphic(result, receiver, Thread*, context)
- move $a0, $sp # Make $a0 a pointer to the JValue result
-.macro MATCH_RETURN_TYPE c, handler
- li $t0, \c
- beq $v0, $t0, \handler
-.endm
- MATCH_RETURN_TYPE 'V', .Lcleanup_and_return
- MATCH_RETURN_TYPE 'L', .Lstore_ref_result
- MATCH_RETURN_TYPE 'I', .Lstore_long_result
- MATCH_RETURN_TYPE 'J', .Lstore_long_result
- MATCH_RETURN_TYPE 'B', .Lstore_long_result
- MATCH_RETURN_TYPE 'C', .Lstore_char_result
- MATCH_RETURN_TYPE 'D', .Lstore_double_result
- MATCH_RETURN_TYPE 'F', .Lstore_float_result
- MATCH_RETURN_TYPE 'S', .Lstore_long_result
- MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
-.purgem MATCH_RETURN_TYPE
- nop
- b .Lcleanup_and_return
- nop
-.Lstore_boolean_result:
- b .Lcleanup_and_return
- lbu $v0, 0($sp) # Move byte from JValue result to return value register.
-.Lstore_char_result:
- b .Lcleanup_and_return
- lhu $v0, 0($sp) # Move char from JValue result to return value register.
-.Lstore_double_result:
-.Lstore_float_result:
- b .Lcleanup_and_return
- l.d $f0, 0($sp) # Move double/float from JValue result to return value register.
-.Lstore_ref_result:
- b .Lcleanup_and_return
- lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register.
-.Lstore_long_result:
- ld $v0, 0($sp) # Move long from JValue result to return value register.
- // Fall-through to clean up and return.
-.Lcleanup_and_return:
- daddiu $sp, $sp, 8 # Remove space for JValue result.
- .cfi_adjust_cfa_offset -8
- ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
- bnez $t0, 1f # Success if no exception is pending.
- nop
- jalr $zero, $ra
- nop
+ move $a0, $a1 # Make $a0 the receiver
+ move $a1, rSELF # Make $a1 an alias for the current Thread.
+ jal artInvokePolymorphic # artInvokePolymorphic(receiver, Thread*, context)
+ move $a2, $sp # Make $a3 a pointer to the saved frame context.
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19
+ RESTORE_SAVE_REFS_ONLY_FRAME
+ bne $t0, $zero, 1f
+ dmtc1 $v0, $f0 # place return value to FP return value
+ jalr $zero, $ra
+ dmtc1 $v1, $f1 # place return value to FP return value
1:
DELIVER_PENDING_EXCEPTION
END art_quick_invoke_polymorphic
+ /*
+ * InvokeCustom invocation.
+ * On entry:
+ * a0 = call_site_idx
+ */
+.extern artInvokeCustom
+ENTRY art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME
+ move $a1, rSELF # Make $a1 an alias for the current Thread.
+ jal artInvokeCustom # Call artInvokeCustom(call_site_idx, Thread*, context).
+ move $a2, $sp # Make $a1 a pointer to the saved frame context.
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19
+ RESTORE_SAVE_REFS_ONLY_FRAME
+ bne $t0, $zero, 1f
+ dmtc1 $v0, $f0 # place return value to FP return value
+ jalr $zero, $ra
+ dmtc1 $v1, $f1 # place return value to FP return value
+1:
+ DELIVER_PENDING_EXCEPTION
+END art_quick_invoke_polymorphic
.set pop
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index b89d45f..e1b3df8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2434,99 +2434,49 @@
END_FUNCTION art_quick_osr_stub
DEFINE_FUNCTION art_quick_invoke_polymorphic
- SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame.
- mov %esp, %edx // Remember SP.
- subl LITERAL(16), %esp // Make space for JValue result.
- CFI_ADJUST_CFA_OFFSET(16)
- movl LITERAL(0), (%esp) // Initialize result to zero.
- movl LITERAL(0), 4(%esp)
- mov %esp, %eax // Store pointer to JValue result in eax.
- PUSH edx // pass SP
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ // On entry: EAX := unused, ECX := receiver
+ SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame.
+ mov %esp, %edx // Remember SP
+ sub LITERAL(4), %esp // Alignment padding
CFI_ADJUST_CFA_OFFSET(4)
- PUSH ecx // pass receiver (method handle)
- PUSH eax // pass JResult
- call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP)
- subl LITERAL('A'), %eax // Eliminate out of bounds options
- cmpb LITERAL('Z' - 'A'), %al
- ja .Lcleanup_and_return
- movzbl %al, %eax
- call .Lput_eip_in_ecx
-.Lbranch_start:
- movl %ecx, %edx
- add $(.Lhandler_table - .Lbranch_start), %edx // Make EDX point to handler_table.
- leal (%edx, %eax, 2), %eax // Calculate address of entry in table.
- movzwl (%eax), %eax // Lookup relative branch in table.
- addl %ecx, %eax // Add EIP relative offset.
- jmp *%eax // Branch to handler.
-
- // Handlers for different return types.
-.Lstore_boolean_result:
- movzbl 16(%esp), %eax // Copy boolean result to the accumulator.
- jmp .Lcleanup_and_return
-.Lstore_char_result:
- movzwl 16(%esp), %eax // Copy char result to the accumulator.
- jmp .Lcleanup_and_return
-.Lstore_float_result:
- movd 16(%esp), %xmm0 // Copy float result to the context restored by
- movd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME.
- jmp .Lcleanup_and_return
-.Lstore_double_result:
- movsd 16(%esp), %xmm0 // Copy double result to the context restored by
- movsd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME.
- jmp .Lcleanup_and_return
-.Lstore_long_result:
- movl 20(%esp), %edx // Copy upper-word of result to the context restored by
- movl %edx, 72(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME.
- // Fall-through for lower bits.
-.Lstore_int_result:
- movl 16(%esp), %eax // Copy int result to the accumulator.
- // Fall-through to clean up and return.
-.Lcleanup_and_return:
- addl LITERAL(32), %esp // Pop arguments and stack allocated JValue result.
- CFI_ADJUST_CFA_OFFSET(-32)
+ push %edx // Push SP
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl %fs:THREAD_SELF_OFFSET // Push Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ push %ecx // Push receiver (method handle)
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artInvokePolymorphic) // invoke with (receiver, thread, SP)
+ addl LITERAL(16), %esp // Pop arguments.
+ CFI_ADJUST_CFA_OFFSET(-16)
+ mov %eax, 4(%esp) // Result is in EAX:EDX. Copy to saved FP state.
+ mov %edx, 8(%esp)
+ mov %edx, 40(%esp) // Copy EDX to saved context
RESTORE_SAVE_REFS_AND_ARGS_FRAME
RETURN_OR_DELIVER_PENDING_EXCEPTION
-
-.Lput_eip_in_ecx: // Internal function that puts address of
- movl 0(%esp), %ecx // next instruction into ECX when CALL
- ret
-
- // Handler table to handlers for given type.
-.Lhandler_table:
-MACRO1(HANDLER_TABLE_ENTRY, handler_label)
- // NB some tools require 16-bits for relocations. Shouldn't need adjusting.
- .word RAW_VAR(handler_label) - .Lbranch_start
-END_MACRO
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // A
- HANDLER_TABLE_ENTRY(.Lstore_int_result) // B (byte)
- HANDLER_TABLE_ENTRY(.Lstore_char_result) // C (char)
- HANDLER_TABLE_ENTRY(.Lstore_double_result) // D (double)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // E
- HANDLER_TABLE_ENTRY(.Lstore_float_result) // F (float)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // G
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // H
- HANDLER_TABLE_ENTRY(.Lstore_int_result) // I (int)
- HANDLER_TABLE_ENTRY(.Lstore_long_result) // J (long)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // K
- HANDLER_TABLE_ENTRY(.Lstore_int_result) // L (object)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // M
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // N
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // O
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // P
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Q
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // R
- HANDLER_TABLE_ENTRY(.Lstore_int_result) // S (short)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // T
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // U
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // V (void)
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // W
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // X
- HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Y
- HANDLER_TABLE_ENTRY(.Lstore_boolean_result) // Z (boolean)
-
END_FUNCTION art_quick_invoke_polymorphic
+DEFINE_FUNCTION art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame.
+ // EAX := call_site_index
+ mov %esp, %ecx // Remember SP.
+ subl LITERAL(4), %esp // Alignment padding.
+ CFI_ADJUST_CFA_OFFSET(4)
+ push %ecx // pass SP
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ push %eax // pass call_site_index
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artInvokeCustom) // artInvokeCustom(call_site_index, Thread*, SP)
+ addl LITERAL(16), %esp // Pop arguments.
+ CFI_ADJUST_CFA_OFFSET(-16)
+ mov %eax, 4(%esp) // Result is in EAX:EDX. Copy to saved FP state.
+ mov %edx, 8(%esp)
+ mov %edx, 40(%esp) // Copy EDX to saved context
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_custom
+
// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
// Argument 0: ESP+4: The context pointer for ExecuteSwitchImpl.
// Argument 1: ESP+8: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index c179033..9980966 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2418,81 +2418,29 @@
END_FUNCTION art_quick_osr_stub
DEFINE_FUNCTION art_quick_invoke_polymorphic
+ // On entry: RDI := unused, RSI := receiver
SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves
- movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread
- movq %rsp, %rcx // pass SP
- subq LITERAL(16), %rsp // make space for JValue result
- CFI_ADJUST_CFA_OFFSET(16)
- movq LITERAL(0), (%rsp) // initialize result
- movq %rsp, %rdi // store pointer to JValue result
- call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP)
+ movq %rsi, %rdi // RDI := receiver
+ movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread (self)
+ movq %rsp, %rdx // RDX := pass SP
+ call SYMBOL(artInvokePolymorphic) // invoke with (receiver, self, SP)
// save the code pointer
- subq LITERAL('A'), %rax // Convert type descriptor character value to a zero based index.
- cmpb LITERAL('Z' - 'A'), %al // Eliminate out of bounds options
- ja .Lcleanup_and_return
- movzbq %al, %rax
- leaq .Lhandler_table(%rip), %rcx // Get the address of the handler table
- movslq (%rcx, %rax, 4), %rax // Lookup handler offset relative to table
- addq %rcx, %rax // Add table address to yield handler address.
- jmpq *%rax // Jump to handler.
-
-.align 4
-.Lhandler_table: // Table of type descriptor to handlers.
-MACRO1(HANDLER_TABLE_OFFSET, handle_label)
- // NB some tools require 32-bits for relocations. Shouldn't need adjusting.
- .long RAW_VAR(handle_label) - .Lhandler_table
-END_MACRO
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte)
- HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char)
- HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E
- HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int)
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R
- HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void)
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X
- HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y
- HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean)
-
-.Lstore_boolean_result:
- movzbq (%rsp), %rax // Copy boolean result to the accumulator
- jmp .Lcleanup_and_return
-.Lstore_char_result:
- movzwq (%rsp), %rax // Copy char result to the accumulator
- jmp .Lcleanup_and_return
-.Lstore_float_result:
- movd (%rsp), %xmm0 // Copy float result to the context restored by
- movd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
- jmp .Lcleanup_and_return
-.Lstore_double_result:
- movsd (%rsp), %xmm0 // Copy double result to the context restored by
- movsd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME.
- jmp .Lcleanup_and_return
-.Lstore_long_result:
- movq (%rsp), %rax // Copy long result to the accumulator.
- // Fall-through
-.Lcleanup_and_return:
- addq LITERAL(16), %rsp // Pop space for JValue result.
- CFI_ADJUST_CFA_OFFSET(16)
RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ movq %rax, %xmm0 // Result is in RAX. Copy to FP result register.
RETURN_OR_DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_invoke_polymorphic
+DEFINE_FUNCTION art_quick_invoke_custom
+ SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves
+ // RDI := call_site_index
+ movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current()
+ movq %rsp, %rdx // RDX := SP
+ call SYMBOL(artInvokeCustom) // artInvokeCustom(Thread*, SP)
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ movq %rax, %xmm0 // Result is in RAX. Copy to FP result register.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_custom
+
// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
// Argument 0: RDI: The context pointer for ExecuteSwitchImpl.
// Argument 1: RSI: Pointer to the templated ExecuteSwitchImpl to call.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 45bf664..5b4dcb7 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -425,7 +425,7 @@
static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
uint16_t class_def_idx,
uint32_t method_idx) {
- ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx));
+ ClassAccessor accessor(dex_file, class_def_idx);
uint32_t class_def_method_index = 0u;
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
if (method.GetIndex() == method_idx) {
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 70ff40d..ffbff88 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
// Offset of field Thread::tlsPtr_.mterp_current_ibase.
#define THREAD_CURRENT_IBASE_OFFSET \
- (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+ (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 165) * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index acdb235..8a2a70e 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -68,8 +68,8 @@
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map);
+ DCHECK_EQ(dex_register_map.size(), number_of_dex_registers);
uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map);
BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
for (int i = 0; i < number_of_references; ++i) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index be636d8..1710e78 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -990,8 +990,7 @@
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(MakeObjPtr(
spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))));
- DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(),
- mirror::kClassFlagClass);
+ DCHECK_EQ(GetClassRoot<mirror::Class>(this)->GetClassFlags(), mirror::kClassFlagClass);
ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
@@ -1610,10 +1609,9 @@
hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
- static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
- "Class loader should be the last image root.");
MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
- app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
+ app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader()
+ : nullptr));
DCHECK(class_roots != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(ClassRoot::kMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -2863,9 +2861,9 @@
}
const DexFile& dex_file = klass->GetDexFile();
- const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
- CHECK(dex_class_def != nullptr);
- ClassAccessor accessor(dex_file, *dex_class_def);
+ const uint16_t class_def_idx = klass->GetDexClassDefIndex();
+ CHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
+ ClassAccessor accessor(dex_file, class_def_idx);
// There should always be class data if there were direct methods.
CHECK(accessor.HasClassData()) << klass->PrettyDescriptor();
bool has_oat_class;
diff --git a/runtime/dex_register_location.h b/runtime/dex_register_location.h
index c6d4ad2..a20dccb 100644
--- a/runtime/dex_register_location.h
+++ b/runtime/dex_register_location.h
@@ -29,6 +29,7 @@
class DexRegisterLocation {
public:
enum class Kind : int32_t {
+ kInvalid = -2, // only used internally during register map decoding.
kNone = -1, // vreg has not been set.
kInStack, // vreg is on the stack, value holds the stack offset.
kConstant, // vreg is a constant value.
@@ -40,9 +41,8 @@
DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {}
- static DexRegisterLocation None() {
- return DexRegisterLocation(Kind::kNone, 0);
- }
+ static DexRegisterLocation None() { return DexRegisterLocation(Kind::kNone, 0); }
+ static DexRegisterLocation Invalid() { return DexRegisterLocation(Kind::kInvalid, 0); }
bool IsLive() const { return kind_ != Kind::kNone; }
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 1804d9e..938489b 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -114,9 +114,9 @@
extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
-// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or
-// reference return type.
+// Polymorphic invoke entrypoints.
extern "C" void art_quick_invoke_polymorphic(uint32_t, void*);
+extern "C" void art_quick_invoke_custom(uint32_t, void*);
// Thread entrypoints.
extern "C" void art_quick_test_suspend();
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 3f66045..5dcece4 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -106,6 +106,7 @@
qpoints->pInvokeVirtualTrampolineWithAccessCheck =
art_quick_invoke_virtual_trampoline_with_access_check;
qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
+ qpoints->pInvokeCustom = art_quick_invoke_custom;
// Thread
qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 3a8faca..415a158 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -134,6 +134,7 @@
V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \
V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \
V(InvokePolymorphic, void, uint32_t, void*) \
+ V(InvokeCustom, void, uint32_t, void*) \
\
V(TestSuspend, void, void) \
\
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index af6a936..2b1623a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -35,6 +35,7 @@
#include "index_bss_mapping.h"
#include "instrumentation.h"
#include "interpreter/interpreter.h"
+#include "interpreter/interpreter_common.h"
#include "interpreter/shadow_frame-inl.h"
#include "jit/jit.h"
#include "linear_alloc.h"
@@ -2722,18 +2723,11 @@
reinterpret_cast<uintptr_t>(method));
}
-// Returns shorty type so the caller can determine how to put |result|
-// into expected registers. The shorty type is static so the compiler
-// could call different flavors of this code path depending on the
-// shorty type though this would require different entry points for
-// each type.
-extern "C" uintptr_t artInvokePolymorphic(
- JValue* result,
- mirror::Object* raw_receiver,
- Thread* self,
- ArtMethod** sp)
+// Returns uint64_t representing raw bits from JValue.
+extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* self, ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ DCHECK(raw_receiver != nullptr);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
// Start new JNI local reference state
@@ -2766,18 +2760,12 @@
ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
self, inst.VRegB(), caller_method, kVirtual);
- if (UNLIKELY(receiver_handle.IsNull())) {
- ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual);
- return static_cast<uintptr_t>('V');
- }
-
Handle<mirror::MethodType> method_type(
hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
-
- // This implies we couldn't resolve one or more types in this method handle.
if (UNLIKELY(method_type.IsNull())) {
+ // This implies we couldn't resolve one or more types in this method handle.
CHECK(self->IsExceptionPending());
- return static_cast<uintptr_t>('V');
+ return 0UL;
}
DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA());
@@ -2811,6 +2799,7 @@
// consecutive order.
RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic());
+ JValue result;
bool success = false;
if (resolved_method->GetDeclaringClass() == GetClassRoot<mirror::MethodHandle>(linker)) {
Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
@@ -2821,7 +2810,7 @@
method_handle,
method_type,
&operands,
- result);
+ &result);
} else {
DCHECK_EQ(static_cast<uint32_t>(intrinsic),
static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke));
@@ -2830,7 +2819,7 @@
method_handle,
method_type,
&operands,
- result);
+ &result);
}
} else {
DCHECK_EQ(GetClassRoot<mirror::VarHandle>(linker), resolved_method->GetDeclaringClass());
@@ -2844,7 +2833,7 @@
method_type,
access_mode,
&operands,
- result);
+ &result);
}
DCHECK(success || self->IsExceptionPending());
@@ -2852,7 +2841,65 @@
// Pop transition record.
self->PopManagedStackFragment(fragment);
- return static_cast<uintptr_t>(shorty[0]);
+ return result.GetJ();
+}
+
+// Returns uint64_t representing raw bits from JValue.
+extern "C" uint64_t artInvokeCustom(uint32_t call_site_idx, Thread* self, ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedQuickEntrypointChecks sqec(self);
+ DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
+
+ // invoke-custom is effectively a static call (no receiver).
+ static constexpr bool kMethodIsStatic = true;
+
+ // Start new JNI local reference state
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedObjectAccessUnchecked soa(env);
+ ScopedJniEnvLocalRefState env_state(env);
+
+ const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe.");
+
+ // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC.
+ ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+ const DexFile* dex_file = caller_method->GetDexFile();
+ const dex::ProtoIndex proto_idx(dex_file->GetProtoIndexForCallSite(call_site_idx));
+ const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx);
+ const uint32_t shorty_len = strlen(shorty);
+
+ // Construct the shadow frame placing arguments consecutively from |first_arg|.
+ const size_t first_arg = 0;
+ const size_t num_vregs = ArtMethod::NumArgRegisters(shorty);
+ ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+ CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, caller_method, dex_pc);
+ ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
+ ScopedStackedShadowFramePusher
+ frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
+ kMethodIsStatic,
+ shorty,
+ shorty_len,
+ shadow_frame,
+ first_arg);
+ shadow_frame_builder.VisitArguments();
+
+ // Push a transition back into managed code onto the linked list in thread.
+ ManagedStack fragment;
+ self->PushManagedStackFragment(&fragment);
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // Perform the invoke-custom operation.
+ RangeInstructionOperands operands(first_arg, num_vregs);
+ JValue result;
+ bool success =
+ interpreter::DoInvokeCustom(self, *shadow_frame, call_site_idx, &operands, &result);
+ DCHECK(success || self->IsExceptionPending());
+
+ // Pop transition record.
+ self->PopManagedStackFragment(fragment);
+
+ return result.GetJ();
}
} // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 1337cd5..dda3dde 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -287,8 +287,8 @@
pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck,
pInvokePolymorphic, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic,
- pTestSuspend, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pInvokeCustom, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeCustom, pTestSuspend, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, sizeof(void*));
diff --git a/runtime/image.cc b/runtime/image.cc
index 17fc664..7819c0b 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '1', '\0' }; // Pre-allocated Throwables.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '2', '\0' }; // Boot image live objects.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index c6fc052..c1cde0a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -211,8 +211,12 @@
kOomeWhenThrowingOome, // Pre-allocated OOME when throwing OOME.
kOomeWhenHandlingStackOverflow, // Pre-allocated OOME when handling StackOverflowError.
kNoClassDefFoundError, // Pre-allocated NoClassDefFoundError.
- kClassLoader, // App image only.
+ kSpecialRoots, // Different for boot image and app image, see aliases below.
kImageRootsMax,
+
+ // Aliases.
+ kAppImageClassLoader = kSpecialRoots, // The class loader used to build the app image.
+ kBootImageLiveObjects = kSpecialRoots, // Array of boot image objects that must be kept live.
};
enum ImageSections {
@@ -229,8 +233,10 @@
kSectionCount, // Number of elements in enum.
};
- static size_t NumberOfImageRoots(bool app_image) {
- return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+ static size_t NumberOfImageRoots(bool app_image ATTRIBUTE_UNUSED) {
+ // At the moment, boot image and app image have the same number of roots,
+ // though the meaning of the kSpecialRoots is different.
+ return kImageRootsMax;
}
ArtMethod* GetImageMethod(ImageMethod index) const;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 27f761a..92d4731 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1164,7 +1164,7 @@
ShadowFrame& shadow_frame,
uint32_t call_site_idx)
REQUIRES_SHARED(Locks::mutator_lock_) {
- StackHandleScope<7> hs(self);
+ StackHandleScope<5> hs(self);
// There are three mandatory arguments expected from the call site
// value array in the DEX file: the bootstrap method handle, the
// method name to pass to the bootstrap method, and the method type
@@ -1358,75 +1358,80 @@
}
// Check the call site target is not null as we're going to invoke it.
- Handle<mirror::CallSite> call_site =
- hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
- Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
- if (UNLIKELY(target.IsNull())) {
+ ObjPtr<mirror::CallSite> call_site =
+ ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL()));
+ ObjPtr<mirror::MethodHandle> target = call_site->GetTarget();
+ if (UNLIKELY(target == nullptr)) {
ThrowClassCastException("Bootstrap method returned a CallSite with a null target");
return nullptr;
}
- return call_site.Get();
+ return call_site;
}
-template<bool is_range>
+namespace {
+
+ObjPtr<mirror::CallSite> DoResolveCallSite(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t call_site_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+
+ // Get the call site from the DexCache if present.
+ ObjPtr<mirror::CallSite> call_site = dex_cache->GetResolvedCallSite(call_site_idx);
+ if (LIKELY(call_site != nullptr)) {
+ return call_site;
+ }
+
+ // Invoke the bootstrap method to get a candidate call site.
+ call_site = InvokeBootstrapMethod(self, shadow_frame, call_site_idx);
+ if (UNLIKELY(call_site == nullptr)) {
+ if (!self->GetException()->IsError()) {
+ // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error.
+ ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+ call_site_idx);
+ }
+ return nullptr;
+ }
+
+ // Attempt to place the candidate call site into the DexCache, return the winning call site.
+ return dex_cache->SetResolvedCallSite(call_site_idx, call_site);
+}
+
+} // namespace
+
bool DoInvokeCustom(Thread* self,
ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t call_site_idx,
+ const InstructionOperands* operands,
+ JValue* result) {
// Make sure to check for async exceptions
if (UNLIKELY(self->ObserveAsyncException())) {
return false;
}
+
// invoke-custom is not supported in transactions. In transactions
// there is a limited set of types supported. invoke-custom allows
// running arbitrary code and instantiating arbitrary types.
CHECK(!Runtime::Current()->IsActiveTransaction());
- StackHandleScope<4> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
- const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- MutableHandle<mirror::CallSite>
- call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+
+ ObjPtr<mirror::CallSite> call_site = DoResolveCallSite(self, shadow_frame, call_site_idx);
if (call_site.IsNull()) {
- call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
- if (UNLIKELY(call_site.IsNull())) {
- CHECK(self->IsExceptionPending());
- if (!self->GetException()->IsError()) {
- // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error.
- ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
- call_site_idx);
- }
- result->SetJ(0);
- return false;
- }
- mirror::CallSite* winning_call_site =
- dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
- call_site.Assign(winning_call_site);
+ DCHECK(self->IsExceptionPending());
+ return false;
}
+ StackHandleScope<2> hs(self);
Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
- DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
- if (is_range) {
- RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc());
- return MethodHandleInvokeExact(self,
- shadow_frame,
- target,
- target_method_type,
- &operands,
- result);
- } else {
- uint32_t args[Instruction::kMaxVarArgRegs];
- inst->GetVarArgs(args, inst_data);
- VarArgsInstructionOperands operands(args, inst->VRegA_35c());
- return MethodHandleInvokeExact(self,
- shadow_frame,
- target,
- target_method_type,
- &operands,
- result);
- }
+ DCHECK_EQ(operands->GetNumberOfOperands(), target_method_type->NumberOfVRegs())
+ << " call_site_idx" << call_site_idx;
+ return MethodHandleInvokeExact(self,
+ shadow_frame,
+ target,
+ target_method_type,
+ operands,
+ result);
}
// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
@@ -1847,16 +1852,6 @@
EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
-// Explicit DoInvokeCustom template function declarations.
-#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvokeCustom<_is_range>( \
- Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
- uint16_t inst_data, JValue* result)
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
-#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
-
// Explicit DoFilledNewArray template function declarations.
#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 60bf505..b324b4c 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -242,7 +242,15 @@
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
- JValue* result);
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+bool DoInvokeCustom(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t call_site_idx,
+ const InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Performs a custom invoke (invoke-custom/invoke-custom-range).
template<bool is_range>
@@ -250,7 +258,19 @@
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
- JValue* result);
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ if (is_range) {
+ RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc());
+ return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result);
+ } else {
+ uint32_t args[Instruction::kMaxVarArgRegs];
+ inst->GetVarArgs(args, inst_data);
+ VarArgsInstructionOperands operands(args, inst->VRegA_35c());
+ return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result);
+ }
+}
// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index f31a24e..0a8e0cd 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -493,8 +493,7 @@
// We found a stack map, now fill the frame with dex register values from the interpreter's
// shadow frame.
- DexRegisterMap vreg_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_vregs);
+ DexRegisterMap vreg_map = code_info.GetDexRegisterMapOf(stack_map);
frame_size = osr_method->GetFrameSizeInBytes();
@@ -510,10 +509,11 @@
memory[0] = method;
shadow_frame = thread->PopShadowFrame();
- if (!vreg_map.IsValid()) {
+ if (vreg_map.empty()) {
// If we don't have a dex register map, then there are no live dex registers at
// this dex pc.
} else {
+ DCHECK_EQ(vreg_map.size(), number_of_vregs);
for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
DexRegisterLocation::Kind location = vreg_map.GetLocationKind(vreg);
if (location == DexRegisterLocation::Kind::kNone) {
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index 8fe68bd..44679a5 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -912,7 +912,11 @@
return utf.c_str();
}
}
- env->ExceptionClear();
+ if (env->ExceptionCheck()) {
+ // We can't do much better logging, really. So leave it with a Describe.
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
return "(Error calling toString)";
}
return "null";
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 96778aa..e64a325 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -157,7 +157,8 @@
return ref.load(std::memory_order_seq_cst).Read();
}
-inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+inline ObjPtr<CallSite> DexCache::SetResolvedCallSite(uint32_t call_site_idx,
+ ObjPtr<CallSite> call_site) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index bb86004..941248e 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -320,8 +320,8 @@
// because multiple threads can invoke the bootstrap method each
// producing a call site, but the method handle invocation on the
// call site must be on a common agreed value.
- CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
- REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
+ REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
diff --git a/runtime/oat.h b/runtime/oat.h
index 72eb27d..40f4edd 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Rewrite TypeLookupTable.
- static constexpr uint8_t kOatVersion[] = { '1', '4', '7', '\0' };
+ // Last oat version changed reason: compiler support invoke-custom
+ static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 23ccf6a..cf1cbe7 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -230,19 +230,18 @@
// Find stack map of the catch block.
StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc());
DCHECK(catch_stack_map.IsValid());
- DexRegisterMap catch_vreg_map =
- code_info.GetDexRegisterMapOf(catch_stack_map, number_of_vregs);
- if (!catch_vreg_map.IsValid() || !catch_vreg_map.HasAnyLiveDexRegisters()) {
+ DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map);
+ if (!catch_vreg_map.HasAnyLiveDexRegisters()) {
return;
}
+ DCHECK_EQ(catch_vreg_map.size(), number_of_vregs);
// Find stack map of the throwing instruction.
StackMap throw_stack_map =
code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset());
DCHECK(throw_stack_map.IsValid());
- DexRegisterMap throw_vreg_map =
- code_info.GetDexRegisterMapOf(throw_stack_map, number_of_vregs);
- DCHECK(throw_vreg_map.IsValid());
+ DexRegisterMap throw_vreg_map = code_info.GetDexRegisterMapOf(throw_stack_map);
+ DCHECK_EQ(throw_vreg_map.size(), number_of_vregs);
// Copy values between them.
for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
@@ -405,14 +404,12 @@
uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map);
BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
DexRegisterMap vreg_map = IsInInlinedFrame()
- ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1,
- stack_map,
- number_of_vregs)
- : code_info.GetDexRegisterMapOf(stack_map, number_of_vregs);
-
- if (!vreg_map.IsValid()) {
+ ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, stack_map)
+ : code_info.GetDexRegisterMapOf(stack_map);
+ if (vreg_map.empty()) {
return;
}
+ DCHECK_EQ(vreg_map.size(), number_of_vregs);
for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
if (updated_vregs != nullptr && updated_vregs[vreg]) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6d10a22..a1645cb 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2045,6 +2045,7 @@
pre_allocated_OutOfMemoryError_when_handling_stack_overflow_
.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ VisitImageRoots(visitor);
verifier::MethodVerifier::VisitStaticRoots(visitor);
VisitTransactionRoots(visitor);
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index bd0d5d6..56e47b9 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -236,12 +236,12 @@
size_t depth_in_stack_map = current_inlining_depth_ - 1;
DexRegisterMap dex_register_map = IsInInlinedFrame()
- ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map, number_of_dex_registers)
- : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
-
- if (!dex_register_map.IsValid()) {
+ ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map)
+ : code_info.GetDexRegisterMapOf(stack_map);
+ if (dex_register_map.empty()) {
return false;
}
+ DCHECK_EQ(dex_register_map.size(), number_of_dex_registers);
DexRegisterLocation::Kind location_kind = dex_register_map.GetLocationKind(vreg);
switch (location_kind) {
case DexRegisterLocation::Kind::kInStack: {
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index a5749b8..a25c9fd 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -25,6 +25,69 @@
namespace art {
+// Scan backward to determine dex register locations at given stack map.
+// All registers for a stack map are combined - inlined registers are just appended,
+// therefore 'first_dex_register' allows us to select a sub-range to decode.
+void CodeInfo::DecodeDexRegisterMap(uint32_t stack_map_index,
+ uint32_t first_dex_register,
+ /*out*/ DexRegisterMap* map) const {
+ // Count remaining work so we know when we have finished.
+ uint32_t remaining_registers = map->size();
+
+ // Keep scanning backwards and collect the most recent location of each register.
+ for (int32_t s = stack_map_index; s >= 0 && remaining_registers != 0; s--) {
+ StackMap stack_map = GetStackMapAt(s);
+ DCHECK_LE(stack_map_index - s, kMaxDexRegisterMapSearchDistance) << "Unbounded search";
+
+ // The mask specifies which registers where modified in this stack map.
+ // NB: the mask can be shorter than expected if trailing zero bits were removed.
+ uint32_t mask_index = stack_map.GetDexRegisterMaskIndex();
+ if (mask_index == StackMap::kNoValue) {
+ continue; // Nothing changed at this stack map.
+ }
+ BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index);
+ if (mask.size_in_bits() <= first_dex_register) {
+ continue; // Nothing changed after the first register we are interested in.
+ }
+
+ // The map stores one catalogue index per each modified register location.
+ uint32_t map_index = stack_map.GetDexRegisterMapIndex();
+ DCHECK_NE(map_index, StackMap::kNoValue);
+
+ // Skip initial registers which we are not interested in (to get to inlined registers).
+ map_index += mask.PopCount(0, first_dex_register);
+ mask = mask.Subregion(first_dex_register, mask.size_in_bits() - first_dex_register);
+
+ // Update registers that we see for first time (i.e. most recent value).
+ DexRegisterLocation* regs = map->data();
+ const uint32_t end = std::min<uint32_t>(map->size(), mask.size_in_bits());
+ const size_t kNumBits = BitSizeOf<uint32_t>();
+ for (uint32_t reg = 0; reg < end; reg += kNumBits) {
+ // Process the mask in chunks of kNumBits for performance.
+ uint32_t bits = mask.LoadBits(reg, std::min<uint32_t>(end - reg, kNumBits));
+ while (bits != 0) {
+ uint32_t bit = CTZ(bits);
+ if (regs[reg + bit].GetKind() == DexRegisterLocation::Kind::kInvalid) {
+ regs[reg + bit] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index));
+ remaining_registers--;
+ }
+ map_index++;
+ bits ^= 1u << bit; // Clear the bit.
+ }
+ }
+ }
+
+ // Set any remaining registers to None (which is the default state at first stack map).
+ if (remaining_registers != 0) {
+ DexRegisterLocation* regs = map->data();
+ for (uint32_t r = 0; r < map->size(); r++) {
+ if (regs[r].GetKind() == DexRegisterLocation::Kind::kInvalid) {
+ regs[r] = DexRegisterLocation::None();
+ }
+ }
+ }
+}
+
std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) {
using Kind = DexRegisterLocation::Kind;
switch (reg.GetKind()) {
@@ -42,6 +105,8 @@
return stream << "f" << reg.GetValue() << "/hi";
case Kind::kConstant:
return stream << "#" << reg.GetValue();
+ case Kind::kInvalid:
+ return stream << "Invalid";
default:
return stream << "DexRegisterLocation(" << static_cast<uint32_t>(reg.GetKind())
<< "," << reg.GetValue() << ")";
@@ -50,7 +115,7 @@
static void DumpDexRegisterMap(VariableIndentationOutputStream* vios,
const DexRegisterMap& map) {
- if (map.IsValid()) {
+ if (!map.empty()) {
ScopedIndentation indent1(vios);
for (size_t i = 0; i < map.size(); ++i) {
if (map.IsDexRegisterLive(i)) {
@@ -98,7 +163,6 @@
void CodeInfo::Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
- uint16_t num_dex_registers,
bool verbose,
InstructionSet instruction_set,
const MethodInfo& method_info) const {
@@ -120,7 +184,7 @@
if (verbose) {
for (size_t i = 0; i < GetNumberOfStackMaps(); ++i) {
StackMap stack_map = GetStackMapAt(i);
- stack_map.Dump(vios, *this, method_info, code_offset, num_dex_registers, instruction_set);
+ stack_map.Dump(vios, *this, method_info, code_offset, instruction_set);
}
}
}
@@ -129,7 +193,6 @@
const CodeInfo& code_info,
const MethodInfo& method_info,
uint32_t code_offset,
- uint16_t number_of_dex_registers,
InstructionSet instruction_set) const {
const uint32_t pc_offset = GetNativePcOffset(instruction_set);
vios->Stream()
@@ -145,22 +208,18 @@
vios->Stream() << stack_mask.LoadBit(e - i - 1);
}
vios->Stream() << ")\n";
- DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this, number_of_dex_registers));
+ DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this));
uint32_t depth = code_info.GetInlineDepthOf(*this);
for (size_t d = 0; d < depth; d++) {
InlineInfo inline_info = code_info.GetInlineInfoAtDepth(*this, d);
- // We do not know the length of the dex register maps of inlined frames
- // at this level, so we just pass null to `InlineInfo::Dump` to tell
- // it not to look at these maps.
- inline_info.Dump(vios, code_info, *this, method_info, 0);
+ inline_info.Dump(vios, code_info, *this, method_info);
}
}
void InlineInfo::Dump(VariableIndentationOutputStream* vios,
const CodeInfo& code_info,
const StackMap& stack_map,
- const MethodInfo& method_info,
- uint16_t number_of_dex_registers) const {
+ const MethodInfo& method_info) const {
uint32_t depth = Row() - stack_map.GetInlineInfoIndex();
vios->Stream()
<< "InlineInfo[" << Row() << "]"
@@ -176,10 +235,7 @@
<< ", method_index=" << GetMethodIndex(method_info);
}
vios->Stream() << ")\n";
- if (number_of_dex_registers != 0) {
- uint16_t vregs = number_of_dex_registers;
- DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(depth, stack_map, vregs));
- }
+ DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(depth, stack_map));
}
} // namespace art
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index b04197e..53f80e5 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -39,6 +39,12 @@
// (signed) values.
static constexpr ssize_t kFrameSlotSize = 4;
+// The delta compression of dex register maps means we need to scan the stackmaps backwards.
+// We compress the data in such a way so that there is an upper bound on the search distance.
+// Max distance 0 means each stack map must be fully defined and no scanning back is allowed.
+// If this value is changed, the oat file version should be incremented (for DCHECK to pass).
+static constexpr size_t kMaxDexRegisterMapSearchDistance = 32;
+
class ArtMethod;
class CodeInfo;
@@ -49,12 +55,14 @@
// If the size is small enough, it keeps the data on the stack.
class DexRegisterMap {
public:
- // Create map for given number of registers and initialize all locations to None.
- explicit DexRegisterMap(size_t count) : count_(count), regs_small_{} {
+ using iterator = DexRegisterLocation*;
+
+ // Create map for given number of registers and initialize them to the given value.
+ DexRegisterMap(size_t count, DexRegisterLocation value) : count_(count), regs_small_{} {
if (count_ <= kSmallCount) {
- std::fill_n(regs_small_.begin(), count, DexRegisterLocation::None());
+ std::fill_n(regs_small_.begin(), count, value);
} else {
- regs_large_.resize(count, DexRegisterLocation::None());
+ regs_large_.resize(count, value);
}
}
@@ -62,9 +70,12 @@
return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data();
}
+ iterator begin() { return data(); }
+ iterator end() { return data() + count_; }
+
size_t size() const { return count_; }
- bool IsValid() const { return count_ != 0; }
+ bool empty() const { return count_ == 0; }
DexRegisterLocation Get(size_t index) const {
DCHECK_LT(index, count_);
@@ -147,38 +158,26 @@
*/
class StackMap : public BitTable<7>::Accessor {
public:
- enum Field {
- kPackedNativePc,
- kDexPc,
- kRegisterMaskIndex,
- kStackMaskIndex,
- kInlineInfoIndex,
- kDexRegisterMaskIndex,
- kDexRegisterMapIndex,
- kCount,
- };
-
- StackMap() : BitTable<kCount>::Accessor(nullptr, -1) {}
- StackMap(const BitTable<kCount>* table, uint32_t row)
- : BitTable<kCount>::Accessor(table, row) {}
+ BIT_TABLE_HEADER()
+ BIT_TABLE_COLUMN(0, PackedNativePc)
+ BIT_TABLE_COLUMN(1, DexPc)
+ BIT_TABLE_COLUMN(2, RegisterMaskIndex)
+ BIT_TABLE_COLUMN(3, StackMaskIndex)
+ BIT_TABLE_COLUMN(4, InlineInfoIndex)
+ BIT_TABLE_COLUMN(5, DexRegisterMaskIndex)
+ BIT_TABLE_COLUMN(6, DexRegisterMapIndex)
ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
return UnpackNativePc(Get<kPackedNativePc>(), instruction_set);
}
- uint32_t GetDexPc() const { return Get<kDexPc>(); }
+ ALWAYS_INLINE bool HasInlineInfo() const {
+ return HasInlineInfoIndex();
+ }
- uint32_t GetDexRegisterMaskIndex() const { return Get<kDexRegisterMaskIndex>(); }
-
- uint32_t GetDexRegisterMapIndex() const { return Get<kDexRegisterMapIndex>(); }
- bool HasDexRegisterMap() const { return GetDexRegisterMapIndex() != kNoValue; }
-
- uint32_t GetInlineInfoIndex() const { return Get<kInlineInfoIndex>(); }
- bool HasInlineInfo() const { return GetInlineInfoIndex() != kNoValue; }
-
- uint32_t GetRegisterMaskIndex() const { return Get<kRegisterMaskIndex>(); }
-
- uint32_t GetStackMaskIndex() const { return Get<kStackMaskIndex>(); }
+ ALWAYS_INLINE bool HasDexRegisterMap() const {
+ return HasDexRegisterMapIndex();
+ }
static uint32_t PackNativePc(uint32_t native_pc, InstructionSet isa) {
DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa));
@@ -195,7 +194,6 @@
const CodeInfo& code_info,
const MethodInfo& method_info,
uint32_t code_offset,
- uint16_t number_of_dex_registers,
InstructionSet instruction_set) const;
};
@@ -204,106 +202,65 @@
* The row referenced from the StackMap holds information at depth 0.
* Following rows hold information for further depths.
*/
-class InlineInfo : public BitTable<7>::Accessor {
+class InlineInfo : public BitTable<6>::Accessor {
public:
- enum Field {
- kIsLast, // Determines if there are further rows for further depths.
- kDexPc,
- kMethodIndexIdx,
- kArtMethodHi, // High bits of ArtMethod*.
- kArtMethodLo, // Low bits of ArtMethod*.
- kDexRegisterMaskIndex,
- kDexRegisterMapIndex,
- kCount,
- };
+ BIT_TABLE_HEADER()
+ BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths.
+ BIT_TABLE_COLUMN(1, DexPc)
+ BIT_TABLE_COLUMN(2, MethodInfoIndex)
+ BIT_TABLE_COLUMN(3, ArtMethodHi) // High bits of ArtMethod*.
+ BIT_TABLE_COLUMN(4, ArtMethodLo) // Low bits of ArtMethod*.
+ BIT_TABLE_COLUMN(5, NumberOfDexRegisters) // Includes outer levels and the main method.
+ BIT_TABLE_COLUMN(6, DexRegisterMapIndex)
+
static constexpr uint32_t kLast = -1;
static constexpr uint32_t kMore = 0;
- InlineInfo(const BitTable<kCount>* table, uint32_t row)
- : BitTable<kCount>::Accessor(table, row) {}
-
- uint32_t GetIsLast() const { return Get<kIsLast>(); }
-
- uint32_t GetMethodIndexIdx() const {
- DCHECK(!EncodesArtMethod());
- return Get<kMethodIndexIdx>();
- }
-
uint32_t GetMethodIndex(const MethodInfo& method_info) const {
- return method_info.GetMethodIndex(GetMethodIndexIdx());
- }
-
- uint32_t GetDexPc() const {
- return Get<kDexPc>();
+ return method_info.GetMethodIndex(GetMethodInfoIndex());
}
bool EncodesArtMethod() const {
- return Get<kArtMethodLo>() != kNoValue;
+ return HasArtMethodLo();
}
ArtMethod* GetArtMethod() const {
- uint64_t lo = Get<kArtMethodLo>();
- uint64_t hi = Get<kArtMethodHi>();
+ uint64_t lo = GetArtMethodLo();
+ uint64_t hi = GetArtMethodHi();
return reinterpret_cast<ArtMethod*>((hi << 32) | lo);
}
- uint32_t GetDexRegisterMaskIndex() const {
- return Get<kDexRegisterMaskIndex>();
- }
-
- uint32_t GetDexRegisterMapIndex() const {
- return Get<kDexRegisterMapIndex>();
- }
- bool HasDexRegisterMap() const {
- return GetDexRegisterMapIndex() != kNoValue;
- }
-
void Dump(VariableIndentationOutputStream* vios,
const CodeInfo& info,
const StackMap& stack_map,
- const MethodInfo& method_info,
- uint16_t number_of_dex_registers) const;
+ const MethodInfo& method_info) const;
};
class InvokeInfo : public BitTable<3>::Accessor {
public:
- enum Field {
- kPackedNativePc,
- kInvokeType,
- kMethodIndexIdx,
- kCount,
- };
-
- InvokeInfo(const BitTable<kCount>* table, uint32_t row)
- : BitTable<kCount>::Accessor(table, row) {}
+ BIT_TABLE_HEADER()
+ BIT_TABLE_COLUMN(0, PackedNativePc)
+ BIT_TABLE_COLUMN(1, InvokeType)
+ BIT_TABLE_COLUMN(2, MethodInfoIndex)
ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
return StackMap::UnpackNativePc(Get<kPackedNativePc>(), instruction_set);
}
- uint32_t GetInvokeType() const { return Get<kInvokeType>(); }
-
- uint32_t GetMethodIndexIdx() const { return Get<kMethodIndexIdx>(); }
-
uint32_t GetMethodIndex(MethodInfo method_info) const {
- return method_info.GetMethodIndex(GetMethodIndexIdx());
+ return method_info.GetMethodIndex(GetMethodInfoIndex());
}
};
class DexRegisterInfo : public BitTable<2>::Accessor {
public:
- enum Field {
- kKind,
- kPackedValue,
- kCount,
- };
-
- DexRegisterInfo(const BitTable<kCount>* table, uint32_t row)
- : BitTable<kCount>::Accessor(table, row) {}
+ BIT_TABLE_HEADER()
+ BIT_TABLE_COLUMN(0, Kind)
+ BIT_TABLE_COLUMN(1, PackedValue)
ALWAYS_INLINE DexRegisterLocation GetLocation() const {
- DexRegisterLocation::Kind kind = static_cast<DexRegisterLocation::Kind>(Get<kKind>());
- return DexRegisterLocation(kind, UnpackValue(kind, Get<kPackedValue>()));
+ DexRegisterLocation::Kind kind = static_cast<DexRegisterLocation::Kind>(GetKind());
+ return DexRegisterLocation(kind, UnpackValue(kind, GetPackedValue()));
}
static uint32_t PackValue(DexRegisterLocation::Kind kind, uint32_t value) {
@@ -328,17 +285,12 @@
// therefore it is worth encoding the mask as value+shift.
class RegisterMask : public BitTable<2>::Accessor {
public:
- enum Field {
- kValue,
- kShift,
- kCount,
- };
-
- RegisterMask(const BitTable<kCount>* table, uint32_t row)
- : BitTable<kCount>::Accessor(table, row) {}
+ BIT_TABLE_HEADER()
+ BIT_TABLE_COLUMN(0, Value)
+ BIT_TABLE_COLUMN(1, Shift)
ALWAYS_INLINE uint32_t GetMask() const {
- return Get<kValue>() << Get<kShift>();
+ return GetValue() << GetShift();
}
};
@@ -391,7 +343,9 @@
}
ALWAYS_INLINE DexRegisterLocation GetDexRegisterCatalogEntry(size_t index) const {
- return DexRegisterInfo(&dex_register_catalog_, index).GetLocation();
+ return (index == StackMap::kNoValue)
+ ? DexRegisterLocation::None()
+ : DexRegisterInfo(&dex_register_catalog_, index).GetLocation();
}
uint32_t GetNumberOfStackMaps() const {
@@ -402,20 +356,28 @@
return InvokeInfo(&invoke_infos_, index);
}
- ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
- size_t num_dex_registers) const {
- return DecodeDexRegisterMap(stack_map.GetDexRegisterMaskIndex(),
- stack_map.GetDexRegisterMapIndex(),
- num_dex_registers);
+ ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const {
+ if (stack_map.HasDexRegisterMap()) {
+ DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid());
+ DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register */ 0, &map);
+ return map;
+ }
+ return DexRegisterMap(0, DexRegisterLocation::None());
}
- ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
- StackMap stack_map,
- size_t num_dex_registers) const {
- InlineInfo inline_info = GetInlineInfoAtDepth(stack_map, depth);
- return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndex(),
- inline_info.GetDexRegisterMapIndex(),
- num_dex_registers);
+ ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, StackMap stack_map) const {
+ if (stack_map.HasDexRegisterMap()) {
+ // The register counts are commutative and include all outer levels.
+ // This allows us to determine the range [first, last) in just two lookups.
+ // If we are at depth 0 (the first inlinee), the count from the main method is used.
+ uint32_t first = (depth == 0) ? number_of_dex_registers_
+ : GetInlineInfoAtDepth(stack_map, depth - 1).GetNumberOfDexRegisters();
+ uint32_t last = GetInlineInfoAtDepth(stack_map, depth).GetNumberOfDexRegisters();
+ DexRegisterMap map(last - first, DexRegisterLocation::Invalid());
+ DecodeDexRegisterMap(stack_map.Row(), first, &map);
+ return map;
+ }
+ return DexRegisterMap(0, DexRegisterLocation::None());
}
InlineInfo GetInlineInfo(size_t index) const {
@@ -474,8 +436,6 @@
if (other.GetDexPc() == dex_pc &&
other.GetNativePcOffset(kRuntimeISA) ==
stack_map.GetNativePcOffset(kRuntimeISA)) {
- DCHECK_EQ(other.GetDexRegisterMapIndex(),
- stack_map.GetDexRegisterMapIndex());
if (i < e - 2) {
// Make sure there are not three identical stack maps following each other.
DCHECK_NE(
@@ -509,36 +469,22 @@
return item;
}
}
- return InvokeInfo(&invoke_infos_, -1);
+ return InvokeInfo();
}
// Dump this CodeInfo object on `vios`.
// `code_offset` is the (absolute) native PC of the compiled method.
void Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
- uint16_t number_of_dex_registers,
bool verbose,
InstructionSet instruction_set,
const MethodInfo& method_info) const;
private:
- ALWAYS_INLINE DexRegisterMap DecodeDexRegisterMap(uint32_t mask_index,
- uint32_t map_index,
- uint32_t num_dex_registers) const {
- DexRegisterMap map(map_index == StackMap::kNoValue ? 0 : num_dex_registers);
- if (mask_index != StackMap::kNoValue) {
- BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index);
- num_dex_registers = std::min<uint32_t>(num_dex_registers, mask.size_in_bits());
- DexRegisterLocation* regs = map.data();
- for (uint32_t r = 0; r < mask.size_in_bits(); r++) {
- if (mask.LoadBit(r) /* is_live */) {
- DCHECK_LT(r, map.size());
- regs[r] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index++));
- }
- }
- }
- return map;
- }
+ // Scan backward to determine dex register locations at given stack map.
+ void DecodeDexRegisterMap(uint32_t stack_map_index,
+ uint32_t first_dex_register,
+ /*out*/ DexRegisterMap* map) const;
void Decode(const uint8_t* data) {
size_t non_header_size = DecodeUnsignedLeb128(&data);
@@ -553,18 +499,20 @@
dex_register_masks_.Decode(region, &bit_offset);
dex_register_maps_.Decode(region, &bit_offset);
dex_register_catalog_.Decode(region, &bit_offset);
+ number_of_dex_registers_ = DecodeVarintBits(region, &bit_offset);
CHECK_EQ(non_header_size, BitsToBytesRoundUp(bit_offset)) << "Invalid CodeInfo";
}
size_t size_;
- BitTable<StackMap::Field::kCount> stack_maps_;
- BitTable<RegisterMask::Field::kCount> register_masks_;
+ BitTable<StackMap::kCount> stack_maps_;
+ BitTable<RegisterMask::kCount> register_masks_;
BitTable<1> stack_masks_;
- BitTable<InvokeInfo::Field::kCount> invoke_infos_;
- BitTable<InlineInfo::Field::kCount> inline_infos_;
+ BitTable<InvokeInfo::kCount> invoke_infos_;
+ BitTable<InlineInfo::kCount> inline_infos_;
BitTable<1> dex_register_masks_;
BitTable<1> dex_register_maps_;
- BitTable<DexRegisterInfo::Field::kCount> dex_register_catalog_;
+ BitTable<DexRegisterInfo::kCount> dex_register_catalog_;
+ uint32_t number_of_dex_registers_; // Excludes any inlined methods.
friend class OatDumper;
};
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 210e1b0..4a53425 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3645,8 +3645,7 @@
RootVisitor& _visitor)
: number_of_dex_registers(method->DexInstructionData().RegistersSize()),
code_info(_code_info),
- dex_register_map(code_info.GetDexRegisterMapOf(map,
- number_of_dex_registers)),
+ dex_register_map(code_info.GetDexRegisterMapOf(map)),
visitor(_visitor) {
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5961748..61ddded 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -212,8 +212,6 @@
bool allow_soft_failures,
HardFailLogMode log_level,
std::string* error) {
- SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
-
// A class must not be abstract and final.
if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) {
*error = "Verifier rejected class ";
@@ -223,6 +221,7 @@
}
ClassAccessor accessor(*dex_file, class_def);
+ SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(accessor.GetDescriptor());
int64_t previous_method_idx[2] = { -1, -1 };
MethodVerifier::FailureData failure_data;
@@ -3054,10 +3053,7 @@
// Step 2. Check the register arguments correspond to the expected arguments for the
// method handle produced by step 1. The dex file verifier has checked ranges for
// the first three arguments and CheckCallSite has checked the method handle type.
- CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
- it.Next(); // Skip to name.
- it.Next(); // Skip to method type of the method handle
- const dex::ProtoIndex proto_idx(it.GetJavaValue().c);
+ const dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx);
const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
DexFileParameterIterator param_it(*dex_file_, proto_id);
// Treat method as static as it has yet to be determined.
@@ -3073,8 +3069,6 @@
work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
}
just_set_result = true;
- // TODO: Add compiler support for invoke-custom (b/35337872).
- Fail(VERIFY_ERROR_FORCE_INTERPRETER);
break;
}
case Instruction::NEG_INT:
@@ -4008,24 +4002,41 @@
CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
// Check essential arguments are provided. The dex file verifier has verified indicies of the
// main values (method handle, name, method_type).
- if (it.Size() < 3) {
+ static const size_t kRequiredArguments = 3;
+ if (it.Size() < kRequiredArguments) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
<< " has too few arguments: "
- << it.Size() << " < 3";
+ << it.Size() << " < " << kRequiredArguments;
return false;
}
- // Get and check the first argument: the method handle (index range
- // checked by the dex file verifier).
- uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
- if (method_handle_idx > dex_file_->NumMethodHandles()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
- << " method handle index invalid " << method_handle_idx
- << " >= " << dex_file_->NumMethodHandles();
- return false;
+ std::pair<const EncodedArrayValueIterator::ValueType, size_t> type_and_max[kRequiredArguments] =
+ { { EncodedArrayValueIterator::ValueType::kMethodHandle, dex_file_->NumMethodHandles() },
+ { EncodedArrayValueIterator::ValueType::kString, dex_file_->NumStringIds() },
+ { EncodedArrayValueIterator::ValueType::kMethodType, dex_file_->NumProtoIds() }
+ };
+ uint32_t index[kRequiredArguments];
+
+ // Check arguments have expected types and are within permitted ranges.
+ for (size_t i = 0; i < kRequiredArguments; ++i) {
+ if (it.GetValueType() != type_and_max[i].first) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
+ << " argument " << i << " has wrong type "
+ << it.GetValueType() << "!=" << type_and_max[i].first;
+ return false;
+ }
+ index[i] = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (index[i] >= type_and_max[i].second) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx
+ << " argument " << i << " bad index "
+ << index[i] << " >= " << type_and_max[i].second;
+ return false;
+ }
+ it.Next();
}
- const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+ // Check method handle kind is valid.
+ const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(index[0]);
if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
<< " argument 0 method handle type is not InvokeStatic: "
diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build
deleted file mode 100644
index d85147f..0000000
--- a/test/551-checker-shifter-operand/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2018 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.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index b3e4a60..8311b60 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -896,7 +896,7 @@
}
// Each test line below should see one merge.
- /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
/// CHECK: DataProcWithShifterOp
/// CHECK: DataProcWithShifterOp
/// CHECK: DataProcWithShifterOp
@@ -933,7 +933,7 @@
/// CHECK-NOT: DataProcWithShifterOp
// On ARM shifts by 1 are not merged.
- /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
/// CHECK: Shl
/// CHECK-NOT: Shl
/// CHECK: Shr
@@ -941,7 +941,7 @@
/// CHECK: UShr
/// CHECK-NOT: UShr
- /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
/// CHECK: DataProcWithShifterOp
/// CHECK: DataProcWithShifterOp
/// CHECK: DataProcWithShifterOp
@@ -980,50 +980,98 @@
/// CHECK: DataProcWithShifterOp
/// CHECK-NOT: DataProcWithShifterOp
- /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
/// CHECK-NOT: Shl
/// CHECK-NOT: Shr
/// CHECK-NOT: UShr
- public static void $opt$validateShiftLong(long a, long b) {
- assertLongEquals(a + $noinline$LongShl(b, 1), a + (b << 1));
- assertLongEquals(a + $noinline$LongShl(b, 6), a + (b << 6));
- assertLongEquals(a + $noinline$LongShl(b, 7), a + (b << 7));
- assertLongEquals(a + $noinline$LongShl(b, 8), a + (b << 8));
- assertLongEquals(a + $noinline$LongShl(b, 14), a + (b << 14));
- assertLongEquals(a + $noinline$LongShl(b, 15), a + (b << 15));
- assertLongEquals(a + $noinline$LongShl(b, 16), a + (b << 16));
- assertLongEquals(a + $noinline$LongShl(b, 30), a + (b << 30));
- assertLongEquals(a + $noinline$LongShl(b, 31), a + (b << 31));
- assertLongEquals(a + $noinline$LongShl(b, 32), a + (b << 32));
- assertLongEquals(a + $noinline$LongShl(b, 62), a + (b << 62));
- assertLongEquals(a + $noinline$LongShl(b, 63), a + (b << 63));
+ public static long[] $opt$validateShiftLong(long a, long b) {
+ long[] results = new long[36];
- assertLongEquals(a - $noinline$LongShr(b, 1), a - (b >> 1));
- assertLongEquals(a - $noinline$LongShr(b, 6), a - (b >> 6));
- assertLongEquals(a - $noinline$LongShr(b, 7), a - (b >> 7));
- assertLongEquals(a - $noinline$LongShr(b, 8), a - (b >> 8));
- assertLongEquals(a - $noinline$LongShr(b, 14), a - (b >> 14));
- assertLongEquals(a - $noinline$LongShr(b, 15), a - (b >> 15));
- assertLongEquals(a - $noinline$LongShr(b, 16), a - (b >> 16));
- assertLongEquals(a - $noinline$LongShr(b, 30), a - (b >> 30));
- assertLongEquals(a - $noinline$LongShr(b, 31), a - (b >> 31));
- assertLongEquals(a - $noinline$LongShr(b, 32), a - (b >> 32));
- assertLongEquals(a - $noinline$LongShr(b, 62), a - (b >> 62));
- assertLongEquals(a - $noinline$LongShr(b, 63), a - (b >> 63));
+ results[0] = a + (b << 1);
+ results[1] = a + (b << 6);
+ results[2] = a + (b << 7);
+ results[3] = a + (b << 8);
+ results[4] = a + (b << 14);
+ results[5] = a + (b << 15);
+ results[6] = a + (b << 16);
+ results[7] = a + (b << 30);
+ results[8] = a + (b << 31);
+ results[9] = a + (b << 32);
+ results[10] = a + (b << 62);
+ results[11] = a + (b << 63);
- assertLongEquals(a ^ $noinline$LongUshr(b, 1), a ^ (b >>> 1));
- assertLongEquals(a ^ $noinline$LongUshr(b, 6), a ^ (b >>> 6));
- assertLongEquals(a ^ $noinline$LongUshr(b, 7), a ^ (b >>> 7));
- assertLongEquals(a ^ $noinline$LongUshr(b, 8), a ^ (b >>> 8));
- assertLongEquals(a ^ $noinline$LongUshr(b, 14), a ^ (b >>> 14));
- assertLongEquals(a ^ $noinline$LongUshr(b, 15), a ^ (b >>> 15));
- assertLongEquals(a ^ $noinline$LongUshr(b, 16), a ^ (b >>> 16));
- assertLongEquals(a ^ $noinline$LongUshr(b, 30), a ^ (b >>> 30));
- assertLongEquals(a ^ $noinline$LongUshr(b, 31), a ^ (b >>> 31));
- assertLongEquals(a ^ $noinline$LongUshr(b, 32), a ^ (b >>> 32));
- assertLongEquals(a ^ $noinline$LongUshr(b, 62), a ^ (b >>> 62));
- assertLongEquals(a ^ $noinline$LongUshr(b, 63), a ^ (b >>> 63));
+ results[12] = a - (b >> 1);
+ results[13] = a - (b >> 6);
+ results[14] = a - (b >> 7);
+ results[15] = a - (b >> 8);
+ results[16] = a - (b >> 14);
+ results[17] = a - (b >> 15);
+ results[18] = a - (b >> 16);
+ results[19] = a - (b >> 30);
+ results[20] = a - (b >> 31);
+ results[21] = a - (b >> 32);
+ results[22] = a - (b >> 62);
+ results[23] = a - (b >> 63);
+
+ results[24] = a ^ (b >>> 1);
+ results[25] = a ^ (b >>> 6);
+ results[26] = a ^ (b >>> 7);
+ results[27] = a ^ (b >>> 8);
+ results[28] = a ^ (b >>> 14);
+ results[29] = a ^ (b >>> 15);
+ results[30] = a ^ (b >>> 16);
+ results[31] = a ^ (b >>> 30);
+ results[32] = a ^ (b >>> 31);
+ results[33] = a ^ (b >>> 32);
+ results[34] = a ^ (b >>> 62);
+ results[35] = a ^ (b >>> 63);
+
+ return results;
+ }
+
+ public static void $opt$validateShiftLongAsserts(long a, long b) {
+ long[] results = $opt$validateShiftLong(a, b);
+ assertIntEquals(3 * 12, results.length);
+
+ assertLongEquals(a + $noinline$LongShl(b, 1), results[0]);
+ assertLongEquals(a + $noinline$LongShl(b, 6), results[1]);
+ assertLongEquals(a + $noinline$LongShl(b, 7), results[2]);
+ assertLongEquals(a + $noinline$LongShl(b, 8), results[3]);
+ assertLongEquals(a + $noinline$LongShl(b, 14), results[4]);
+ assertLongEquals(a + $noinline$LongShl(b, 15), results[5]);
+ assertLongEquals(a + $noinline$LongShl(b, 16), results[6]);
+ assertLongEquals(a + $noinline$LongShl(b, 30), results[7]);
+ assertLongEquals(a + $noinline$LongShl(b, 31), results[8]);
+ assertLongEquals(a + $noinline$LongShl(b, 32), results[9]);
+ assertLongEquals(a + $noinline$LongShl(b, 62), results[10]);
+ assertLongEquals(a + $noinline$LongShl(b, 63), results[11]);
+
+ assertLongEquals(a - $noinline$LongShr(b, 1), results[12]);
+ assertLongEquals(a - $noinline$LongShr(b, 6), results[13]);
+ assertLongEquals(a - $noinline$LongShr(b, 7), results[14]);
+ assertLongEquals(a - $noinline$LongShr(b, 8), results[15]);
+ assertLongEquals(a - $noinline$LongShr(b, 14), results[16]);
+ assertLongEquals(a - $noinline$LongShr(b, 15), results[17]);
+ assertLongEquals(a - $noinline$LongShr(b, 16), results[18]);
+ assertLongEquals(a - $noinline$LongShr(b, 30), results[19]);
+ assertLongEquals(a - $noinline$LongShr(b, 31), results[20]);
+ assertLongEquals(a - $noinline$LongShr(b, 32), results[21]);
+ assertLongEquals(a - $noinline$LongShr(b, 62), results[22]);
+ assertLongEquals(a - $noinline$LongShr(b, 63), results[23]);
+
+ assertLongEquals(a ^ $noinline$LongUshr(b, 1), results[24]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 6), results[25]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 7), results[26]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 8), results[27]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 14), results[28]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 15), results[29]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 16), results[30]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 30), results[31]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 31), results[32]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 32), results[33]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 62), results[34]);
+ assertLongEquals(a ^ $noinline$LongUshr(b, 63), results[35]);
}
@@ -1072,7 +1120,7 @@
$opt$validateExtendLong(inputs[i], inputs[j]);
$opt$validateShiftInt((int)inputs[i], (int)inputs[j]);
- $opt$validateShiftLong(inputs[i], inputs[j]);
+ $opt$validateShiftLongAsserts(inputs[i], inputs[j]);
}
}
diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build
deleted file mode 100755
index 10ffcc5..0000000
--- a/test/565-checker-doublenegbitwise/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
index 2e08022..ce69154 100644
--- a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
+++ b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
@@ -403,3 +403,591 @@
sput-boolean v0, LSmaliTests;->doThrow:Z
return-void
.end method
+
+
+# Test transformation of Not/Not/And into Or/Not.
+
+# Note: before the instruction_simplifier pass, Xor's are used instead of
+# Not's (the simplification happens during the same pass).
+## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after)
+## CHECK-NOT: And
+
+# Original java source:
+#
+# public static int $opt$noinline$andToOr(int a, int b) {
+# if (doThrow) throw new Error();
+# return ~a & ~b;
+# }
+
+.method public static $opt$noinline$andToOrV2(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ .line 85
+ sget-boolean v0, LMain;->doThrow:Z
+
+ if-eqz v0, :cond_a
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 86
+ :cond_a
+ xor-int/lit8 v0, p0, -0x1
+
+ xor-int/lit8 v1, p1, -0x1
+
+ and-int/2addr v0, v1
+
+ return v0
+.end method
+
+
+# Test transformation of Not/Not/And into Or/Not for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+## CHECK-DAG: <<And:i\d+>> And [<<Select1>>,<<Select2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: And
+
+# Original java source:
+#
+# public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
+# if (doThrow) throw new Error();
+# return !a & !b;
+# }
+
+.method public static $opt$noinline$booleanAndToOrV2(ZZ)Z
+ .registers 5
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ const/4 v0, 0x1
+
+ const/4 v1, 0x0
+
+ .line 122
+ sget-boolean v2, LMain;->doThrow:Z
+
+ if-eqz v2, :cond_c
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 123
+ :cond_c
+ if-nez p0, :cond_13
+
+ move v2, v0
+
+ :goto_f
+ if-nez p1, :cond_15
+
+ :goto_11
+ and-int/2addr v0, v2
+
+ return v0
+
+ :cond_13
+ move v2, v1
+
+ goto :goto_f
+
+ :cond_15
+ move v0, v1
+
+ goto :goto_11
+.end method
+
+
+# Test transformation of Not/Not/Or into And/Not.
+
+# See note above.
+# The second Xor has its arguments reversed for no obvious reason.
+## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (before)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<CstM1:j\d+>> LongConstant -1
+## CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>]
+## CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>]
+## CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after)
+## CHECK-NOT: Or
+
+# Original java source:
+#
+# public static long $opt$noinline$orToAnd(long a, long b) {
+# if (doThrow) throw new Error();
+# return ~a | ~b;
+# }
+
+.method public static $opt$noinline$orToAndV2(JJ)J
+ .registers 8
+ .param p0, "a" # J
+ .param p2, "b" # J
+
+ .prologue
+ const-wide/16 v2, -0x1
+
+ .line 156
+ sget-boolean v0, LMain;->doThrow:Z
+
+ if-eqz v0, :cond_c
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 157
+ :cond_c
+ xor-long v0, p0, v2
+
+ xor-long/2addr v2, p2
+
+ or-long/2addr v0, v2
+
+ return-wide v0
+.end method
+
+# Test transformation of Not/Not/Or into Or/And for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<Select1>>,<<Select2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: Or
+
+# Original java source:
+#
+# public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
+# if (doThrow) throw new Error();
+# return !a | !b;
+# }
+
+.method public static $opt$noinline$booleanOrToAndV2(ZZ)Z
+ .registers 5
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ const/4 v0, 0x1
+
+ const/4 v1, 0x0
+
+ .line 193
+ sget-boolean v2, LMain;->doThrow:Z
+
+ if-eqz v2, :cond_c
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 194
+ :cond_c
+ if-nez p0, :cond_13
+
+ move v2, v0
+
+ :goto_f
+ if-nez p1, :cond_15
+
+ :goto_11
+ or-int/2addr v0, v2
+
+ return v0
+
+ :cond_13
+ move v2, v1
+
+ goto :goto_f
+
+ :cond_15
+ move v0, v1
+
+ goto :goto_11
+.end method
+
+
+# Test that the transformation copes with inputs being separated from the
+# bitwise operations.
+# This is a regression test. The initial logic was inserting the new bitwise
+# operation incorrectly.
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+
+# Original java source:
+#
+# public static int $opt$noinline$regressInputsAway(int a, int b) {
+# if (doThrow) throw new Error();
+# int a1 = a + 1;
+# int not_a1 = ~a1;
+# int b1 = b + 1;
+# int not_b1 = ~b1;
+# return not_a1 | not_b1;
+# }
+
+.method public static $opt$noinline$regressInputsAwayV2(II)I
+ .registers 7
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ .line 234
+ sget-boolean v4, LMain;->doThrow:Z
+
+ if-eqz v4, :cond_a
+
+ new-instance v4, Ljava/lang/Error;
+
+ invoke-direct {v4}, Ljava/lang/Error;-><init>()V
+
+ throw v4
+
+ .line 235
+ :cond_a
+ add-int/lit8 v0, p0, 0x1
+
+ .line 236
+ .local v0, "a1":I
+ xor-int/lit8 v2, v0, -0x1
+
+ .line 237
+ .local v2, "not_a1":I
+ add-int/lit8 v1, p1, 0x1
+
+ .line 238
+ .local v1, "b1":I
+ xor-int/lit8 v3, v1, -0x1
+
+ .line 239
+ .local v3, "not_b1":I
+ or-int v4, v2, v3
+
+ return v4
+.end method
+
+
+# Test transformation of Not/Not/Xor into Xor.
+
+# See first note above.
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after)
+## CHECK-NOT: Not
+
+# Original java source:
+#
+# public static int $opt$noinline$notXorToXor(int a, int b) {
+# if (doThrow) throw new Error();
+# return ~a ^ ~b;
+# }
+
+.method public static $opt$noinline$notXorToXorV2(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ .line 266
+ sget-boolean v0, LMain;->doThrow:Z
+
+ if-eqz v0, :cond_a
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 267
+ :cond_a
+ xor-int/lit8 v0, p0, -0x1
+
+ xor-int/lit8 v1, p1, -0x1
+
+ xor-int/2addr v0, v1
+
+ return v0
+.end method
+
+
+# Test transformation of Not/Not/Xor into Xor for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Select1>>,<<Select2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: BooleanNot
+
+# Original java source:
+#
+# public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
+# if (doThrow) throw new Error();
+# return !a ^ !b;
+# }
+
+.method public static $opt$noinline$booleanNotXorToXorV2(ZZ)Z
+ .registers 5
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ const/4 v0, 0x1
+
+ const/4 v1, 0x0
+
+ .line 298
+ sget-boolean v2, LMain;->doThrow:Z
+
+ if-eqz v2, :cond_c
+
+ new-instance v0, Ljava/lang/Error;
+
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+
+ throw v0
+
+ .line 299
+ :cond_c
+ if-nez p0, :cond_13
+
+ move v2, v0
+
+ :goto_f
+ if-nez p1, :cond_15
+
+ :goto_11
+ xor-int/2addr v0, v2
+
+ return v0
+
+ :cond_13
+ move v2, v1
+
+ goto :goto_f
+
+ :cond_15
+ move v0, v1
+
+ goto :goto_11
+.end method
+
+
+# Check that no transformation is done when one Not has multiple uses.
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+
+# Original java source:
+#
+# public static int $opt$noinline$notMultipleUses(int a, int b) {
+# if (doThrow) throw new Error();
+# int tmp = ~b;
+# return (tmp & 0x1) + (~a & tmp);
+# }
+
+.method public static $opt$noinline$notMultipleUsesV2(II)I
+ .registers 5
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ .line 333
+ sget-boolean v1, LMain;->doThrow:Z
+
+ if-eqz v1, :cond_a
+
+ new-instance v1, Ljava/lang/Error;
+
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+
+ throw v1
+
+ .line 334
+ :cond_a
+ xor-int/lit8 v0, p1, -0x1
+
+ .line 335
+ .local v0, "tmp":I
+ and-int/lit8 v1, v0, 0x1
+
+ xor-int/lit8 v2, p0, -0x1
+
+ and-int/2addr v2, v0
+
+ add-int/2addr v1, v2
+
+ return v1
+.end method
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index e36a2ba..5121569 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -52,305 +52,22 @@
}
}
- /**
- * Test transformation of Not/Not/And into Or/Not.
- */
-
- // Note: before the instruction_simplifier pass, Xor's are used instead of
- // Not's (the simplification happens during the same pass).
- /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
- /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
- /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
- /// CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: Return [<<And>>]
-
- /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
- /// CHECK-DAG: Return [<<Not>>]
-
- /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK-DAG: Not
- /// CHECK-NOT: Not
-
- /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK-NOT: And
-
- public static int $opt$noinline$andToOr(int a, int b) {
- if (doThrow) throw new Error();
- return ~a & ~b;
- }
-
- /**
- * Test transformation of Not/Not/And into Or/Not for boolean negations.
- * Note that the graph before this instruction simplification pass does not
- * contain `HBooleanNot` instructions. This is because this transformation
- * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
- * same pass.
- */
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (before)
- /// CHECK-DAG: <<P1:z\d+>> ParameterValue
- /// CHECK-DAG: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
- /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
- /// CHECK-DAG: <<And:i\d+>> And [<<Select1>>,<<Select2>>]
- /// CHECK-DAG: Return [<<And>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (after)
- /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
- /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
- /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
- /// CHECK-DAG: Return [<<BooleanNot>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-DAG: BooleanNot
- /// CHECK-NOT: BooleanNot
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: And
-
- public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
- if (doThrow) throw new Error();
- return !a & !b;
- }
-
- /**
- * Test transformation of Not/Not/Or into And/Not.
- */
-
- // See note above.
- // The second Xor has its arguments reversed for no obvious reason.
- /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
- /// CHECK-DAG: <<P1:j\d+>> ParameterValue
- /// CHECK-DAG: <<P2:j\d+>> ParameterValue
- /// CHECK-DAG: <<CstM1:j\d+>> LongConstant -1
- /// CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>]
- /// CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>]
- /// CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: Return [<<Or>>]
-
- /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK-DAG: <<P1:j\d+>> ParameterValue
- /// CHECK-DAG: <<P2:j\d+>> ParameterValue
- /// CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
- /// CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
- /// CHECK-DAG: Return [<<Not>>]
-
- /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK-DAG: Not
- /// CHECK-NOT: Not
-
- /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK-NOT: Or
-
- public static long $opt$noinline$orToAnd(long a, long b) {
- if (doThrow) throw new Error();
- return ~a | ~b;
- }
-
- /**
- * Test transformation of Not/Not/Or into Or/And for boolean negations.
- * Note that the graph before this instruction simplification pass does not
- * contain `HBooleanNot` instructions. This is because this transformation
- * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
- * same pass.
- */
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (before)
- /// CHECK-DAG: <<P1:z\d+>> ParameterValue
- /// CHECK-DAG: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
- /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
- /// CHECK-DAG: <<Or:i\d+>> Or [<<Select1>>,<<Select2>>]
- /// CHECK-DAG: Return [<<Or>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (after)
- /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
- /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
- /// CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
- /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
- /// CHECK-DAG: Return [<<BooleanNot>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-DAG: BooleanNot
- /// CHECK-NOT: BooleanNot
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: Or
-
- public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
- if (doThrow) throw new Error();
- return !a | !b;
- }
-
- /**
- * Test that the transformation copes with inputs being separated from the
- * bitwise operations.
- * This is a regression test. The initial logic was inserting the new bitwise
- * operation incorrectly.
- */
-
- /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
- /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>]
- /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>]
- /// CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: Return [<<Or>>]
-
- /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
- /// CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
- /// CHECK-DAG: Return [<<Not>>]
-
- /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK-DAG: Not
- /// CHECK-NOT: Not
-
- /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
-
- public static int $opt$noinline$regressInputsAway(int a, int b) {
- if (doThrow) throw new Error();
- int a1 = a + 1;
- int not_a1 = ~a1;
- int b1 = b + 1;
- int not_b1 = ~b1;
- return not_a1 | not_b1;
- }
-
- /**
- * Test transformation of Not/Not/Xor into Xor.
- */
-
- // See first note above.
- /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
- /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
- /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
- /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: Return [<<Xor>>]
-
- /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
- /// CHECK-DAG: Return [<<Xor>>]
-
- /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Not
-
- public static int $opt$noinline$notXorToXor(int a, int b) {
- if (doThrow) throw new Error();
- return ~a ^ ~b;
- }
-
- /**
- * Test transformation of Not/Not/Xor into Xor for boolean negations.
- * Note that the graph before this instruction simplification pass does not
- * contain `HBooleanNot` instructions. This is because this transformation
- * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
- * same pass.
- */
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (before)
- /// CHECK-DAG: <<P1:z\d+>> ParameterValue
- /// CHECK-DAG: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
- /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
- /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Select1>>,<<Select2>>]
- /// CHECK-DAG: Return [<<Xor>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (after)
- /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
- /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
- /// CHECK-DAG: Return [<<Xor>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: BooleanNot
-
- public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
- if (doThrow) throw new Error();
- return !a ^ !b;
- }
-
- /**
- * Check that no transformation is done when one Not has multiple uses.
- */
-
- /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
- /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
- /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK-DAG: Return [<<Add>>]
-
- /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK-DAG: <<P1:i\d+>> ParameterValue
- /// CHECK-DAG: <<P2:i\d+>> ParameterValue
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK-DAG: Return [<<Add>>]
-
- /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
-
- public static int $opt$noinline$notMultipleUses(int a, int b) {
- if (doThrow) throw new Error();
- int tmp = ~b;
- return (tmp & 0x1) + (~a & tmp);
- }
-
- public static void main(String[] args) {
- assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff));
+ public static void main(String[] args) throws Exception {
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOrV2", int.class, 0xf, 0xff));
assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff));
- assertEquals(true, $opt$noinline$booleanAndToOr(false, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOrV2", boolean.class, false, false));
assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false));
- assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff));
+ assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAndV2", long.class, 0xfL, 0xffL));
assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL));
- assertEquals(false, $opt$noinline$booleanOrToAnd(true, true));
+ assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAndV2", boolean.class, true, true));
assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true));
- assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff));
+ assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAwayV2", int.class, 0xf, 0xff));
assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff));
- assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff));
+ assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXorV2", int.class, 0xf, 0xff));
assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff));
- assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXorV2", boolean.class, true, false));
assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false));
- assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff));
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUsesV2", int.class, 0xf, 0xff));
assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff));
}
}
diff --git a/test/952-invoke-custom/src/TestReturnValues.java b/test/952-invoke-custom/src/TestReturnValues.java
new file mode 100644
index 0000000..8450a44
--- /dev/null
+++ b/test/952-invoke-custom/src/TestReturnValues.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2018 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 annotations.BootstrapMethod;
+import annotations.CalledByIndy;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class TestReturnValues extends TestBase {
+ static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType)
+ throws Throwable {
+ MethodHandle mh = lookup.findStatic(TestReturnValues.class, name, methodType);
+ return new ConstantCallSite(mh);
+ }
+
+ //
+ // Methods that pass through a single argument.
+ // Used to check return path.
+ //
+ static byte passThrough(byte value) {
+ return value;
+ }
+
+ static char passThrough(char value) {
+ return value;
+ }
+
+ static double passThrough(double value) {
+ return value;
+ }
+
+ static float passThrough(float value) {
+ return value;
+ }
+
+ static int passThrough(int value) {
+ return value;
+ }
+
+ static Object passThrough(Object value) {
+ return value;
+ }
+
+ static Object[] passThrough(Object[] value) {
+ return value;
+ }
+
+ static long passThrough(long value) {
+ return value;
+ }
+
+ static short passThrough(short value) {
+ return value;
+ }
+
+ static void passThrough() {}
+
+ static boolean passThrough(boolean value) {
+ return value;
+ }
+
+ // byte
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = byte.class,
+ parameterTypes = {byte.class})
+ private static byte passThroughCallSite(byte value) {
+ assertNotReached();
+ return (byte) 0;
+ }
+
+ // char
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = char.class,
+ parameterTypes = {char.class})
+ private static char passThroughCallSite(char value) {
+ assertNotReached();
+ return 'Z';
+ }
+
+ // double
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = double.class,
+ parameterTypes = {double.class})
+ private static double passThroughCallSite(double value) {
+ assertNotReached();
+ return Double.NaN;
+ }
+
+ // float
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = float.class,
+ parameterTypes = {float.class})
+ private static float passThroughCallSite(float value) {
+ assertNotReached();
+ return Float.NaN;
+ }
+
+ // int
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = int.class,
+ parameterTypes = {int.class})
+ private static int passThroughCallSite(int value) {
+ assertNotReached();
+ return 0;
+ }
+
+ // long
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = long.class,
+ parameterTypes = {long.class})
+ private static long passThroughCallSite(long value) {
+ assertNotReached();
+ return Long.MIN_VALUE;
+ }
+
+ // Object
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = Object.class,
+ parameterTypes = {Object.class})
+ private static Object passThroughCallSite(Object value) {
+ assertNotReached();
+ return null;
+ }
+
+ // Object[]
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = Object[].class,
+ parameterTypes = {Object[].class})
+ private static Object[] passThroughCallSite(Object[] value) {
+ assertNotReached();
+ return null;
+ }
+
+ // short
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = short.class,
+ parameterTypes = {short.class})
+ private static short passThroughCallSite(short value) {
+ assertNotReached();
+ return (short) 0;
+ }
+
+ // void
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = void.class,
+ parameterTypes = {})
+ private static void passThroughCallSite() {
+ assertNotReached();
+ }
+
+ // boolean
+ @CalledByIndy(
+ bootstrapMethod =
+ @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"),
+ fieldOrMethodName = "passThrough",
+ returnType = boolean.class,
+ parameterTypes = {boolean.class})
+ private static boolean passThroughCallSite(boolean value) {
+ assertNotReached();
+ return false;
+ }
+
+ private static void testByteReturnValues() {
+ byte[] values = {Byte.MIN_VALUE, Byte.MAX_VALUE};
+ for (byte value : values) {
+ assertEquals(value, (byte) passThroughCallSite(value));
+ }
+ }
+
+ private static void testCharReturnValues() {
+ char[] values = {
+ Character.MIN_VALUE,
+ Character.MAX_HIGH_SURROGATE,
+ Character.MAX_LOW_SURROGATE,
+ Character.MAX_VALUE
+ };
+ for (char value : values) {
+ assertEquals(value, (char) passThroughCallSite(value));
+ }
+ }
+
+ private static void testDoubleReturnValues() {
+ double[] values = {
+ Double.MIN_VALUE,
+ Double.MIN_NORMAL,
+ Double.NaN,
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ Double.MAX_VALUE
+ };
+ for (double value : values) {
+ assertEquals(value, (double) passThroughCallSite(value));
+ }
+ }
+
+ private static void testFloatReturnValues() {
+ float[] values = {
+ Float.MIN_VALUE,
+ Float.MIN_NORMAL,
+ Float.NaN,
+ Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.MAX_VALUE
+ };
+ for (float value : values) {
+ assertEquals(value, (float) passThroughCallSite(value));
+ }
+ }
+
+ private static void testIntReturnValues() {
+ int[] values = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.SIZE, -Integer.SIZE};
+ for (int value : values) {
+ assertEquals(value, (int) passThroughCallSite(value));
+ }
+ }
+
+ private static void testLongReturnValues() {
+ long[] values = {Long.MIN_VALUE, Long.MAX_VALUE, (long) Long.SIZE, (long) -Long.SIZE};
+ for (long value : values) {
+ assertEquals(value, (long) passThroughCallSite(value));
+ }
+ }
+
+ private static void testObjectReturnValues() {
+ Object[] values = {null, "abc", Integer.valueOf(123)};
+ for (Object value : values) {
+ assertEquals(value, (Object) passThroughCallSite(value));
+ }
+
+ Object[] otherValues = (Object[]) passThroughCallSite(values);
+ assertEquals(values.length, otherValues.length);
+ for (int i = 0; i < otherValues.length; ++i) {
+ assertEquals(values[i], otherValues[i]);
+ }
+ }
+
+ private static void testShortReturnValues() {
+ short[] values = {
+ Short.MIN_VALUE, Short.MAX_VALUE, (short) Short.SIZE, (short) -Short.SIZE
+ };
+ for (short value : values) {
+ assertEquals(value, (short) passThroughCallSite(value));
+ }
+ }
+
+ private static void testVoidReturnValues() {
+ long l = Long.MIN_VALUE;
+ double d = Double.MIN_VALUE;
+ passThroughCallSite(); // Initializes call site
+ assertEquals(Long.MIN_VALUE, l);
+ assertEquals(Double.MIN_VALUE, d);
+
+ l = Long.MAX_VALUE;
+ d = Double.MAX_VALUE;
+ passThroughCallSite(); // re-uses existing call site
+ assertEquals(Long.MAX_VALUE, l);
+ assertEquals(Double.MAX_VALUE, d);
+ }
+
+ private static void testBooleanReturnValues() {
+ boolean[] values = {true, false, true, false, false};
+ for (boolean value : values) {
+ assertEquals(value, (boolean) passThroughCallSite(value));
+ }
+ }
+
+ public static void test() {
+ System.out.println(TestReturnValues.class.getName());
+ // Two passes here - the first is for the call site creation and invoke path, the second
+ // for the lookup and invoke path.
+ for (int pass = 0; pass < 2; ++pass) {
+ testByteReturnValues(); // B
+ testCharReturnValues(); // C
+ testDoubleReturnValues(); // D
+ testFloatReturnValues(); // F
+ testIntReturnValues(); // I
+ testLongReturnValues(); // J
+ testObjectReturnValues(); // L
+ testShortReturnValues(); // S
+ testVoidReturnValues(); // S
+ testBooleanReturnValues(); // Z
+ }
+ }
+}
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index 6954c22..a8b609b 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -15,6 +15,7 @@
Chatty.chatter()
Chatty.chatter()
String constructors done.
+testReturnValues done.
testReferenceReturnValueConversions done.
testPrimitiveReturnValueConversions done.
Hi
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index dee818a..11d6ead 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -102,6 +102,7 @@
testAsType();
testConstructors();
testStringConstructors();
+ testReturnValues();
testReturnValueConversions();
testVariableArity();
testVariableArity_MethodHandles_bind();
@@ -873,6 +874,89 @@
System.out.println("String constructors done.");
}
+ private static void testReturnValues() throws Throwable {
+ Lookup lookup = MethodHandles.lookup();
+
+ // byte
+ MethodHandle mhByteValue =
+ lookup.findVirtual(Byte.class, "byteValue", MethodType.methodType(byte.class));
+ assertEquals((byte) -77, (byte) mhByteValue.invokeExact(Byte.valueOf((byte) -77)));
+ assertEquals((byte) -77, (byte) mhByteValue.invoke(Byte.valueOf((byte) -77)));
+
+ // char
+ MethodHandle mhCharacterValue =
+ lookup.findStaticGetter(Character.class, "MAX_SURROGATE", char.class);
+ assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invokeExact());
+ assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invoke());
+
+ // double
+ MethodHandle mhSin =
+ lookup.findStatic(
+ Math.class, "sin", MethodType.methodType(double.class, double.class));
+ for (double i = -Math.PI; i <= Math.PI; i += Math.PI / 8) {
+ assertEquals(Math.sin(i), (double) mhSin.invokeExact(i));
+ assertEquals(Math.sin(i), (double) mhSin.invoke(i));
+ }
+
+ // float
+ MethodHandle mhAbsFloat =
+ lookup.findStatic(
+ Math.class, "abs", MethodType.methodType(float.class, float.class));
+ assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invokeExact(-3.3e6f));
+ assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invoke(-3.3e6f));
+
+ // int
+ MethodHandle mhAbsInt =
+ lookup.findStatic(Math.class, "abs", MethodType.methodType(int.class, int.class));
+ assertEquals(Math.abs(-1000), (int) mhAbsInt.invokeExact(-1000));
+ assertEquals(Math.abs(-1000), (int) mhAbsInt.invoke(-1000));
+
+ // long
+ MethodHandle mhMaxLong =
+ lookup.findStatic(
+ Math.class,
+ "max",
+ MethodType.methodType(long.class, long.class, long.class));
+ assertEquals(
+ Long.MAX_VALUE, (long) mhMaxLong.invokeExact(Long.MAX_VALUE, Long.MAX_VALUE / 2));
+ assertEquals(Long.MAX_VALUE, (long) mhMaxLong.invoke(Long.MAX_VALUE, Long.MAX_VALUE / 2));
+ assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invokeExact(0x0123456789abcdefL, 0L));
+ assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invoke(0x0123456789abcdefL, 0L));
+
+ // ref
+ MethodHandle mhShortValueOf =
+ lookup.findStatic(
+ Short.class, "valueOf", MethodType.methodType(Short.class, short.class));
+ assertEquals(
+ (short) -7890, ((Short) mhShortValueOf.invokeExact((short) -7890)).shortValue());
+ assertEquals((short) -7890, ((Short) mhShortValueOf.invoke((short) -7890)).shortValue());
+
+ // array
+ int [] array = {Integer.MIN_VALUE, -1, 0, +1, Integer.MAX_VALUE};
+ MethodHandle mhCopyOf =
+ lookup.findStatic(
+ Arrays.class, "copyOf", MethodType.methodType(int[].class, int[].class, int.class));
+ assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invokeExact(array, array.length)));
+ assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invoke(array, array.length)));
+
+ // short
+ MethodHandle mhShortValue =
+ lookup.findVirtual(Short.class, "shortValue", MethodType.methodType(short.class));
+ assertEquals((short) 12131, (short) mhShortValue.invokeExact(Short.valueOf((short) 12131)));
+ assertEquals((short) 12131, (short) mhShortValue.invoke(Short.valueOf((short) 12131)));
+
+ // boolean
+ MethodHandle mhBooleanValue =
+ lookup.findVirtual(
+ Boolean.class, "booleanValue", MethodType.methodType(boolean.class));
+ assertEquals(true, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(true)));
+ assertEquals(true, (boolean) mhBooleanValue.invoke(Boolean.valueOf(true)));
+ assertEquals(false, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(false)));
+ assertEquals(false, (boolean) mhBooleanValue.invoke(Boolean.valueOf(false)));
+
+ System.out.println("testReturnValues done.");
+ }
+
private static void testReferenceReturnValueConversions() throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(
Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
diff --git a/test/knownfailures.json b/test/knownfailures.json
index ed98d23..c680f53 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -991,5 +991,11 @@
"variant": "jit",
"bug": "b/77567088",
"description": ["Test throws exception before or during OOME."]
+ },
+ {
+ "tests": ["021-string2"],
+ "variant": "jit & debuggable",
+ "bug": "b/109791792",
+ "description": ["Stack too big."]
}
]
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 66ed0d0..1f4b829 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -140,3 +140,6 @@
# include platform prebuilt java, javac, etc in $PATH.
os.environ['PATH'] = ANDROID_JAVA_TOOLCHAIN + ':' + os.environ['PATH']
+
+DIST_DIR = _get_build_var('DIST_DIR')
+SOONG_OUT_DIR = _get_build_var('SOONG_OUT_DIR')
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 2d1398e..044e8dc 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -52,6 +52,7 @@
import multiprocessing
import os
import re
+import shutil
import subprocess
import sys
import tempfile
@@ -1007,6 +1008,9 @@
build_command += ' -C ' + env.ANDROID_BUILD_TOP
build_command += ' ' + build_targets
if subprocess.call(build_command.split()):
+ # Debugging for b/62653020
+ if env.DIST_DIR:
+ shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja')
sys.exit(1)
if user_requested_tests:
test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,))
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index ad33233..9f423ba 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -147,6 +147,7 @@
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := ahat-tests
+LOCAL_COMPATIBILITY_SUITE := general-tests
include $(BUILD_HOST_JAVA_LIBRARY)
AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE)
diff --git a/tools/ahat/AndroidTest.xml b/tools/ahat/AndroidTest.xml
new file mode 100644
index 0000000..867658c
--- /dev/null
+++ b/tools/ahat/AndroidTest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<configuration description="Runs the ahat unit tests">
+ <option name="null-device" value="true" />
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.ahat.AhatTestSuite" />
+ </test>
+</configuration>
diff --git a/tools/ahat/etc/ahat-tests.mf b/tools/ahat/etc/ahat-tests.mf
index af17fad..48fdeb3 100644
--- a/tools/ahat/etc/ahat-tests.mf
+++ b/tools/ahat/etc/ahat-tests.mf
@@ -1 +1 @@
-Main-Class: com.android.ahat.Tests
+Main-Class: com.android.ahat.AhatTestSuite
diff --git a/tools/ahat/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java
index 04a6012..af197d4 100644
--- a/tools/ahat/src/main/com/android/ahat/Main.java
+++ b/tools/ahat/src/main/com/android/ahat/Main.java
@@ -102,7 +102,7 @@
i++;
try {
map.readFromFile(new File(args[i]));
- } catch (IOException|ParseException ex) {
+ } catch (IOException | ParseException ex) {
System.out.println("Unable to read proguard map: " + ex);
System.out.println("The proguard map will not be used.");
}
@@ -110,7 +110,7 @@
i++;
try {
mapbase.readFromFile(new File(args[i]));
- } catch (IOException|ParseException ex) {
+ } catch (IOException | ParseException ex) {
System.out.println("Unable to read baseline proguard map: " + ex);
System.out.println("The proguard map will not be used.");
}
diff --git a/tools/ahat/src/test/com/android/ahat/AhatTestSuite.java b/tools/ahat/src/test/com/android/ahat/AhatTestSuite.java
new file mode 100644
index 0000000..bce1f05
--- /dev/null
+++ b/tools/ahat/src/test/com/android/ahat/AhatTestSuite.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 com.android.ahat;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ DiffFieldsTest.class,
+ DiffTest.class,
+ DominatorsTest.class,
+ HtmlEscaperTest.class,
+ InstanceTest.class,
+ NativeAllocationTest.class,
+ ObjectHandlerTest.class,
+ OverviewHandlerTest.class,
+ PerformanceTest.class,
+ ProguardMapTest.class,
+ RootedHandlerTest.class,
+ QueryTest.class,
+ SiteHandlerTest.class,
+ SiteTest.class
+})
+
+public class AhatTestSuite {
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ args = new String[]{"com.android.ahat.AhatTestSuite"};
+ }
+ JUnitCore.main(args);
+ }
+}
diff --git a/tools/ahat/src/test/com/android/ahat/Tests.java b/tools/ahat/src/test/com/android/ahat/Tests.java
deleted file mode 100644
index 0e70432..0000000
--- a/tools/ahat/src/test/com/android/ahat/Tests.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ahat;
-
-import org.junit.runner.JUnitCore;
-
-public class Tests {
- public static void main(String[] args) {
- if (args.length == 0) {
- args = new String[]{
- "com.android.ahat.DiffFieldsTest",
- "com.android.ahat.DiffTest",
- "com.android.ahat.DominatorsTest",
- "com.android.ahat.HtmlEscaperTest",
- "com.android.ahat.InstanceTest",
- "com.android.ahat.NativeAllocationTest",
- "com.android.ahat.ObjectHandlerTest",
- "com.android.ahat.OverviewHandlerTest",
- "com.android.ahat.PerformanceTest",
- "com.android.ahat.ProguardMapTest",
- "com.android.ahat.RootedHandlerTest",
- "com.android.ahat.QueryTest",
- "com.android.ahat.SiteHandlerTest",
- "com.android.ahat.SiteTest",
- };
- }
- JUnitCore.main(args);
- }
-}
-
diff --git a/tools/build/var_list b/tools/build/var_list
index bb005cf..98a5472 100644
--- a/tools/build/var_list
+++ b/tools/build/var_list
@@ -34,3 +34,6 @@
HOST_OUT_EXECUTABLES
ANDROID_JAVA_TOOLCHAIN
+# b/62653020
+DIST_DIR
+SOONG_OUT_DIR
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
index f8463c1..2faa577 100644
--- a/tools/veridex/Android.mk
+++ b/tools/veridex/Android.mk
@@ -22,13 +22,13 @@
system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX)
- $(transform-classes-d8.jar-to-dex)
+ $(transform-classes.jar-to-dex)
oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
- $(transform-classes-d8.jar-to-dex)
+ $(transform-classes.jar-to-dex)
app_compat_lists := \
$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \