diff options
Diffstat (limited to 'compiler/optimizing')
27 files changed, 784 insertions, 275 deletions
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index a6687fe258..58f591bd1e 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -16,6 +16,7 @@ #include "block_builder.h" +#include "base/logging.h" // FOR VLOG. #include "bytecode_utils.h" #include "quicken_info.h" @@ -58,6 +59,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // cannot have any code afterwards. } else { // The TryItem spans beyond the end of the CodeItem. This is invalid code. + VLOG(compiler) << "Not compiled: TryItem spans beyond the end of the CodeItem"; return false; } } @@ -110,6 +112,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { if (next == instructions.end()) { // In the normal case we should never hit this but someone can artificially forge a dex // file to fall-through out the method code. In this case we bail out compilation. + VLOG(compiler) << "Not compiled: Fall-through beyond the CodeItem"; return false; } MaybeCreateBlockAt(next.DexPc()); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index aff6f9f64f..45eec6d744 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -981,21 +981,6 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, } } - uint32_t outer_dex_pc = dex_pc; - uint32_t outer_environment_size = 0; - uint32_t inlining_depth = 0; - if (instruction != nullptr) { - for (HEnvironment* environment = instruction->GetEnvironment(); - environment != nullptr; - environment = environment->GetParent()) { - outer_dex_pc = environment->GetDexPc(); - outer_environment_size = environment->Size(); - if (environment != instruction->GetEnvironment()) { - inlining_depth++; - } - } - } - // Collect PC infos for the mapping table. uint32_t native_pc = GetAssembler()->CodePosition(); @@ -1003,12 +988,12 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (instruction == nullptr) { // For stack overflow checks and native-debug-info entries without dex register // mapping (i.e. start of basic block or start of slow path). - stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0); + stack_map_stream->BeginStackMapEntry(dex_pc, native_pc, 0, 0, 0, 0); stack_map_stream->EndStackMapEntry(); return; } - LocationSummary* locations = instruction->GetLocations(); + LocationSummary* locations = instruction->GetLocations(); uint32_t register_mask = locations->GetRegisterMask(); DCHECK_EQ(register_mask & ~locations->GetLiveRegisters()->GetCoreRegisters(), 0u); if (locations->OnlyCallsOnSlowPath()) { @@ -1023,22 +1008,33 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, // The register mask must be a subset of callee-save registers. DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask); } + + uint32_t outer_dex_pc = dex_pc; + uint32_t outer_environment_size = 0u; + uint32_t inlining_depth = 0; + HEnvironment* const environment = instruction->GetEnvironment(); + if (environment != nullptr) { + HEnvironment* outer_environment = environment; + while (outer_environment->GetParent() != nullptr) { + outer_environment = outer_environment->GetParent(); + ++inlining_depth; + } + outer_dex_pc = outer_environment->GetDexPc(); + outer_environment_size = outer_environment->Size(); + } stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, inlining_depth); - - HEnvironment* const environment = instruction->GetEnvironment(); EmitEnvironment(environment, slow_path); // Record invoke info, the common case for the trampoline is super and static invokes. Only // record these to reduce oat file size. if (kEnableDexLayoutOptimizations) { - if (environment != nullptr && - instruction->IsInvoke() && - instruction->IsInvokeStaticOrDirect()) { - HInvoke* const invoke = instruction->AsInvoke(); + if (instruction->IsInvokeStaticOrDirect()) { + HInvoke* const invoke = instruction->AsInvokeStaticOrDirect(); + DCHECK(environment != nullptr); stack_map_stream->AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex()); } } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index d6922d2f3f..1f6b214f11 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1941,6 +1941,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DCHECK_EQ(instruction->InputCount(), 2U); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction); DataType::Type type = instruction->GetResultType(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { locations->SetInAt(0, Location::RequiresRegister()); @@ -1950,11 +1951,22 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { int32_t imm = CodeGenerator::GetInt32ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsSub() || instruction->IsAdd()); + if (instruction->IsSub()) { + imm = -imm; + } + if (isR6) { + bool single_use = right->GetUses().HasExactlyOneElement(); + int16_t imm_high = High16Bits(imm); + int16_t imm_low = Low16Bits(imm); + if (imm_low < 0) { + imm_high += 1; + } + can_use_imm = !((imm_high != 0) && (imm_low != 0)) || single_use; + } else { + can_use_imm = IsInt<16>(imm); + } } } if (can_use_imm) @@ -1988,6 +2000,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DataType::Type type = instruction->GetType(); LocationSummary* locations = instruction->GetLocations(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { @@ -2019,17 +2032,32 @@ void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); } else { - DCHECK(instruction->IsSub()); - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else + DCHECK(instruction->IsAdd() || instruction->IsSub()); + if (use_imm) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; + } + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + DCHECK(isR6); + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Subu(dst, lhs, rhs_reg); + } } break; } @@ -3103,23 +3131,92 @@ void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); - codegen_->AddSlowPath(slow_path); - - Register index = locations->InAt(0).AsRegister<Register>(); - Register length = locations->InAt(1).AsRegister<Register>(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeu(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + Register index = index_loc.AsRegister<Register>(); + if (length == 0) { + __ B(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnez(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqz(TMP, slow_path->GetEntryLabel()); + } + } else { + Register length = length_loc.AsRegister<Register>(); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ B(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blez(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnez(TMP, slow_path->GetEntryLabel()); + } + } else { + Register index = index_loc.AsRegister<Register>(); + __ Bgeu(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index ee33b3f335..22989c8283 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1793,11 +1793,19 @@ void LocationsBuilderMIPS64::HandleBinaryOp(HBinaryOperation* instruction) { int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsAdd() || instruction->IsSub()); + bool single_use = right->GetUses().HasExactlyOneElement(); + if (instruction->IsSub()) { + if (!(type == DataType::Type::kInt32 && imm == INT32_MIN)) { + imm = -imm; + } + } + if (type == DataType::Type::kInt32) { + can_use_imm = IsInt<16>(imm) || (Low16Bits(imm) == 0) || single_use; + } else { + can_use_imm = IsInt<16>(imm) || (IsInt<32>(imm) && (Low16Bits(imm) == 0)) || single_use; + } } } if (can_use_imm) @@ -1855,30 +1863,90 @@ void InstructionCodeGeneratorMIPS64::HandleBinaryOp(HBinaryOperation* instructio __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); - } else { - if (use_imm) - __ Daddiu(dst, lhs, rhs_imm); - else - __ Daddu(dst, lhs, rhs_reg); + } else if (instruction->IsAdd() || instruction->IsSub()) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; } - } else { - DCHECK(instruction->IsSub()); if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else - __ Subu(dst, lhs, rhs_reg); + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else { + if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); + __ Subu(dst, lhs, rhs_reg); + } + } } else { - if (use_imm) - __ Daddiu(dst, lhs, -rhs_imm); - else + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Daddiu(dst, lhs, rhs_imm); + } else if (IsInt<32>(rhs_imm)) { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + bool overflow_hi16 = false; + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + overflow_hi16 = (rhs_imm_high == -32768); + } + __ Daui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Daddiu(dst, dst, rhs_imm_low); + } + if (overflow_hi16) { + __ Dahi(dst, 1); + } + } else { + int16_t rhs_imm_low = Low16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_low < 0) { + rhs_imm += (INT64_C(1) << 16); + } + int16_t rhs_imm_upper = High16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_upper < 0) { + rhs_imm += (INT64_C(1) << 32); + } + int16_t rhs_imm_high = Low16Bits(High32Bits(rhs_imm)); + if (rhs_imm_high < 0) { + rhs_imm += (INT64_C(1) << 48); + } + int16_t rhs_imm_top = High16Bits(High32Bits(rhs_imm)); + GpuRegister tmp = lhs; + if (rhs_imm_low != 0) { + __ Daddiu(dst, tmp, rhs_imm_low); + tmp = dst; + } + // Dahi and Dati must use the same input and output register, so we have to initialize + // the dst register using Daddiu or Daui, even when the intermediate value is zero: + // Daui(dst, lhs, 0). + if ((rhs_imm_upper != 0) || (rhs_imm_low == 0)) { + __ Daui(dst, tmp, rhs_imm_upper); + } + if (rhs_imm_high != 0) { + __ Dahi(dst, rhs_imm_high); + } + if (rhs_imm_top != 0) { + __ Dati(dst, rhs_imm_top); + } + } + } else if (instruction->IsAdd()) { + __ Daddu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Dsubu(dst, lhs, rhs_reg); + } } } break; @@ -2614,23 +2682,92 @@ void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS64* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); - codegen_->AddSlowPath(slow_path); - - GpuRegister index = locations->InAt(0).AsRegister<GpuRegister>(); - GpuRegister length = locations->InAt(1).AsRegister<GpuRegister>(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + __ Bc(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeuc(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + GpuRegister index = index_loc.AsRegister<GpuRegister>(); + if (length == 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnezc(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqzc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister length = length_loc.AsRegister<GpuRegister>(); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blezc(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnezc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister index = index_loc.AsRegister<GpuRegister>(); + __ Bgeuc(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc index 96fe2a17e6..dd47a1fc6c 100644 --- a/compiler/optimizing/code_generator_utils.cc +++ b/compiler/optimizing/code_generator_utils.cc @@ -15,9 +15,10 @@ */ #include "code_generator_utils.h" -#include "nodes.h" -#include "base/logging.h" +#include <android-base/logging.h> + +#include "nodes.h" namespace art { diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index bb586bf096..6f11e628ee 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -113,7 +113,7 @@ void HConstantFoldingVisitor::VisitBinaryOperation(HBinaryOperation* inst) { void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) { // Constant folding: replace `TypeConversion(a)' with a constant at // compile time if `a' is a constant. - HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation(); + HConstant* constant = inst->TryStaticEvaluation(); if (constant != nullptr) { inst->ReplaceWith(constant); inst->GetBlock()->RemoveInstruction(inst); diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index d253036479..548fe28cee 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/bit_utils.h" namespace art { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 2444e43d64..8750910fe1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -294,7 +294,7 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, // as there may be different class loaders. So only return the index if it's // the right class already resolved with the class loader. if (index.IsValid()) { - ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> resolved = compilation_unit.GetClassLinker()->LookupResolvedType( index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); if (resolved != cls) { index = dex::TypeIndex::Invalid(); @@ -682,7 +682,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( << "is invalid in location" << dex_cache->GetDexFile()->GetLocation(); return kInlineCacheNoData; } - ObjPtr<mirror::Class> clazz = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType( class_ref.type_index, dex_cache, caller_compilation_unit_.GetClassLoader().Get()); @@ -876,9 +876,9 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, load_class, codegen_, compiler_driver_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; - // Insert before setting the kind, as setting the kind affects the inputs. - bb_cursor->InsertInstructionAfter(load_class, receiver_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(kind); + bb_cursor->InsertInstructionAfter(load_class, receiver_class); // In AOT mode, we will most likely load the class from BSS, which will involve a call // to the runtime. In this case, the load instruction will need an environment so copy // it from the invoke instruction. @@ -1211,11 +1211,49 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ReferenceTypeInfo receiver_type, bool do_rtp, bool cha_devirtualize) { + DCHECK(!invoke_instruction->IsIntrinsic()); HInstruction* return_replacement = nullptr; uint32_t dex_pc = invoke_instruction->GetDexPc(); HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { + bool should_remove_invoke_instruction = false; + + // If invoke_instruction is devirtualized to a different method, give intrinsics + // another chance before we try to inline it. + bool wrong_invoke_type = false; + if (invoke_instruction->GetResolvedMethod() != method && + IntrinsicsRecognizer::Recognize(invoke_instruction, method, &wrong_invoke_type)) { + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); + if (invoke_instruction->IsInvokeInterface()) { + // We don't intrinsify an invoke-interface directly. + // Replace the invoke-interface with an invoke-virtual. + HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual( + graph_->GetAllocator(), + invoke_instruction->GetNumberOfArguments(), + invoke_instruction->GetType(), + invoke_instruction->GetDexPc(), + invoke_instruction->GetDexMethodIndex(), // Use interface method's dex method index. + method, + method->GetMethodIndex()); + HInputsRef inputs = invoke_instruction->GetInputs(); + for (size_t index = 0; index != inputs.size(); ++index) { + new_invoke->SetArgumentAt(index, inputs[index]); + } + invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction); + new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); + if (invoke_instruction->GetType() == DataType::Type::kReference) { + new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); + } + // Run intrinsic recognizer again to set new_invoke's intrinsic. + IntrinsicsRecognizer::Recognize(new_invoke, method, &wrong_invoke_type); + DCHECK_NE(new_invoke->GetIntrinsic(), Intrinsics::kNone); + return_replacement = new_invoke; + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; + } else { + // invoke_instruction is intrinsified and stays. + } + } else if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { DCHECK(!method->IsProxyMethod()); // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always @@ -1258,26 +1296,27 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); } return_replacement = new_invoke; - // Directly check if the new virtual can be recognized as an intrinsic. - // This way, we avoid running a full recognition pass just to detect - // these relative rare cases. - bool wrong_invoke_type = false; - if (IntrinsicsRecognizer::Recognize(new_invoke, &wrong_invoke_type)) { - MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); - } + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; } else { // TODO: Consider sharpening an invoke virtual once it is not dependent on the // compiler driver. return false; } + } else { + // invoke_instruction is inlined. + should_remove_invoke_instruction = true; } + if (cha_devirtualize) { AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor); } if (return_replacement != nullptr) { invoke_instruction->ReplaceWith(return_replacement); } - invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + if (should_remove_invoke_instruction) { + invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + } FixUpReturnReferenceType(method, return_replacement); if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { // Actual return value has a more specific type than the method's declared @@ -1893,7 +1932,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, // optimization that could lead to a HDeoptimize. The following optimizations do not. HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); - HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); + HSharpening sharpening(callee_graph, codegen_, compiler_driver_); InstructionSimplifier simplify(callee_graph, codegen_, compiler_driver_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 978d0c2225..e36d91fb05 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -442,17 +442,15 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_->insns_size_in_code_units_; + CodeItemDebugInfoAccessor accessor(dex_file_, code_item_); ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, - num_instructions, + accessor.InsnsSizeInCodeUnits(), /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_); - dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), Callback::Position, locations); // Instruction-specific tweaks. - IterationRange<DexInstructionIterator> instructions = code_item_->Instructions(); - for (const DexInstructionPcPair& inst : instructions) { + for (const DexInstructionPcPair& inst : accessor) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { // Stop in native debugger after the exception has been moved. @@ -461,7 +459,7 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { locations->ClearBit(inst.DexPc()); DexInstructionIterator next = std::next(DexInstructionIterator(inst)); DCHECK(next.DexPc() != inst.DexPc()); - if (next != instructions.end()) { + if (next != accessor.end()) { locations->SetBit(next.DexPc()); } break; @@ -796,7 +794,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - *dex_compilation_unit_->GetDexFile(), method_idx, dex_compilation_unit_->GetDexCache(), class_loader, @@ -831,7 +828,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( - *dex_compilation_unit_->GetDexFile(), dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, dex_compilation_unit_->GetDexCache().Get(), class_loader.Get()); @@ -1128,7 +1124,7 @@ void HInstructionBuilder::BuildConstructorFenceForAllocation(HInstruction* alloc MethodCompilationStat::kConstructorFenceGeneratedNew); } -static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) +static bool IsSubClass(ObjPtr<mirror::Class> to_test, ObjPtr<mirror::Class> super_class) REQUIRES_SHARED(Locks::mutator_lock_) { return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class); } @@ -1351,6 +1347,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio uint16_t field_index; if (instruction.IsQuickened()) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } field_index = LookupQuickenedInfo(quicken_index); @@ -1422,8 +1420,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio return true; } -static mirror::Class* GetClassFrom(CompilerDriver* driver, - const DexCompilationUnit& compilation_unit) { +static ObjPtr<mirror::Class> GetClassFrom(CompilerDriver* driver, + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); @@ -1431,11 +1429,11 @@ static mirror::Class* GetClassFrom(CompilerDriver* driver, return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); } -mirror::Class* HInstructionBuilder::GetOutermostCompilingClass() const { +ObjPtr<mirror::Class> HInstructionBuilder::GetOutermostCompilingClass() const { return GetClassFrom(compiler_driver_, *outer_compilation_unit_); } -mirror::Class* HInstructionBuilder::GetCompilingClass() const { +ObjPtr<mirror::Class> HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } @@ -1482,12 +1480,10 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); - ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), - field_idx, + ArtField* resolved_field = class_linker->ResolveField(field_idx, dex_compilation_unit_->GetDexCache(), class_loader, is_static); - if (UNLIKELY(resolved_field == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); @@ -1523,7 +1519,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, return resolved_field; } -bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, +void HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { uint32_t source_or_dest_reg = instruction.VRegA_21c(); @@ -1537,7 +1533,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MethodCompilationStat::kUnresolvedField); DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); @@ -1555,7 +1551,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MaybeRecordStat(compilation_stats_, MethodCompilationStat::kUnresolvedFieldNotAFastAccess); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } HInstruction* cls = constant; @@ -1591,7 +1587,6 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, dex_pc)); UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); } - return true; } void HInstructionBuilder::BuildCheckedDivRem(uint16_t out_vreg, @@ -1799,6 +1794,17 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) } } +void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { + HLoadString* load_string = + new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); + HSharpening::ProcessLoadString(load_string, + code_generator_, + compiler_driver_, + *dex_compilation_unit_, + handles_); + AppendInstruction(load_string); +} + HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); @@ -1811,7 +1817,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3 if (klass->IsPublic()) { needs_access_check = false; } else { - mirror::Class* compiling_class = GetCompilingClass(); + ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { needs_access_check = false; } @@ -1856,9 +1862,9 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, // We actually cannot reference this class, we're forced to bail. return nullptr; } - // Append the instruction first, as setting the load kind affects the inputs. - AppendInstruction(load_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(load_kind); + AppendInstruction(load_class); return load_class; } @@ -2058,6 +2064,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2083,6 +2091,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2758,7 +2768,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IGET_CHAR_QUICK: case Instruction::IGET_SHORT: case Instruction::IGET_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, false, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ false, quicken_index)) { return false; } break; @@ -2778,7 +2788,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IPUT_CHAR_QUICK: case Instruction::IPUT_SHORT: case Instruction::IPUT_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, true, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ true, quicken_index)) { return false; } break; @@ -2791,9 +2801,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, false)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ false); break; } @@ -2804,9 +2812,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, true)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ true); break; } @@ -2837,20 +2843,14 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_STRING: { dex::StringIndex string_index(instruction.VRegB_21c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } case Instruction::CONST_STRING_JUMBO: { dex::StringIndex string_index(instruction.VRegB_31c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction()); break; } @@ -2930,7 +2930,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType( dex::TypeIndex type_index, const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( + return compilation_unit.GetClassLinker()->LookupResolvedType( type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f551ac4280..0500d40cd3 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -175,8 +175,8 @@ class HInstructionBuilder : public ValueObject { uint32_t dex_pc, bool is_put, DataType::Type field_type); - // Builds a static field access node and returns whether the instruction is supported. - bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); + // Builds a static field access node. + void BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); void BuildArrayAccess(const Instruction& instruction, uint32_t dex_pc, @@ -240,9 +240,10 @@ class HInstructionBuilder : public ValueObject { // Builds an instruction sequence for a switch statement. void BuildSwitch(const Instruction& instruction, uint32_t dex_pc); - // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true, - // this method will use the outer class's dex file to lookup the type at - // `type_index`. + // Builds a `HLoadString` loading the given `string_index`. + void BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc); + + // Builds a `HLoadClass` loading the given `type_index`. HLoadClass* BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc); HLoadClass* BuildLoadClass(dex::TypeIndex type_index, @@ -253,10 +254,10 @@ class HInstructionBuilder : public ValueObject { REQUIRES_SHARED(Locks::mutator_lock_); // Returns the outer-most compiling method's class. - mirror::Class* GetOutermostCompilingClass() const; + ObjPtr<mirror::Class> GetOutermostCompilingClass() const; // Returns the class whose method is being compiled. - mirror::Class* GetCompilingClass() const; + ObjPtr<mirror::Class> GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index bd20d28992..a42a85dc1d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1081,6 +1081,58 @@ static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataTy } } +// The type conversion is only used for storing into a field/element of the +// same/narrower size. +static bool IsTypeConversionForStoringIntoNoWiderFieldOnly(HTypeConversion* type_conversion) { + if (type_conversion->HasEnvironmentUses()) { + return false; + } + DataType::Type input_type = type_conversion->GetInputType(); + DataType::Type result_type = type_conversion->GetResultType(); + if (!DataType::IsIntegralType(input_type) || + !DataType::IsIntegralType(result_type) || + input_type == DataType::Type::kInt64 || + result_type == DataType::Type::kInt64) { + // Type conversion is needed if non-integer types are involved, or 64-bit + // types are involved, which may use different number of registers. + return false; + } + if (DataType::Size(input_type) >= DataType::Size(result_type)) { + // Type conversion is not necessary when storing to a field/element of the + // same/smaller size. + } else { + // We do not handle this case here. + return false; + } + + // Check if the converted value is only used for storing into heap. + for (const HUseListNode<HInstruction*>& use : type_conversion->GetUses()) { + HInstruction* instruction = use.GetUser(); + if (instruction->IsInstanceFieldSet() && + instruction->AsInstanceFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsInstanceFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsStaticFieldSet() && + instruction->AsStaticFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsStaticFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsArraySet() && + instruction->AsArraySet()->GetComponentType() == result_type && + // not index use. + instruction->AsArraySet()->GetIndex() != type_conversion) { + DCHECK_EQ(instruction->AsArraySet()->GetValue(), type_conversion); + continue; + } + // The use is not as a store value, or the field/element type is not the + // same as the result_type, keep the type conversion. + return false; + } + // Codegen automatically handles the type conversion during the store. + return true; +} + void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) { HInstruction* input = instruction->GetInput(); DataType::Type input_type = input->GetType(); @@ -1168,16 +1220,13 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct RecordSimplification(); return; } - } else if (input->IsIntConstant()) { - // Try to eliminate type conversion on int constant whose value falls into - // the range of the result type. - int32_t value = input->AsIntConstant()->GetValue(); - if (DataType::IsTypeConversionImplicit(value, result_type)) { - instruction->ReplaceWith(input); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } + } + + if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) { + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; } } @@ -2045,7 +2094,9 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { optimizations.SetArgumentIsString(); } else if (kUseReadBarrier) { DCHECK(instruction->GetResolvedMethod() != nullptr); - DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass()); + DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass() || + // Object.equals() can be devirtualized to String.equals(). + instruction->GetResolvedMethod()->GetDeclaringClass()->IsObjectClass()); Runtime* runtime = Runtime::Current(); // For AOT, we always assume that the boot image shall contain the String.class and // we do not need a read barrier for boot image classes as they are non-moveable. @@ -2276,7 +2327,7 @@ void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { HArrayLength* length = new (allocator) HArrayLength(str, dex_pc, /* is_string_length */ true); invoke->GetBlock()->InsertInstructionBefore(length, invoke); HBoundsCheck* bounds_check = new (allocator) HBoundsCheck( - index, length, dex_pc, invoke->GetDexMethodIndex()); + index, length, dex_pc, /* is_string_char_at */ true); invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); HArrayGet* array_get = new (allocator) HArrayGet(str, bounds_check, diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h index 22cc2efc1a..6cb8affe85 100644 --- a/compiler/optimizing/instruction_simplifier_mips.h +++ b/compiler/optimizing/instruction_simplifier_mips.h @@ -30,7 +30,7 @@ namespace mips { class InstructionSimplifierMips : public HOptimization { public: InstructionSimplifierMips(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "instruction_simplifier_mips", stats), + : HOptimization(graph, kInstructionSimplifierMipsPassName, stats), codegen_(down_cast<CodeGeneratorMIPS*>(codegen)) {} static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips"; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 9bf10f58fd..6928b70df7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -104,7 +104,8 @@ static inline IntrinsicExceptions GetExceptions(Intrinsics i) { return kCanThrow; } -static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { +static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) + REQUIRES_SHARED(Locks::mutator_lock_) { // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual. // // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization @@ -130,14 +131,13 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { } if (invoke_type == kVirtual) { ArtMethod* art_method = invoke->GetResolvedMethod(); - ScopedObjectAccess soa(Thread::Current()); return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal()); } return false; case kVirtual: // Call might be devirtualized. - return (invoke_type == kVirtual || invoke_type == kDirect); + return (invoke_type == kVirtual || invoke_type == kDirect || invoke_type == kInterface); case kSuper: case kInterface: @@ -148,19 +148,21 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { UNREACHABLE(); } -bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) { - ArtMethod* art_method = invoke->GetResolvedMethod(); +bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, + ArtMethod* art_method, + /*out*/ bool* wrong_invoke_type) { + if (art_method == nullptr) { + art_method = invoke->GetResolvedMethod(); + } *wrong_invoke_type = false; if (art_method == nullptr || !art_method->IsIntrinsic()) { return false; } - { - // TODO: b/65872996 Polymorphic signature methods should be compiler intrinsics. - ScopedObjectAccess soa(Thread::Current()); - if (art_method->IsPolymorphicSignature()) { - return false; - } + // TODO: b/65872996 The intent is that polymorphic signature methods should + // be compiler intrinsics. At present, they are only interpreter intrinsics. + if (art_method->IsPolymorphicSignature()) { + return false; } Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic()); @@ -184,7 +186,7 @@ void IntrinsicsRecognizer::Run() { HInstruction* inst = inst_it.Current(); if (inst->IsInvoke()) { bool wrong_invoke_type = false; - if (Recognize(inst->AsInvoke(), &wrong_invoke_type)) { + if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } else if (wrong_invoke_type) { LOG(WARNING) diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 8088ab25a7..62991435c7 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -46,8 +46,9 @@ class IntrinsicsRecognizer : public HOptimization { // Static helper that recognizes intrinsic call. Returns true on success. // If it fails due to invoke type mismatch, wrong_invoke_type is set. - // Useful to recognize intrinsics on invidual calls outside this full pass. - static bool Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type); + // Useful to recognize intrinsics on individual calls outside this full pass. + static bool Recognize(HInvoke* invoke, ArtMethod* method, /*out*/ bool* wrong_invoke_type) + REQUIRES_SHARED(Locks::mutator_lock_); static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 89ad85e0b4..88326d321b 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -74,25 +74,116 @@ class LSEVisitor : public HGraphDelegateVisitor { HGraphVisitor::VisitBasicBlock(block); } + HTypeConversion* AddTypeConversionIfNecessary(HInstruction* instruction, + HInstruction* value, + DataType::Type expected_type) { + HTypeConversion* type_conversion = nullptr; + // Should never add type conversion into boolean value. + if (expected_type != DataType::Type::kBool && + !DataType::IsTypeConversionImplicit(value->GetType(), expected_type)) { + type_conversion = new (GetGraph()->GetAllocator()) HTypeConversion( + expected_type, value, instruction->GetDexPc()); + instruction->GetBlock()->InsertInstructionBefore(type_conversion, instruction); + } + return type_conversion; + } + + // Find an instruction's substitute if it should be removed. + // Return the same instruction if it should not be removed. + HInstruction* FindSubstitute(HInstruction* instruction) { + size_t size = removed_loads_.size(); + for (size_t i = 0; i < size; i++) { + if (removed_loads_[i] == instruction) { + return substitute_instructions_for_loads_[i]; + } + } + return instruction; + } + + void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) { + DCHECK_EQ(FindSubstitute(heap_value), heap_value) << + "Unexpected heap_value that has a substitute " << heap_value->DebugName(); + removed_loads_.push_back(load); + substitute_instructions_for_loads_.push_back(heap_value); + } + + // Scan the list of removed loads to see if we can reuse `type_conversion`, if + // the other removed load has the same substitute and type and is dominated + // by `type_conversioni`. + void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) { + size_t size = removed_loads_.size(); + HInstruction* load = removed_loads_[index]; + HInstruction* substitute = substitute_instructions_for_loads_[index]; + for (size_t j = index + 1; j < size; j++) { + HInstruction* load2 = removed_loads_[j]; + HInstruction* substitute2 = substitute_instructions_for_loads_[j]; + if (load2 == nullptr) { + DCHECK(substitute2->IsTypeConversion()); + continue; + } + DCHECK(load2->IsInstanceFieldGet() || + load2->IsStaticFieldGet() || + load2->IsArrayGet()); + DCHECK(substitute2 != nullptr); + if (substitute2 == substitute && + load2->GetType() == load->GetType() && + type_conversion->GetBlock()->Dominates(load2->GetBlock()) && + // Don't share across irreducible loop headers. + // TODO: can be more fine-grained than this by testing each dominator. + (load2->GetBlock() == type_conversion->GetBlock() || + !GetGraph()->HasIrreducibleLoops())) { + // The removed_loads_ are added in reverse post order. + DCHECK(type_conversion->StrictlyDominates(load2)); + load2->ReplaceWith(type_conversion); + load2->GetBlock()->RemoveInstruction(load2); + removed_loads_[j] = nullptr; + substitute_instructions_for_loads_[j] = type_conversion; + } + } + } + // Remove recorded instructions that should be eliminated. void RemoveInstructions() { size_t size = removed_loads_.size(); DCHECK_EQ(size, substitute_instructions_for_loads_.size()); for (size_t i = 0; i < size; i++) { HInstruction* load = removed_loads_[i]; - DCHECK(load != nullptr); + if (load == nullptr) { + // The load has been handled in the scan for type conversion below. + DCHECK(substitute_instructions_for_loads_[i]->IsTypeConversion()); + continue; + } DCHECK(load->IsInstanceFieldGet() || load->IsStaticFieldGet() || load->IsArrayGet()); HInstruction* substitute = substitute_instructions_for_loads_[i]; DCHECK(substitute != nullptr); - // Keep tracing substitute till one that's not removed. - HInstruction* sub_sub = FindSubstitute(substitute); - while (sub_sub != substitute) { - substitute = sub_sub; - sub_sub = FindSubstitute(substitute); + // We proactively retrieve the substitute for a removed load, so + // a load that has a substitute should not be observed as a heap + // location value. + DCHECK_EQ(FindSubstitute(substitute), substitute); + + // The load expects to load the heap value as type load->GetType(). + // However the tracked heap value may not be of that type. An explicit + // type conversion may be needed. + // There are actually three types involved here: + // (1) tracked heap value's type (type A) + // (2) heap location (field or element)'s type (type B) + // (3) load's type (type C) + // We guarantee that type A stored as type B and then fetched out as + // type C is the same as casting from type A to type C directly, since + // type B and type C will have the same size which is guarenteed in + // HInstanceFieldGet/HStaticFieldGet/HArrayGet's SetType(). + // So we only need one type conversion from type A to type C. + HTypeConversion* type_conversion = AddTypeConversionIfNecessary( + load, substitute, load->GetType()); + if (type_conversion != nullptr) { + TryToReuseTypeConversion(type_conversion, i); + load->ReplaceWith(type_conversion); + substitute_instructions_for_loads_[i] = type_conversion; + } else { + load->ReplaceWith(substitute); } - load->ReplaceWith(substitute); load->GetBlock()->RemoveInstruction(load); } @@ -328,8 +419,7 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* heap_value = heap_values[idx]; if (heap_value == kDefaultHeapValue) { HInstruction* constant = GetDefaultValue(instruction->GetType()); - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(constant); + AddRemovedLoad(instruction, constant); heap_values[idx] = constant; return; } @@ -342,6 +432,8 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(ref_info->IsSingleton()); // Get the real heap value of the store. heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2); + // heap_value may already have a substitute. + heap_value = FindSubstitute(heap_value); } } if (heap_value == kUnknownHeapValue) { @@ -362,8 +454,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } return; } - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(heap_value); + AddRemovedLoad(instruction, heap_value); TryRemovingNullCheck(instruction); } } @@ -385,6 +476,8 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t vector_length, int16_t declaring_class_def_index, HInstruction* value) { + // value may already have a substitute. + value = FindSubstitute(value); HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); size_t idx = heap_location_collector_.FindHeapLocationIndex( @@ -679,18 +772,6 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - // Find an instruction's substitute if it should be removed. - // Return the same instruction if it should not be removed. - HInstruction* FindSubstitute(HInstruction* instruction) { - size_t size = removed_loads_.size(); - for (size_t i = 0; i < size; i++) { - if (removed_loads_[i] == instruction) { - return substitute_instructions_for_loads_[i]; - } - } - return instruction; - } - const HeapLocationCollector& heap_location_collector_; const SideEffectsAnalysis& side_effects_; diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1ca096035e..3dc1ef7534 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1749,7 +1749,8 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) { if (instruction->IsPhi()) { HInstruction* input = instruction->InputAt(1); - if (input->IsVecOperation() && !input->IsVecExtractScalar()) { + if (HVecOperation::ReturnsSIMDValue(input)) { + DCHECK(!input->IsPhi()); HVecOperation* input_vector = input->AsVecOperation(); uint32_t vector_length = input_vector->GetVectorLength(); DataType::Type type = input_vector->GetPackedType(); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index fa580d9bed..5f33ed6303 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -507,6 +507,7 @@ GraphAnalysisResult HGraph::AnalyzeLoops() const { if (block->IsCatchBlock()) { // TODO: Dealing with exceptional back edges could be tricky because // they only approximate the real control flow. Bail out for now. + VLOG(compiler) << "Not compiled: Exceptional back edges"; return kAnalysisFailThrowCatchLoop; } block->GetLoopInformation()->Populate(); @@ -1403,6 +1404,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { if (GetInput()->IsIntConstant()) { int32_t value = GetInput()->AsIntConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast<int8_t>(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc()); case DataType::Type::kInt64: return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc()); case DataType::Type::kFloat32: @@ -1415,6 +1424,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { } else if (GetInput()->IsLongConstant()) { int64_t value = GetInput()->AsLongConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast<int8_t>(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc()); case DataType::Type::kInt32: return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc()); case DataType::Type::kFloat32: @@ -2814,21 +2831,6 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadClass::SetLoadKind(LoadKind load_kind) { - SetPackedField<LoadKindField>(load_kind); - - if (load_kind != LoadKind::kRuntimeCall && - load_kind != LoadKind::kReferrersClass) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { switch (rhs) { case HLoadClass::LoadKind::kReferrersClass: @@ -2871,21 +2873,6 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadString::SetLoadKind(LoadKind load_kind) { - // Once sharpened, the load kind should not be changed again. - DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); - SetPackedField<LoadKindField>(load_kind); - - if (load_kind != LoadKind::kRuntimeCall) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { switch (rhs) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 66d5bfea32..42a9d95b9a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4614,7 +4614,6 @@ class HInvokeInterface FINAL : public HInvoke { } uint32_t GetImtIndex() const { return imt_index_; } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } DECLARE_INSTRUCTION(InvokeInterface); @@ -5787,10 +5786,10 @@ class HBoundsCheck FINAL : public HExpression<2> { HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc, - bool string_char_at = false) + bool is_string_char_at = false) : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType())); - SetPackedFlag<kFlagIsStringCharAt>(string_char_at); + SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } @@ -6062,6 +6061,20 @@ class HLoadClass FINAL : public HInstruction { std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); // Note: defined outside class to see operator<<(., HLoadClass::LoadKind). +inline void HLoadClass::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + SetPackedField<LoadKindField>(load_kind); + if (load_kind != LoadKind::kRuntimeCall && load_kind != LoadKind::kReferrersClass) { + special_input_ = HUserRecord<HInstruction*>(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + +// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, // including literal pool loads, which are PC-relative too. @@ -6209,6 +6222,21 @@ class HLoadString FINAL : public HInstruction { std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs); // Note: defined outside class to see operator<<(., HLoadString::LoadKind). +inline void HLoadString::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); + SetPackedField<LoadKindField>(load_kind); + if (load_kind != LoadKind::kRuntimeCall) { + special_input_ = HUserRecord<HInstruction*>(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + +// Note: defined outside class to see operator<<(., HLoadString::LoadKind). inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, // including literal pool loads, which are PC-relative too. diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 59d5b9f847..096349fd73 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -150,6 +150,19 @@ class HVecOperation : public HVariableInputSizeInstruction { } } + // Helper method to determine if an instruction returns a SIMD value. + // TODO: This method is needed until we introduce SIMD as proper type. + static bool ReturnsSIMDValue(HInstruction* instruction) { + if (instruction->IsVecOperation()) { + return !instruction->IsVecExtractScalar(); // only scalar returning vec op + } else if (instruction->IsPhi()) { + return + instruction->GetType() == kSIMDType && + instruction->InputAt(1)->IsVecOperation(); // vectorizer does not go deeper + } + return false; + } + DECLARE_ABSTRACT_INSTRUCTION(VecOperation); protected: @@ -879,7 +892,7 @@ class HVecSetScalars FINAL : public HVecOperation { vector_length, dex_pc) { for (size_t i = 0; i < number_of_scalars; i++) { - DCHECK(!scalars[i]->IsVecOperation() || scalars[i]->IsVecExtractScalar()); + DCHECK(!ReturnsSIMDValue(scalars[i])); SetRawInputAt(0, scalars[i]); } } diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 7edb642c5b..7149d93d07 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -258,8 +258,7 @@ ArenaVector<HOptimization*> ConstructOptimizations( break; } case OptimizationPass::kSharpening: - opt = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles, name); + opt = new (allocator) HSharpening(graph, codegen, driver, name); break; case OptimizationPass::kSelectGenerator: opt = new (allocator) HSelectGenerator(graph, handles, stats, name); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index a281c4a310..73c72fc57a 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,7 +1196,69 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); + + if (UNLIKELY(method->IsNative())) { + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, *dex_file); + ScopedNullHandle<mirror::ObjectArray<mirror::Object>> roots; + ArenaSet<ArtMethod*, std::less<ArtMethod*>> cha_single_implementation_list( + allocator.Adapter(kArenaAllocCHA)); + const void* code = code_cache->CommitCode( + self, + method, + /* stack_map_data */ nullptr, + /* method_info_data */ nullptr, + /* roots_data */ nullptr, + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + jni_compiled_method.GetCode().data(), + jni_compiled_method.GetCode().size(), + /* data_size */ 0u, + osr, + roots, + /* has_should_deoptimize_flag */ false, + cha_single_implementation_list); + if (code == nullptr) { + return false; + } + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { + const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); + const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); + debug::MethodDebugInfo info = {}; + DCHECK(info.trampoline_name.empty()); + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = jni_compiled_method.GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = jni_compiled_method.GetCode().size(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = nullptr; + info.cfi = jni_compiled_method.GetCfi(); + std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForMethods( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + ArrayRef<const debug::MethodDebugInfo>(&info, 1)); + CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); + } + + Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); + } + return true; + } + + ArenaStack arena_stack(runtime->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index a2e92d2931..32a94ab5e4 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -23,6 +23,7 @@ #include <type_traits> #include "atomic.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "globals.h" namespace art { diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 7246129e25..8bb124e066 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -544,7 +544,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst // the method is from the String class, the null loader is good enough. Handle<mirror::ClassLoader> loader(hs.NewHandle<mirror::ClassLoader>(nullptr)); ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + invoke->GetDexMethodIndex(), dex_cache, loader, /* referrer */ nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); @@ -576,8 +576,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache, class_loader_.Get()); SetClassAsTypeInfo(instr, klass, is_exact); } @@ -612,7 +612,7 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio // The field is unknown only during tests. if (info.GetField() != nullptr) { - klass = info.GetField()->LookupType(); + klass = info.GetField()->LookupResolvedType(); } SetClassAsTypeInfo(instr, klass, /* is_exact */ false); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index e46c9a7081..1e49411c72 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -45,8 +45,6 @@ void HSharpening::Run() { SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_, compiler_driver_); - } else if (instruction->IsLoadString()) { - ProcessLoadString(instruction->AsLoadString()); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data @@ -147,10 +145,11 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, invoke->SetDispatchInfo(dispatch_info); } -HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) { +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( + HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { Handle<mirror::Class> klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kRuntimeCall || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) @@ -237,7 +236,12 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, return load_kind; } -void HSharpening::ProcessLoadString(HLoadString* load_string) { +void HSharpening::ProcessLoadString( + HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall); const DexFile& dex_file = load_string->GetDexFile(); @@ -249,27 +253,27 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { ClassLinker* class_linker = runtime->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() + Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *dex_compilation_unit.GetDexFile()) + ? dex_compilation_unit.GetDexCache() : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::String* string = nullptr; + ObjPtr<mirror::String> string = nullptr; - if (codegen_->GetCompilerOptions().IsBootImage()) { + if (codegen->GetCompilerOptions().IsBootImage()) { // Compiling boot image. Resolve the string and allocate it if needed, to ensure // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); - string = class_linker->ResolveString(dex_file, string_index, dex_cache); + string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr); - if (compiler_driver_->GetSupportBootImageFixup()) { - DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); + if (compiler_driver->GetSupportBootImageFixup()) { + DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); desired_load_kind = HLoadString::LoadKind::kBootImageLinkTimePcRelative; } else { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadString::LoadKind::kRuntimeCall; } } else if (runtime->UseJitCompilation()) { - DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); + string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr) { if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -281,9 +285,9 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } } else { // AOT app compilation. Try to lookup the string without allocating if not found. - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - if (codegen_->GetCompilerOptions().GetCompilePic()) { + if (codegen->GetCompilerOptions().GetCompilePic()) { desired_load_kind = HLoadString::LoadKind::kBootImageInternTable; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -293,12 +297,12 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } } if (string != nullptr) { - load_string->SetString(handles_->NewHandle(string)); + load_string->SetString(handles->NewHandle(string)); } } DCHECK_NE(desired_load_kind, static_cast<HLoadString::LoadKind>(-1)); - HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); + HLoadString::LoadKind load_kind = codegen->GetSupportedLoadStringKind(desired_load_kind); load_string->SetLoadKind(load_kind); } diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index bb1954eeeb..6df7d6d91e 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -34,26 +34,29 @@ class HSharpening : public HOptimization { public: HSharpening(HGraph* graph, CodeGenerator* codegen, - const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles, const char* name = kSharpeningPassName) : HOptimization(graph, name), codegen_(codegen), - compilation_unit_(compilation_unit), - compiler_driver_(compiler_driver), - handles_(handles) { } + compiler_driver_(compiler_driver) { } void Run() OVERRIDE; static constexpr const char* kSharpeningPassName = "sharpening"; + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); + // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, CodeGenerator* codegen, CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit) - REQUIRES_SHARED(Locks::mutator_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Used by Sharpening and InstructionSimplifier. static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, @@ -61,12 +64,8 @@ class HSharpening : public HOptimization { CompilerDriver* compiler_driver); private: - void ProcessLoadString(HLoadString* load_string); - CodeGenerator* codegen_; - const DexCompilationUnit& compilation_unit_; CompilerDriver* compiler_driver_; - VariableSizedHandleScope* handles_; }; } // namespace art diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index e4edbfdc24..cb384768b7 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -328,6 +328,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aget_int->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aget_int->GetDexPc(); return false; } @@ -368,6 +370,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aset->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aset->GetDexPc(); return false; } diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 9ab7a89b33..f6bd05269e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -474,9 +474,10 @@ size_t LiveInterval::NumberOfSpillSlotsNeeded() const { // For a SIMD operation, compute the number of needed spill slots. // TODO: do through vector type? HInstruction* definition = GetParent()->GetDefinedBy(); - if (definition != nullptr && - definition->IsVecOperation() && - !definition->IsVecExtractScalar()) { + if (definition != nullptr && HVecOperation::ReturnsSIMDValue(definition)) { + if (definition->IsPhi()) { + definition = definition->InputAt(1); // SIMD always appears on back-edge + } return definition->AsVecOperation()->GetVectorNumberOfBytes() / kVRegSize; } // Return number of needed spill slots based on type. |