summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/block_builder.cc3
-rw-r--r--compiler/optimizing/code_generator.cc42
-rw-r--r--compiler/optimizing/code_generator_mips.cc147
-rw-r--r--compiler/optimizing/code_generator_mips64.cc209
-rw-r--r--compiler/optimizing/code_generator_utils.cc5
-rw-r--r--compiler/optimizing/constant_folding.cc2
-rw-r--r--compiler/optimizing/data_type.h3
-rw-r--r--compiler/optimizing/inliner.cc67
-rw-r--r--compiler/optimizing/instruction_builder.cc82
-rw-r--r--compiler/optimizing/instruction_builder.h15
-rw-r--r--compiler/optimizing/instruction_simplifier.cc75
-rw-r--r--compiler/optimizing/instruction_simplifier_mips.h2
-rw-r--r--compiler/optimizing/intrinsics.cc26
-rw-r--r--compiler/optimizing/intrinsics.h5
-rw-r--r--compiler/optimizing/load_store_elimination.cc127
-rw-r--r--compiler/optimizing/loop_optimization.cc3
-rw-r--r--compiler/optimizing/nodes.cc47
-rw-r--r--compiler/optimizing/nodes.h34
-rw-r--r--compiler/optimizing/nodes_vector.h15
-rw-r--r--compiler/optimizing/optimization.cc3
-rw-r--r--compiler/optimizing/optimizing_compiler.cc64
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h1
-rw-r--r--compiler/optimizing/reference_type_propagation.cc8
-rw-r--r--compiler/optimizing/sharpening.cc44
-rw-r--r--compiler/optimizing/sharpening.h19
-rw-r--r--compiler/optimizing/ssa_builder.cc4
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc7
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.