summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc477
-rw-r--r--compiler/optimizing/builder.h13
-rw-r--r--compiler/optimizing/code_generator_arm64.cc9
-rw-r--r--compiler/optimizing/intrinsics_arm.cc99
-rw-r--r--compiler/optimizing/nodes.h2
-rw-r--r--compiler/optimizing/reference_type_propagation.cc2
-rw-r--r--compiler/optimizing/register_allocator.cc18
7 files changed, 399 insertions, 221 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index a8f6a24908..7b42db8a7f 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -759,33 +759,214 @@ void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type
current_block_ = nullptr;
}
-void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register,
- uint32_t dex_pc,
- HInvoke* actual_string) {
- if (!graph_->IsDebuggable()) {
- // Notify that we cannot compile with baseline. The dex registers aliasing
- // with `original_dex_register` will be handled when we optimize
- // (see HInstructionSimplifer::VisitFakeString).
- can_use_baseline_for_string_init_ = false;
- return;
+static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
+ switch (opcode) {
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ return kStatic;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ return kDirect;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ return kVirtual;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return kInterface;
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_SUPER:
+ return kSuper;
+ default:
+ LOG(FATAL) << "Unexpected invoke opcode: " << opcode;
+ UNREACHABLE();
}
- const VerifiedMethod* verified_method =
- compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex());
- if (verified_method != nullptr) {
- UpdateLocal(original_dex_register, actual_string);
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
- verified_method->GetStringInitPcRegMap();
- auto map_it = string_init_map.find(dex_pc);
- if (map_it != string_init_map.end()) {
- std::set<uint32_t> reg_set = map_it->second;
- for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
- HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot);
- UpdateLocal(*set_it, load_local);
- }
+}
+
+bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index) {
+ InvokeType original_invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
+ InvokeType optimized_invoke_type = original_invoke_type;
+ const char* descriptor = dex_file_->GetMethodShorty(method_idx);
+ Primitive::Type return_type = Primitive::GetType(descriptor[0]);
+
+ // Remove the return type from the 'proto'.
+ size_t number_of_arguments = strlen(descriptor) - 1;
+ if (original_invoke_type != kStatic) { // instance call
+ // One extra argument for 'this'.
+ number_of_arguments++;
+ }
+
+ MethodReference target_method(dex_file_, method_idx);
+ int32_t table_index;
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+
+ if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_,
+ dex_pc,
+ true /* update_stats */,
+ true /* enable_devirtualization */,
+ &optimized_invoke_type,
+ &target_method,
+ &table_index,
+ &direct_code,
+ &direct_method)) {
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because a method call could not be resolved";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
+ return false;
+ }
+
+ DCHECK(optimized_invoke_type != kSuper);
+
+ // Special handling for string init.
+ int32_t string_init_offset = 0;
+ bool is_string_init = compiler_driver_->IsStringInit(method_idx, dex_file_,
+ &string_init_offset);
+
+ // Potential class initialization check, in the case of a static method call.
+ HClinitCheck* clinit_check = nullptr;
+ HInvoke* invoke = nullptr;
+
+ if (is_string_init
+ || optimized_invoke_type == kDirect
+ || optimized_invoke_type == kStatic) {
+ // By default, consider that the called method implicitly requires
+ // an initialization check of its declaring method.
+ HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement
+ = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
+ if (optimized_invoke_type == kStatic && !is_string_init) {
+ clinit_check = ProcessClinitCheckForInvoke(dex_pc, method_idx, &clinit_check_requirement);
+ }
+
+ // Replace calls to String.<init> with StringFactory.
+ if (is_string_init) {
+ return_type = Primitive::kPrimNot;
+ number_of_arguments--;
+ optimized_invoke_type = kStatic;
}
+
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init,
+ string_init_offset,
+ target_method,
+ direct_method,
+ direct_code);
+ invoke = new (arena_) HInvokeStaticOrDirect(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ target_method,
+ dispatch_info,
+ original_invoke_type,
+ optimized_invoke_type,
+ clinit_check_requirement);
+ } else if (optimized_invoke_type == kVirtual) {
+ invoke = new (arena_) HInvokeVirtual(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ table_index);
} else {
- can_use_baseline_for_string_init_ = false;
+ DCHECK_EQ(optimized_invoke_type, kInterface);
+ invoke = new (arena_) HInvokeInterface(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ table_index);
+ }
+
+ return SetupArgumentsAndAddInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ clinit_check);
+}
+
+HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ ArtMethod* resolved_method = compiler_driver_->ResolveMethod(
+ soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, InvokeType::kStatic);
+
+ DCHECK(resolved_method != nullptr);
+
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // The index at which the method's class is stored in the DexCache's type array.
+ uint32_t storage_index = DexFile::kDexNoIndex;
+ bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
+ if (is_outer_class) {
+ storage_index = outer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() == dex_cache.Get()) {
+ // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
+ compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
+ GetCompilingClass(),
+ resolved_method,
+ method_idx,
+ &storage_index);
+ }
+
+ HClinitCheck* clinit_check = nullptr;
+
+ if (!outer_class->IsInterface()
+ && outer_class->IsSubClass(resolved_method->GetDeclaringClass())) {
+ // If the outer class is the declaring class or a subclass
+ // of the declaring class, no class initialization is needed
+ // before the static method call.
+ // Note that in case of inlining, we do not need to add clinit checks
+ // to calls that satisfy this subclass check with any inlined methods. This
+ // will be detected by the optimization passes.
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else if (storage_index != DexFile::kDexNoIndex) {
+ // If the method's class type index is available, check
+ // whether we should add an explicit class initialization
+ // check for its declaring class before the static method call.
+
+ // TODO: find out why this check is needed.
+ bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+ *outer_compilation_unit_->GetDexFile(), storage_index);
+ bool is_initialized =
+ resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
+
+ if (is_initialized) {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ storage_index,
+ *dex_compilation_unit_->GetDexFile(),
+ is_outer_class,
+ dex_pc);
+ current_block_->AddInstruction(load_class);
+ clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
+ current_block_->AddInstruction(clinit_check);
+ }
}
+ return clinit_check;
}
HInvokeStaticOrDirect::DispatchInfo HGraphBuilder::ComputeDispatchInfo(
@@ -859,210 +1040,40 @@ HInvokeStaticOrDirect::DispatchInfo HGraphBuilder::ComputeDispatchInfo(
method_load_kind, code_ptr_location, method_load_data, direct_code_ptr };
}
-bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
- uint32_t dex_pc,
- uint32_t method_idx,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index) {
- Instruction::Code opcode = instruction.Opcode();
- InvokeType invoke_type;
- switch (opcode) {
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE:
- invoke_type = kStatic;
- break;
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- invoke_type = kDirect;
- break;
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_QUICK:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
- invoke_type = kVirtual;
- break;
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- invoke_type = kInterface;
- break;
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_SUPER:
- invoke_type = kSuper;
- break;
- default:
- LOG(FATAL) << "Unexpected invoke op: " << opcode;
- return false;
- }
-
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_);
- const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_);
- Primitive::Type return_type = Primitive::GetType(descriptor[0]);
- bool is_instance_call = invoke_type != kStatic;
- // Remove the return type from the 'proto'.
- size_t number_of_arguments = strlen(descriptor) - 1;
- if (is_instance_call) {
- // One extra argument for 'this'.
- ++number_of_arguments;
- }
+bool HGraphBuilder::SetupArgumentsAndAddInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check) {
+ size_t start_index = 0;
+ size_t argument_index = 0;
+ uint32_t descriptor_index = 1; // Skip the return type.
- MethodReference target_method(dex_file_, method_idx);
- uintptr_t direct_code;
- uintptr_t direct_method;
- int table_index;
- InvokeType optimized_invoke_type = invoke_type;
+ bool is_instance_call = invoke->GetOriginalInvokeType() != InvokeType::kStatic;
+ bool is_string_init = invoke->IsInvokeStaticOrDirect()
+ && invoke->AsInvokeStaticOrDirect()->IsStringInit();
- if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, true, true,
- &optimized_invoke_type, &target_method, &table_index,
- &direct_code, &direct_method)) {
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because a method call could not be resolved";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
- return false;
- }
- DCHECK(optimized_invoke_type != kSuper);
-
- // By default, consider that the called method implicitly requires
- // an initialization check of its declaring method.
- HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement =
- HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
- // Potential class initialization check, in the case of a static method call.
- HClinitCheck* clinit_check = nullptr;
- // Replace calls to String.<init> with StringFactory.
- int32_t string_init_offset = 0;
- bool is_string_init = compiler_driver_->IsStringInit(method_idx, dex_file_, &string_init_offset);
if (is_string_init) {
- return_type = Primitive::kPrimNot;
- is_instance_call = false;
- number_of_arguments--;
- invoke_type = kStatic;
- optimized_invoke_type = kStatic;
- }
-
- HInvoke* invoke = nullptr;
-
- if (optimized_invoke_type == kVirtual) {
- invoke = new (arena_) HInvokeVirtual(
- arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
- } else if (optimized_invoke_type == kInterface) {
- invoke = new (arena_) HInvokeInterface(
- arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
- } else {
- DCHECK(optimized_invoke_type == kDirect || optimized_invoke_type == kStatic);
-
- if (optimized_invoke_type == kStatic && !is_string_init) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- ArtMethod* resolved_method = compiler_driver_->ResolveMethod(
- soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, optimized_invoke_type);
-
- if (resolved_method == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
- return false;
- }
-
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
-
- // The index at which the method's class is stored in the DexCache's type array.
- uint32_t storage_index = DexFile::kDexNoIndex;
- bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
- if (is_outer_class) {
- storage_index = outer_class->GetDexTypeIndex();
- } else if (outer_dex_cache.Get() == dex_cache.Get()) {
- // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
- compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
- GetCompilingClass(),
- resolved_method,
- method_idx,
- &storage_index);
- }
-
- if (!outer_class->IsInterface()
- && outer_class->IsSubClass(resolved_method->GetDeclaringClass())) {
- // If the outer class is the declaring class or a subclass
- // of the declaring class, no class initialization is needed
- // before the static method call.
- // Note that in case of inlining, we do not need to add clinit checks
- // to calls that satisfy this subclass check with any inlined methods. This
- // will be detected by the optimization passes.
- clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
- } else if (storage_index != DexFile::kDexNoIndex) {
- // If the method's class type index is available, check
- // whether we should add an explicit class initialization
- // check for its declaring class before the static method call.
-
- // TODO: find out why this check is needed.
- bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
- *outer_compilation_unit_->GetDexFile(), storage_index);
- bool is_initialized =
- resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
-
- if (is_initialized) {
- clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
- } else {
- clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
- HLoadClass* load_class = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- storage_index,
- *dex_compilation_unit_->GetDexFile(),
- is_outer_class,
- dex_pc);
- current_block_->AddInstruction(load_class);
- clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
- current_block_->AddInstruction(clinit_check);
- }
- }
- }
-
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init,
- string_init_offset,
- target_method,
- direct_method,
- direct_code);
- invoke = new (arena_) HInvokeStaticOrDirect(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- target_method,
- dispatch_info,
- invoke_type,
- optimized_invoke_type,
- clinit_check_requirement);
- }
-
- size_t start_index = 0;
- Temporaries temps(graph_);
- if (is_instance_call) {
+ start_index = 1;
+ argument_index = 0;
+ } else if (is_instance_call) {
+ Temporaries temps(graph_);
HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
- HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_pc);
+ HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
current_block_->AddInstruction(null_check);
temps.Add(null_check);
invoke->SetArgumentAt(0, null_check);
start_index = 1;
+ argument_index = 1;
}
- uint32_t descriptor_index = 1; // Skip the return type.
- uint32_t argument_index = start_index;
- if (is_string_init) {
- start_index = 1;
- }
for (size_t i = start_index;
// Make sure we don't go over the expected arguments or over the number of
// dex registers given. If the instruction was seen as dead by the verifier,
// it hasn't been properly checked.
- (i < number_of_vreg_arguments) && (argument_index < number_of_arguments);
+ (i < number_of_vreg_arguments) && (argument_index < invoke->GetNumberOfArguments());
i++, argument_index++) {
Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
@@ -1085,7 +1096,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
}
}
- if (argument_index != number_of_arguments) {
+ if (argument_index != invoke->GetNumberOfArguments()) {
VLOG(compiler) << "Did not compile "
<< PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
<< " because of wrong number of arguments in invoke instruction";
@@ -1098,10 +1109,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
argument_index++;
}
- if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) {
+ if (clinit_check != nullptr) {
// Add the class initialization check as last input of `invoke`.
- DCHECK(clinit_check != nullptr);
DCHECK(!is_string_init);
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement()
+ == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit);
invoke->SetArgumentAt(argument_index, clinit_check);
argument_index++;
}
@@ -1112,15 +1125,45 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot);
invoke->SetArgumentAt(argument_index, fake_string);
current_block_->AddInstruction(invoke);
- PotentiallySimplifyFakeString(orig_this_reg, dex_pc, invoke);
+ PotentiallySimplifyFakeString(orig_this_reg, invoke->GetDexPc(), invoke);
} else {
current_block_->AddInstruction(invoke);
}
+
latest_result_ = invoke;
return true;
}
+void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register,
+ uint32_t dex_pc,
+ HInvoke* actual_string) {
+ if (!graph_->IsDebuggable()) {
+ // Notify that we cannot compile with baseline. The dex registers aliasing
+ // with `original_dex_register` will be handled when we optimize
+ // (see HInstructionSimplifer::VisitFakeString).
+ can_use_baseline_for_string_init_ = false;
+ return;
+ }
+ const VerifiedMethod* verified_method =
+ compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex());
+ if (verified_method != nullptr) {
+ UpdateLocal(original_dex_register, actual_string);
+ const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
+ verified_method->GetStringInitPcRegMap();
+ auto map_it = string_init_map.find(dex_pc);
+ if (map_it != string_init_map.end()) {
+ std::set<uint32_t> reg_set = map_it->second;
+ for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
+ HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot);
+ UpdateLocal(*set_it, load_local);
+ }
+ }
+ } else {
+ can_use_baseline_for_string_init_ = false;
+ }
+}
+
bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 08600c756d..19dd94475a 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -272,6 +272,19 @@ class HGraphBuilder : public ValueObject {
uintptr_t direct_method,
uintptr_t direct_code);
+ bool SetupArgumentsAndAddInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check);
+
+ HClinitCheck* ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement);
+
ArenaAllocator* const arena_;
// A list of the size of the dex code holding block information for
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 41523557c3..25b3ea2f5f 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3202,6 +3202,15 @@ void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* convers
Register source = InputRegisterAt(conversion, 0);
if ((result_type == Primitive::kPrimChar) && (input_size < result_size)) {
__ Ubfx(output, source, 0, result_size * kBitsPerByte);
+ } else if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) {
+ // 'int' values are used directly as W registers, discarding the top
+ // bits, so we don't need to sign-extend and can just perform a move.
+ // We do not pass the `kDiscardForSameWReg` argument to force clearing the
+ // top 32 bits of the target register. We theoretically could leave those
+ // bits unchanged, but we would have to make sure that no code uses a
+ // 32bit input value as a 64bit value assuming that the top 32 bits are
+ // zero.
+ __ Mov(output.W(), source.W());
} else if ((result_type == Primitive::kPrimChar) ||
((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
__ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 1527a6aa23..806fd7a8f4 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -919,6 +919,104 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
__ Bind(slow_path->GetExitLabel());
}
+void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Temporary registers to store lengths of strings and for calculations.
+ // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
+ locations->AddTemp(Location::RegisterLocation(R0));
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register str = locations->InAt(0).AsRegister<Register>();
+ Register arg = locations->InAt(1).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register temp1 = locations->GetTemp(1).AsRegister<Register>();
+ Register temp2 = locations->GetTemp(2).AsRegister<Register>();
+
+ Label loop;
+ Label end;
+ Label return_true;
+ Label return_false;
+
+ // Get offsets of count, value, and class fields within a string object.
+ const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+ const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
+ const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
+
+ // Note that the null check must have been done earlier.
+ DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
+
+ // Check if input is null, return false if it is.
+ __ CompareAndBranchIfZero(arg, &return_false);
+
+ // Instanceof check for the argument by comparing class fields.
+ // All string objects must have the same type since String cannot be subclassed.
+ // Receiver must be a string object, so its class field is equal to all strings' class fields.
+ // If the argument is a string object, its class field must be equal to receiver's class field.
+ __ ldr(temp, Address(str, class_offset));
+ __ ldr(temp1, Address(arg, class_offset));
+ __ cmp(temp, ShifterOperand(temp1));
+ __ b(&return_false, NE);
+
+ // Load lengths of this and argument strings.
+ __ ldr(temp, Address(str, count_offset));
+ __ ldr(temp1, Address(arg, count_offset));
+ // Check if lengths are equal, return false if they're not.
+ __ cmp(temp, ShifterOperand(temp1));
+ __ b(&return_false, NE);
+ // Return true if both strings are empty.
+ __ cbz(temp, &return_true);
+
+ // Reference equality check, return true if same reference.
+ __ cmp(str, ShifterOperand(arg));
+ __ b(&return_true, EQ);
+
+ // Assertions that must hold in order to compare strings 2 characters at a time.
+ DCHECK_ALIGNED(value_offset, 4);
+ static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
+
+ // temp cannot overflow because we cannot allocate a String object with size 4GiB or greater.
+ __ add(temp, temp, ShifterOperand(temp));
+ __ LoadImmediate(temp1, value_offset);
+ __ add(temp, temp, ShifterOperand(value_offset));
+
+ // Loop to compare strings 2 characters at a time starting at the front of the string.
+ // Ok to do this because strings with an odd length are zero-padded.
+ __ Bind(&loop);
+ __ ldr(out, Address(str, temp1));
+ __ ldr(temp2, Address(arg, temp1));
+ __ cmp(out, ShifterOperand(temp2));
+ __ b(&return_false, NE);
+ __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
+ __ cmp(temp1, ShifterOperand(temp));
+ __ b(&loop, LO);
+
+ // Return true and exit the function.
+ // If loop does not result in returning false, we return true.
+ __ Bind(&return_true);
+ __ LoadImmediate(out, 1);
+ __ b(&end);
+
+ // Return false and exit the function.
+ __ Bind(&return_false);
+ __ LoadImmediate(out, 0);
+ __ Bind(&end);
+}
+
static void GenerateVisitStringIndexOf(HInvoke* invoke,
ArmAssembler* assembler,
CodeGeneratorARM* codegen,
@@ -1110,7 +1208,6 @@ UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
-UNIMPLEMENTED_INTRINSIC(StringEquals)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f2db33086c..2ed2d9ab20 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3181,6 +3181,8 @@ class HInvokeStaticOrDirect : public HInvoke {
return dispatch_info_.direct_code_ptr;
}
+ ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; }
+
// Is this instruction a call to a static method?
bool IsStatic() const {
return GetInvokeType() == kStatic;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 45b3df008b..97b9972001 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -475,7 +475,7 @@ void RTPVisitor::VisitLoadException(HLoadException* instr) {
if (catch_info->IsCatchAllTypeIndex()) {
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(throwable_class_handle_,
- /* is_exact */ false));
+ /* is_exact */ false));
} else {
UpdateReferenceTypeInfo(instr,
catch_info->GetCatchTypeIndex(),
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 9f32a9eaf8..37c8bc5f51 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -587,9 +587,17 @@ void RegisterAllocator::LinearScan() {
while (!unhandled_->IsEmpty()) {
// (1) Remove interval with the lowest start position from unhandled.
LiveInterval* current = unhandled_->Pop();
+
+ // Make sure the interval is an expected state.
DCHECK(!current->IsFixed() && !current->HasSpillSlot());
+ // Make sure we are going in the right order.
DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart());
+ // Make sure a low interval is always with a high.
DCHECK(!current->IsLowInterval() || unhandled_->Peek()->IsHighInterval());
+ // Make sure a high interval is always with a low.
+ DCHECK(current->IsLowInterval() ||
+ unhandled_->IsEmpty() ||
+ !unhandled_->Peek()->IsHighInterval());
size_t position = current->GetStart();
@@ -916,13 +924,19 @@ bool RegisterAllocator::TrySplitNonPairOrUnalignedPairIntervalAt(size_t position
if (active->IsHighInterval()) continue;
if (first_register_use > next_use[active->GetRegister()]) continue;
- // Split the first interval found.
- if (!active->IsLowInterval() || IsLowOfUnalignedPairInterval(active)) {
+ // Split the first interval found that is either:
+ // 1) A non-pair interval.
+ // 2) A pair interval whose high is not low + 1.
+ // 3) A pair interval whose low is not even.
+ if (!active->IsLowInterval() ||
+ IsLowOfUnalignedPairInterval(active) ||
+ !IsLowRegister(active->GetRegister())) {
LiveInterval* split = Split(active, position);
active_.DeleteAt(i);
if (split != active) {
handled_.Add(active);
}
+ PotentiallyRemoveOtherHalf(active, &active_, i);
AddSorted(unhandled_, split);
return true;
}