diff options
Diffstat (limited to 'compiler/optimizing')
22 files changed, 727 insertions, 251 deletions
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 9a9215135a..8100a29f32 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -18,6 +18,26 @@ namespace art { +void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) { + DCHECK(block->EndsWithIf()); + + // Check if the condition is a Boolean negation. + HIf* if_instruction = block->GetLastInstruction()->AsIf(); + HInstruction* boolean_not = if_instruction->InputAt(0); + if (!boolean_not->IsBooleanNot()) { + return; + } + + // Make BooleanNot's input the condition of the If and swap branches. + if_instruction->ReplaceInput(boolean_not->InputAt(0), 0); + block->SwapSuccessors(); + + // Remove the BooleanNot if it is now unused. + if (!boolean_not->HasUses()) { + boolean_not->GetBlock()->RemoveInstruction(boolean_not); + } +} + // Returns true if 'block1' and 'block2' are empty, merge into the same single // successor and the successor can only be reached from them. static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) { @@ -78,58 +98,69 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { } } +void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) { + DCHECK(block->EndsWithIf()); + + // Find elements of the pattern. + HIf* if_instruction = block->GetLastInstruction()->AsIf(); + HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); + HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); + if (!BlocksDoMergeTogether(true_block, false_block)) { + return; + } + HBasicBlock* merge_block = true_block->GetSuccessors().Get(0); + if (!merge_block->HasSinglePhi()) { + return; + } + HPhi* phi = merge_block->GetFirstPhi()->AsPhi(); + HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block)); + HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block)); + + // Check if the selection negates/preserves the value of the condition and + // if so, generate a suitable replacement instruction. + HInstruction* if_condition = if_instruction->InputAt(0); + HInstruction* replacement; + if (NegatesCondition(true_value, false_value)) { + replacement = GetOppositeCondition(if_condition); + if (replacement->GetBlock() == nullptr) { + block->InsertInstructionBefore(replacement, if_instruction); + } + } else if (PreservesCondition(true_value, false_value)) { + replacement = if_condition; + } else { + return; + } + + // Replace the selection outcome with the new instruction. + phi->ReplaceWith(replacement); + merge_block->RemovePhi(phi); + + // Delete the true branch and merge the resulting chain of blocks + // 'block->false_block->merge_block' into one. + true_block->DisconnectAndDelete(); + block->MergeWith(false_block); + block->MergeWith(merge_block); + + // Remove the original condition if it is now unused. + if (!if_condition->HasUses()) { + if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition); + } +} + void HBooleanSimplifier::Run() { // Iterate in post order in the unlikely case that removing one occurrence of - // the pattern empties a branch block of another occurrence. Otherwise the - // order does not matter. + // the selection pattern empties a branch block of another occurrence. + // Otherwise the order does not matter. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); if (!block->EndsWithIf()) continue; - // Find elements of the pattern. - HIf* if_instruction = block->GetLastInstruction()->AsIf(); - HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); - HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); - if (!BlocksDoMergeTogether(true_block, false_block)) { - continue; - } - HBasicBlock* merge_block = true_block->GetSuccessors().Get(0); - if (!merge_block->HasSinglePhi()) { - continue; - } - HPhi* phi = merge_block->GetFirstPhi()->AsPhi(); - HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block)); - HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block)); - - // Check if the selection negates/preserves the value of the condition and - // if so, generate a suitable replacement instruction. - HInstruction* if_condition = if_instruction->InputAt(0); - HInstruction* replacement; - if (NegatesCondition(true_value, false_value)) { - replacement = GetOppositeCondition(if_condition); - if (replacement->GetBlock() == nullptr) { - block->InsertInstructionBefore(replacement, if_instruction); - } - } else if (PreservesCondition(true_value, false_value)) { - replacement = if_condition; - } else { - continue; - } + // If condition is negated, remove the negation and swap the branches. + TryRemovingNegatedCondition(block); - // Replace the selection outcome with the new instruction. - phi->ReplaceWith(replacement); - merge_block->RemovePhi(phi); - - // Delete the true branch and merge the resulting chain of blocks - // 'block->false_block->merge_block' into one. - true_block->DisconnectAndDelete(); - block->MergeWith(false_block); - block->MergeWith(merge_block); - - // Remove the original condition if it is now unused. - if (!if_condition->HasUses()) { - if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition); - } + // If this is a boolean-selection diamond pattern, replace its result with + // the condition value (or its negation) and simplify the graph. + TryRemovingBooleanSelection(block); } } diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h index a88733e1af..733ebaac2c 100644 --- a/compiler/optimizing/boolean_simplifier.h +++ b/compiler/optimizing/boolean_simplifier.h @@ -14,11 +14,15 @@ * limitations under the License. */ -// This optimization recognizes a common pattern where a boolean value is -// either cast to an integer or negated by selecting from zero/one integer -// constants with an If statement. Because boolean values are internally -// represented as zero/one, we can safely replace the pattern with a suitable -// condition instruction. +// This optimization recognizes two common patterns: +// (a) Boolean selection: Casting a boolean to an integer or negating it is +// carried out with an If statement selecting from zero/one integer +// constants. Because Boolean values are represented as zero/one, the +// pattern can be replaced with the condition instruction itself or its +// negation, depending on the layout. +// (b) Negated condition: Instruction simplifier may replace an If's condition +// with a boolean value. If this value is the result of a Boolean negation, +// the true/false branches can be swapped and negation removed. // Example: Negating a boolean value // B1: @@ -66,6 +70,9 @@ class HBooleanSimplifier : public HOptimization { static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier"; private: + void TryRemovingNegatedCondition(HBasicBlock* block); + void TryRemovingBooleanSelection(HBasicBlock* block); + DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier); }; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 96e08fd24c..0f44af07b8 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -19,6 +19,7 @@ #include "art_field-inl.h" #include "base/logging.h" #include "class_linker.h" +#include "dex/verified_method.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex/verified_method.h" @@ -612,6 +613,16 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, 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; @@ -691,14 +702,14 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, current_block_->AddInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); current_block_->AddInstruction(clinit_check); - ++number_of_arguments; } } } invoke = new (arena_) HInvokeStaticOrDirect( arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index, - is_recursive, invoke_type, optimized_invoke_type, clinit_check_requirement); + is_recursive, string_init_offset, invoke_type, optimized_invoke_type, + clinit_check_requirement); } size_t start_index = 0; @@ -714,6 +725,9 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, uint32_t descriptor_index = 1; uint32_t argument_index = start_index; + if (is_string_init) { + start_index = 1; + } for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) { Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]); bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble); @@ -730,16 +744,38 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, i++; } } + DCHECK_EQ(argument_index, number_of_arguments); if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) { // Add the class initialization check as last input of `invoke`. DCHECK(clinit_check != nullptr); - invoke->SetArgumentAt(argument_index++, clinit_check); + invoke->SetArgumentAt(argument_index, clinit_check); } - DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); latest_result_ = invoke; + + // Add move-result for StringFactory method. + if (is_string_init) { + uint32_t orig_this_reg = is_range ? register_index : args[0]; + const VerifiedMethod* verified_method = + compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex()); + if (verified_method == nullptr) { + LOG(WARNING) << "No verified method for method calling String.<init>: " + << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_); + return false; + } + 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) { + UpdateLocal(*set_it, invoke); + } + } + UpdateLocal(orig_this_reg, invoke); + } return true; } @@ -1916,12 +1952,19 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::NEW_INSTANCE: { uint16_t type_index = instruction.VRegB_21c(); - QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index) - ? kQuickAllocObjectWithAccessCheck - : kQuickAllocObject; - - current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint)); - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + if (compiler_driver_->IsStringTypeIndex(type_index, dex_file_)) { + // Turn new-instance of string into a const 0. + int32_t register_index = instruction.VRegA(); + HNullConstant* constant = graph_->GetNullConstant(); + UpdateLocal(register_index, constant); + } else { + QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index) + ? kQuickAllocObjectWithAccessCheck + : kQuickAllocObject; + + current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + } break; } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 5163395cac..cfe121e0ec 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -153,6 +153,7 @@ HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const { } void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) { + is_baseline_ = is_baseline; HGraphVisitor* instruction_visitor = GetInstructionVisitor(); DCHECK_EQ(current_block_index_, 0u); GenerateFrameEntry(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index e536b2d0ee..6342f91684 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -212,6 +212,10 @@ class CodeGenerator { std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const; void BuildStackMaps(std::vector<uint8_t>* vector); + bool IsBaseline() const { + return is_baseline_; + } + bool IsLeafMethod() const { return is_leaf_; } @@ -325,6 +329,7 @@ class CodeGenerator { number_of_register_pairs_(number_of_register_pairs), core_callee_save_mask_(core_callee_save_mask), fpu_callee_save_mask_(fpu_callee_save_mask), + is_baseline_(false), graph_(graph), compiler_options_(compiler_options), pc_infos_(graph->GetArena(), 32), @@ -404,6 +409,9 @@ class CodeGenerator { const uint32_t core_callee_save_mask_; const uint32_t fpu_callee_save_mask_; + // Whether we are using baseline. + bool is_baseline_; + private: void InitLocationsBaseline(HInstruction* instruction); size_t GetStackOffsetOfSavedRegister(size_t index); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 01748a9f5c..159bd30e45 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1241,13 +1241,9 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { } void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), codegen_->GetInstructionSetFeatures()); @@ -1273,9 +1269,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) } void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1293,7 +1289,7 @@ void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(R0)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -4071,15 +4067,9 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ LoadFromOffset( - kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); - // temp = temp[index_in_cache] - __ LoadFromOffset( - kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())); + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ LoadFromOffset(kLoadWord, temp, TR, invoke->GetStringInitOffset()); // LR = temp[offset_of_quick_compiled_code] __ LoadFromOffset(kLoadWord, LR, temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( @@ -4087,7 +4077,24 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // LR() __ blx(LR); } else { - __ bl(GetFrameEntryLabel()); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ LoadFromOffset( + kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); + // temp = temp[index_in_cache] + __ LoadFromOffset( + kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())); + // LR = temp[offset_of_quick_compiled_code] + __ LoadFromOffset(kLoadWord, LR, temp, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmWordSize).Int32Value()); + // LR() + __ blx(LR); + } else { + __ bl(GetFrameEntryLabel()); + } } DCHECK(!IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index dada4ce5bd..946ffc8ea8 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1908,7 +1908,7 @@ void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { locations->AddTemp(LocationFrom(x0)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -1968,13 +1968,9 @@ void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena()); if (intrinsic.TryDispatch(invoke)) { @@ -2006,29 +2002,39 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset())); - // temp = temp[index_in_cache]; - __ Ldr(temp, HeapOperand(temp, index_in_cache)); - // lr = temp->entry_point_from_quick_compiled_code_; + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ Ldr(temp, HeapOperand(tr, invoke->GetStringInitOffset())); + // LR = temp->entry_point_from_quick_compiled_code_; __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kArm64WordSize))); - // lr(); + // lr() __ Blr(lr); } else { - __ Bl(&frame_entry_label_); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset())); + // temp = temp[index_in_cache]; + __ Ldr(temp, HeapOperand(temp, index_in_cache)); + // lr = temp->entry_point_from_quick_compiled_code_; + __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64WordSize))); + // lr(); + __ Blr(lr); + } else { + __ Bl(&frame_entry_label_); + } } DCHECK(!IsLeafMethod()); } void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 04999bedb0..7df4b53cbe 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1194,13 +1194,9 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderX86 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -1220,9 +1216,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86* codegen) } void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1243,7 +1239,7 @@ void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(EAX)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -3114,18 +3110,27 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // 3) app -> app // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ fs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset())); // (temp + offset_of_quick_compiled_code)() __ call(Address( temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); } else { - __ call(GetFrameEntryLabel()); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); + } else { + __ call(GetFrameEntryLabel()); + } } DCHECK(!IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5ce932928b..37b00c8d52 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -366,18 +366,26 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ gs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset())); // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kX86_64WordSize).SizeValue())); } else { - __ call(&frame_entry_label_); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86_64WordSize).SizeValue())); + } else { + __ call(&frame_entry_label_); + } } DCHECK(!IsLeafMethod()); @@ -1289,13 +1297,9 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type } void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderX86_64 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -1315,9 +1319,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86_64* codeg } void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1335,7 +1339,7 @@ void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(RDI)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 6cdc82262c..e3fd5d7ec1 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -37,7 +37,7 @@ static constexpr FloatRegister kParameterFloatRegisters[] = static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); static constexpr size_t kParameterFloatRegistersLength = arraysize(kParameterFloatRegisters); -static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; +static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX, RCX }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 }; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2df7c166d8..e79d4f4bdc 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -377,15 +377,42 @@ void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) { return; } - if ((input_cst != nullptr) && input_cst->IsMinusOne() && - (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) { + if ((input_cst != nullptr) && input_cst->IsMinusOne()) { // Replace code looking like // DIV dst, src, -1 // with // NEG dst, src instruction->GetBlock()->ReplaceAndRemoveInstructionWith( - instruction, (new (GetGraph()->GetArena()) HNeg(type, input_other))); + instruction, new (GetGraph()->GetArena()) HNeg(type, input_other)); RecordSimplification(); + return; + } + + if ((input_cst != nullptr) && Primitive::IsFloatingPointType(type)) { + // Try replacing code looking like + // DIV dst, src, constant + // with + // MUL dst, src, 1 / constant + HConstant* reciprocal = nullptr; + if (type == Primitive::Primitive::kPrimDouble) { + double value = input_cst->AsDoubleConstant()->GetValue(); + if (CanDivideByReciprocalMultiplyDouble(bit_cast<int64_t, double>(value))) { + reciprocal = GetGraph()->GetDoubleConstant(1.0 / value); + } + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + float value = input_cst->AsFloatConstant()->GetValue(); + if (CanDivideByReciprocalMultiplyFloat(bit_cast<int32_t, float>(value))) { + reciprocal = GetGraph()->GetFloatConstant(1.0f / value); + } + } + + if (reciprocal != nullptr) { + instruction->GetBlock()->ReplaceAndRemoveInstructionWith( + instruction, new (GetGraph()->GetArena()) HMul(type, input_other, reciprocal)); + RecordSimplification(); + return; + } } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 20aa45f197..5d3db5c6f2 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -186,6 +186,8 @@ static Intrinsics GetIntrinsic(InlineMethod method) { return Intrinsics::kStringCharAt; case kIntrinsicCompareTo: return Intrinsics::kStringCompareTo; + case kIntrinsicGetCharsNoCheck: + return Intrinsics::kStringGetCharsNoCheck; case kIntrinsicIsEmptyOrLength: // The inliner can handle these two cases - and this is the preferred approach // since after inlining the call is no longer visible (as opposed to waiting @@ -194,6 +196,12 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; + case kIntrinsicNewStringFromBytes: + return Intrinsics::kStringNewStringFromBytes; + case kIntrinsicNewStringFromChars: + return Intrinsics::kStringNewStringFromChars; + case kIntrinsicNewStringFromString: + return Intrinsics::kStringNewStringFromString; case kIntrinsicCas: switch (GetType(method.d.data, false)) { @@ -280,6 +288,11 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kInlineOpIPut: return Intrinsics::kNone; + // String init cases, not intrinsics. + + case kInlineStringInit: + return Intrinsics::kNone; + // No default case to make the compiler warn on missing cases. } return Intrinsics::kNone; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index abdf04ebb1..e3fa272d22 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -48,7 +48,7 @@ static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGener DCHECK_NE(type, Primitive::kPrimVoid); - if (Primitive::IsIntegralType(type)) { + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { if (type == Primitive::kPrimLong) { Register trg_reg_lo = trg.AsRegisterPairLow<Register>(); Register trg_reg_hi = trg.AsRegisterPairHigh<Register>(); @@ -78,7 +78,7 @@ static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGener } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -90,7 +90,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorA // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -810,10 +810,6 @@ void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { const MemberOffset value_offset = mirror::String::ValueOffset(); // Location of count const MemberOffset count_offset = mirror::String::CountOffset(); - // Starting offset within data array - const MemberOffset offset_offset = mirror::String::OffsetOffset(); - // Start of char data with array_ - const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t)); Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer. Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character. @@ -835,15 +831,10 @@ void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { __ cmp(idx, ShifterOperand(temp)); __ b(slow_path->GetEntryLabel(), CS); - // Index computation. - __ ldr(temp, Address(obj, offset_offset.Int32Value())); // temp := str.offset. - __ ldr(array_temp, Address(obj, value_offset.Int32Value())); // array_temp := str.offset. - __ add(temp, temp, ShifterOperand(idx)); - DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting. - __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2)); + __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value. // Load the value. - __ ldrh(out, Address(array_temp, temp, LSL, 1)); // out := array_temp[temp]. + __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx]. __ Bind(slow_path->GetExitLabel()); } @@ -878,6 +869,81 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister<Register>(); + __ cmp(byte_array, ShifterOperand(0)); + SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), EQ); + + __ LoadFromOffset( + kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + + __ LoadFromOffset( + kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); +} + +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister<Register>(); + __ cmp(string_to_copy, ShifterOperand(0)); + SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), EQ); + + __ LoadFromOffset(kLoadWord, + LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -907,6 +973,7 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) } // namespace arm } // namespace art diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 7a753b2da9..d71b49e6f1 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -75,7 +75,7 @@ static void MoveFromReturnRegister(Location trg, DCHECK_NE(type, Primitive::kPrimVoid); - if (Primitive::IsIntegralType(type)) { + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { Register trg_reg = RegisterFrom(trg, type); Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type); __ Mov(trg_reg, res_reg, kDiscardForSameWReg); @@ -87,7 +87,7 @@ static void MoveFromReturnRegister(Location trg, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -99,7 +99,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorA // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -953,10 +953,6 @@ void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { const MemberOffset value_offset = mirror::String::ValueOffset(); // Location of count const MemberOffset count_offset = mirror::String::CountOffset(); - // Starting offset within data array - const MemberOffset offset_offset = mirror::String::OffsetOffset(); - // Start of char data with array_ - const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t)); Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer. Register idx = WRegisterFrom(locations->InAt(1)); // Index of character. @@ -979,21 +975,15 @@ void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { __ Cmp(idx, temp); __ B(hs, slow_path->GetEntryLabel()); - // Index computation. - __ Ldr(temp, HeapOperand(obj, offset_offset)); // temp := str.offset. - __ Ldr(array_temp, HeapOperand(obj, value_offset)); // array_temp := str.offset. - __ Add(temp, temp, idx); - DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting. - __ Add(temp, temp, Operand(data_offset.Int32Value() / 2)); + __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value. // Load the value. - __ Ldrh(out, MemOperand(array_temp.X(), temp, UXTW, 1)); // out := array_temp[temp]. + __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx]. __ Bind(slow_path->GetExitLabel()); } void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { - // The inputs plus one temp. LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); @@ -1022,6 +1012,84 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = WRegisterFrom(locations->InAt(0)); + __ Cmp(byte_array, 0); + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + __ B(eq, slow_path->GetEntryLabel()); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); +} + +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = WRegisterFrom(locations->InAt(0)); + __ Cmp(string_to_copy, 0); + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + __ B(eq, slow_path->GetEntryLabel()); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1034,6 +1102,7 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 10f6e1d6c7..2c9248f52c 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -60,8 +60,12 @@ V(MemoryPokeShortNative, kStatic) \ V(StringCharAt, kDirect) \ V(StringCompareTo, kDirect) \ + V(StringGetCharsNoCheck, kDirect) \ V(StringIndexOf, kDirect) \ V(StringIndexOfAfter, kDirect) \ + V(StringNewStringFromBytes, kStatic) \ + V(StringNewStringFromChars, kStatic) \ + V(StringNewStringFromString, kStatic) \ V(UnsafeCASInt, kDirect) \ V(UnsafeCASLong, kDirect) \ V(UnsafeCASObject, kDirect) \ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 7275edb695..18fb3c4d43 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -112,7 +112,7 @@ static void MoveFromReturnRegister(Location target, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -124,7 +124,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -910,23 +910,18 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); Register obj = locations->InAt(0).AsRegister<Register>(); Register idx = locations->InAt(1).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); - Location temp_loc = locations->GetTemp(0); - Register temp = temp_loc.AsRegister<Register>(); // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth // the cost. // TODO: For simplicity, the index parameter is requested in a register, so different from Quick // we will not optimize the code for constants (which would save a register). - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke, temp); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); codegen_->AddSlowPath(slow_path); X86Assembler* assembler = GetAssembler(); @@ -935,12 +930,8 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { codegen_->MaybeRecordImplicitNullCheck(invoke); __ j(kAboveEqual, slow_path->GetEntryLabel()); - // Get the actual element. - __ movl(temp, idx); // temp := idx. - __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx. - __ movl(out, Address(obj, value_offset)); // obj := obj.array. - // out = out[2*temp]. - __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset)); + // out = out[2*idx]. + __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); __ Bind(slow_path->GetExitLabel()); } @@ -976,6 +967,81 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(EAX)); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister<Register>(); + __ testl(byte_array, byte_array); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromBytes))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromChars))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(EAX)); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister<Register>(); + __ testl(string_to_copy, string_to_copy); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromString))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + static void GenPeek(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { Register address = locations->InAt(0).AsRegisterPairLow<Register>(); Location out_loc = locations->Out(); @@ -1536,6 +1602,7 @@ void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) } UNIMPLEMENTED_INTRINSIC(MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 35daaf60bb..db7b58bc66 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -104,7 +104,7 @@ static void MoveFromReturnRegister(Location trg, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -116,7 +116,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -824,16 +824,10 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); - Location temp_loc = locations->GetTemp(0); - CpuRegister temp = temp_loc.AsRegister<CpuRegister>(); // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth // the cost. @@ -849,12 +843,8 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { codegen_->MaybeRecordImplicitNullCheck(invoke); __ j(kAboveEqual, slow_path->GetEntryLabel()); - // Get the actual element. - __ movl(temp, idx); // temp := idx. - __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx. - __ movl(out, Address(obj, value_offset)); // obj := obj.array. - // out = out[2*temp]. - __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset)); + // out = out[2*idx]. + __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); __ Bind(slow_path->GetExitLabel()); } @@ -887,6 +877,78 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + CpuRegister byte_array = locations->InAt(0).AsRegister<CpuRegister>(); + __ testl(byte_array, byte_array); + SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromBytes), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromChars), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromString(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + CpuRegister string_to_copy = locations->InAt(0).AsRegister<CpuRegister>(); + __ testl(string_to_copy, string_to_copy); + SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromString), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) { CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); // == address, here for clarity. @@ -1390,6 +1452,7 @@ void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UN void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index cc76d9ca85..f64086e607 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -561,6 +561,13 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { predecessors_.Put(1, temp); } + void SwapSuccessors() { + DCHECK_EQ(successors_.Size(), 2u); + HBasicBlock* temp = successors_.Get(0); + successors_.Put(0, successors_.Get(1)); + successors_.Put(1, temp); + } + size_t GetPredecessorIndexOf(HBasicBlock* predecessor) { for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { if (predecessors_.Get(i) == predecessor) { @@ -2223,6 +2230,12 @@ class HInvoke : public HInstruction { SetRawInputAt(index, argument); } + // Return the number of arguments. This number can be lower than + // the number of inputs returned by InputCount(), as some invoke + // instructions (e.g. HInvokeStaticOrDirect) can have non-argument + // inputs at the end of their list of inputs. + uint32_t GetNumberOfArguments() const { return number_of_arguments_; } + Primitive::Type GetType() const OVERRIDE { return return_type_; } uint32_t GetDexPc() const { return dex_pc_; } @@ -2242,16 +2255,19 @@ class HInvoke : public HInstruction { protected: HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, + uint32_t number_of_other_inputs, Primitive::Type return_type, uint32_t dex_pc, uint32_t dex_method_index) : HInstruction(SideEffects::All()), + number_of_arguments_(number_of_arguments), inputs_(arena, number_of_arguments), return_type_(return_type), dex_pc_(dex_pc), dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone) { - inputs_.SetSize(number_of_arguments); + uint32_t number_of_inputs = number_of_arguments + number_of_other_inputs; + inputs_.SetSize(number_of_inputs); } const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { return inputs_.Get(i); } @@ -2259,6 +2275,7 @@ class HInvoke : public HInstruction { inputs_.Put(index, input); } + uint32_t number_of_arguments_; GrowableArray<HUserRecord<HInstruction*> > inputs_; const Primitive::Type return_type_; const uint32_t dex_pc_; @@ -2285,14 +2302,21 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, bool is_recursive, + int32_t string_init_offset, InvokeType original_invoke_type, InvokeType invoke_type, ClinitCheckRequirement clinit_check_requirement) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, + number_of_arguments, + clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u, + return_type, + dex_pc, + dex_method_index), original_invoke_type_(original_invoke_type), invoke_type_(invoke_type), is_recursive_(is_recursive), - clinit_check_requirement_(clinit_check_requirement) {} + clinit_check_requirement_(clinit_check_requirement), + string_init_offset_(string_init_offset) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { UNUSED(obj); @@ -2305,21 +2329,24 @@ class HInvokeStaticOrDirect : public HInvoke { InvokeType GetInvokeType() const { return invoke_type_; } bool IsRecursive() const { return is_recursive_; } bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); } + bool IsStringInit() const { return string_init_offset_ != 0; } + int32_t GetStringInitOffset() const { return string_init_offset_; } // Is this instruction a call to a static method? bool IsStatic() const { return GetInvokeType() == kStatic; } - // Remove the art::HClinitCheck or art::HLoadClass instruction as - // last input (only relevant for static calls with explicit clinit - // check). - void RemoveClinitCheckOrLoadClassAsLastInput() { + // Remove the art::HLoadClass instruction set as last input by + // art::PrepareForRegisterAllocation::VisitClinitCheck in lieu of + // the initial art::HClinitCheck instruction (only relevant for + // static calls with explicit clinit check). + void RemoveLoadClassAsLastInput() { DCHECK(IsStaticWithExplicitClinitCheck()); size_t last_input_index = InputCount() - 1; HInstruction* last_input = InputAt(last_input_index); DCHECK(last_input != nullptr); - DCHECK(last_input->IsClinitCheck() || last_input->IsLoadClass()) << last_input->DebugName(); + DCHECK(last_input->IsLoadClass()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.DeleteAt(last_input_index); clinit_check_requirement_ = ClinitCheckRequirement::kImplicit; @@ -2360,6 +2387,9 @@ class HInvokeStaticOrDirect : public HInvoke { const InvokeType invoke_type_; const bool is_recursive_; ClinitCheckRequirement clinit_check_requirement_; + // Thread entrypoint offset for string init method if this is a string init invoke. + // Note that there are multiple string init methods, each having its own offset. + int32_t string_init_offset_; DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; @@ -2372,7 +2402,7 @@ class HInvokeVirtual : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, uint32_t vtable_index) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index), vtable_index_(vtable_index) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { @@ -2398,7 +2428,7 @@ class HInvokeInterface : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, uint32_t imt_index) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index), imt_index_(imt_index) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index fa6b3c292c..78d11857c3 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -91,7 +91,7 @@ void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDire // previously) by the graph builder during the creation of the // static invoke instruction, but is no longer required at this // stage (i.e., after inlining has been performed). - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + invoke->RemoveLoadClassAsLastInput(); // If the load class instruction is no longer used, remove it from // the graph. diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 0fdf051957..a8d006f104 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -1455,6 +1455,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { : Location::StackSlot(interval->GetParent()->GetSpillSlot())); } UsePosition* use = current->GetFirstUse(); + UsePosition* env_use = current->GetFirstEnvironmentUse(); // Walk over all siblings, updating locations of use positions, and // connecting them when they are adjacent. @@ -1466,32 +1467,39 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { LiveRange* range = current->GetFirstRange(); while (range != nullptr) { - while (use != nullptr && use->GetPosition() < range->GetStart()) { - DCHECK(use->GetIsEnvironment()); - use = use->GetNext(); - } + DCHECK(use == nullptr || use->GetPosition() >= range->GetStart()); while (use != nullptr && use->GetPosition() <= range->GetEnd()) { + DCHECK(!use->GetIsEnvironment()); DCHECK(current->CoversSlow(use->GetPosition()) || (use->GetPosition() == range->GetEnd())); LocationSummary* locations = use->GetUser()->GetLocations(); - if (use->GetIsEnvironment()) { - locations->SetEnvironmentAt(use->GetInputIndex(), source); - } else { - Location expected_location = locations->InAt(use->GetInputIndex()); - // The expected (actual) location may be invalid in case the input is unused. Currently - // this only happens for intrinsics. - if (expected_location.IsValid()) { - if (expected_location.IsUnallocated()) { - locations->SetInAt(use->GetInputIndex(), source); - } else if (!expected_location.IsConstant()) { - AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location); - } - } else { - DCHECK(use->GetUser()->IsInvoke()); - DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone); + Location expected_location = locations->InAt(use->GetInputIndex()); + // The expected (actual) location may be invalid in case the input is unused. Currently + // this only happens for intrinsics. + if (expected_location.IsValid()) { + if (expected_location.IsUnallocated()) { + locations->SetInAt(use->GetInputIndex(), source); + } else if (!expected_location.IsConstant()) { + AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location); } + } else { + DCHECK(use->GetUser()->IsInvoke()); + DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone); } use = use->GetNext(); } + + // Walk over the environment uses, and update their locations. + while (env_use != nullptr && env_use->GetPosition() < range->GetStart()) { + env_use = env_use->GetNext(); + } + + while (env_use != nullptr && env_use->GetPosition() <= range->GetEnd()) { + DCHECK(current->CoversSlow(env_use->GetPosition()) || (env_use->GetPosition() == range->GetEnd())); + LocationSummary* locations = env_use->GetUser()->GetLocations(); + locations->SetEnvironmentAt(env_use->GetInputIndex(), source); + env_use = env_use->GetNext(); + } + range = range->GetNext(); } @@ -1553,14 +1561,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { current = next_sibling; } while (current != nullptr); - if (kIsDebugBuild) { - // Following uses can only be environment uses. The location for - // these environments will be none. - while (use != nullptr) { - DCHECK(use->GetIsEnvironment()); - use = use->GetNext(); - } - } + DCHECK(use == nullptr); } void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index ea0e7c3712..b674f746b6 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -341,7 +341,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position >= start && !use->GetIsEnvironment()) { + if (use_position >= start) { HInstruction* user = use->GetUser(); size_t input_index = use->GetInputIndex(); if (user->IsPhi()) { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97254edb5e..b95276afd7 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -219,6 +219,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { void AddTempUse(HInstruction* instruction, size_t temp_index) { DCHECK(IsTemp()); DCHECK(first_use_ == nullptr) << "A temporary can only have one user"; + DCHECK(first_env_use_ == nullptr) << "A temporary cannot have environment user"; size_t position = instruction->GetLifetimePosition(); first_use_ = new (allocator_) UsePosition( instruction, temp_index, /* is_environment */ false, position, first_use_); @@ -265,8 +266,13 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return; } - first_use_ = new (allocator_) UsePosition( - instruction, input_index, is_environment, position, first_use_); + if (is_environment) { + first_env_use_ = new (allocator_) UsePosition( + instruction, input_index, is_environment, position, first_env_use_); + } else { + first_use_ = new (allocator_) UsePosition( + instruction, input_index, is_environment, position, first_use_); + } if (is_environment && !keep_alive) { // If this environment use does not keep the instruction live, it does not @@ -477,7 +483,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position > position && !use->GetIsEnvironment()) { + if (use_position > position) { Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); if (location.IsUnallocated() && (location.GetPolicy() == Location::kRequiresRegister @@ -508,12 +514,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { UsePosition* use = first_use_; size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { - if (!use->GetIsEnvironment()) { - Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); - size_t use_position = use->GetPosition(); - if (use_position > position && location.IsValid()) { - return use_position; - } + Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); + size_t use_position = use->GetPosition(); + if (use_position > position && location.IsValid()) { + return use_position; } use = use->GetNext(); } @@ -524,6 +528,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return first_use_; } + UsePosition* GetFirstEnvironmentUse() const { + return first_env_use_; + } + Primitive::Type GetType() const { return type_; } @@ -577,6 +585,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { new_interval->parent_ = parent_; new_interval->first_use_ = first_use_; + new_interval->first_env_use_ = first_env_use_; LiveRange* current = first_range_; LiveRange* previous = nullptr; // Iterate over the ranges, and either find a range that covers this position, or @@ -655,10 +664,18 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { stream << " "; } while ((use = use->GetNext()) != nullptr); } + stream << "}, {"; + use = first_env_use_; + if (use != nullptr) { + do { + use->Dump(stream); + stream << " "; + } while ((use = use->GetNext()) != nullptr); + } stream << "}"; stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit(); - stream << " is_high: " << IsHighInterval(); stream << " is_low: " << IsLowInterval(); + stream << " is_high: " << IsHighInterval(); } LiveInterval* GetNextSibling() const { return next_sibling_; } @@ -754,6 +771,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { if (first_use_ != nullptr) { high_or_low_interval_->first_use_ = first_use_->Dup(allocator_); } + + if (first_env_use_ != nullptr) { + high_or_low_interval_->first_env_use_ = first_env_use_->Dup(allocator_); + } } // Returns whether an interval, when it is non-split, is using @@ -851,6 +872,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { first_safepoint_(nullptr), last_safepoint_(nullptr), first_use_(nullptr), + first_env_use_(nullptr), type_(type), next_sibling_(nullptr), parent_(this), @@ -905,6 +927,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { // Uses of this interval. Note that this linked list is shared amongst siblings. UsePosition* first_use_; + UsePosition* first_env_use_; // The instruction type this interval corresponds to. const Primitive::Type type_; |