diff options
Diffstat (limited to 'compiler/optimizing')
| -rw-r--r-- | compiler/optimizing/builder.cc | 477 | ||||
| -rw-r--r-- | compiler/optimizing/builder.h | 13 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 9 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_arm.cc | 99 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/reference_type_propagation.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/register_allocator.cc | 18 |
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; } |