summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc5
-rw-r--r--compiler/optimizing/graph_checker.cc12
-rw-r--r--compiler/optimizing/graph_checker.h1
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc6
-rw-r--r--compiler/optimizing/licm_test.cc20
-rw-r--r--compiler/optimizing/load_store_elimination.cc28
-rw-r--r--compiler/optimizing/nodes.cc27
-rw-r--r--compiler/optimizing/nodes.h25
-rw-r--r--compiler/optimizing/optimizing_compiler.cc4
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h4
-rw-r--r--compiler/optimizing/reference_type_propagation.cc124
-rw-r--r--compiler/optimizing/ssa_builder.cc96
-rw-r--r--compiler/optimizing/ssa_builder.h11
13 files changed, 207 insertions, 156 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 4dd0d26b89..1af684683b 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -1817,7 +1817,12 @@ void HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
UpdateLocal(destination, current_block_->GetLastInstruction(), dex_pc);
} else {
DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
+ // We emit a CheckCast followed by a BoundType. CheckCast is a statement
+ // which may throw. If it succeeds BoundType sets the new type of `object`
+ // for all subsequent uses.
current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
+ current_block_->AddInstruction(new (arena_) HBoundType(object, dex_pc));
+ UpdateLocal(reference, current_block_->GetLastInstruction(), dex_pc);
}
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 3b93b2b571..6d0bdbe19b 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -921,4 +921,16 @@ void SSAChecker::VisitConstant(HConstant* instruction) {
}
}
+void SSAChecker::VisitBoundType(HBoundType* instruction) {
+ VisitInstruction(instruction);
+
+ ScopedObjectAccess soa(Thread::Current());
+ if (!instruction->GetUpperBound().IsValid()) {
+ AddError(StringPrintf(
+ "%s %d does not have a valid upper bound RTI.",
+ instruction->DebugName(),
+ instruction->GetId()));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index d5ddbabc8c..2e16bfe245 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -128,6 +128,7 @@ class SSAChecker : public GraphChecker {
void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE;
void VisitConstant(HConstant* instruction) OVERRIDE;
+ void VisitBoundType(HBoundType* instruction) OVERRIDE;
void HandleBooleanInput(HInstruction* instruction, size_t input_index);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 776c115e9d..29a1845658 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -85,6 +85,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
constant0_ = graph_->GetIntConstant(0);
constant1_ = graph_->GetIntConstant(1);
constant100_ = graph_->GetIntConstant(100);
+ float_constant0_ = graph_->GetFloatConstant(0.0f);
induc_ = new (&allocator_) HLocal(n);
entry_->AddInstruction(induc_);
entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_));
@@ -156,8 +157,10 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
HInstruction* InsertArrayStore(HLocal* subscript, int d) {
HInstruction* load = InsertInstruction(
new (&allocator_) HLoadLocal(subscript, Primitive::kPrimInt), d);
+ // ArraySet is given a float value in order to avoid SsaBuilder typing
+ // it from the array's non-existent reference type info.
return InsertInstruction(new (&allocator_) HArraySet(
- parameter_, load, constant0_, Primitive::kPrimInt, 0), d);
+ parameter_, load, float_constant0_, Primitive::kPrimFloat, 0), d);
}
// Returns induction information of instruction in loop at depth d.
@@ -187,6 +190,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
HInstruction* constant0_;
HInstruction* constant1_;
HInstruction* constant100_;
+ HInstruction* float_constant0_;
HLocal* induc_; // "vreg_n", the "k"
HLocal* tmp_; // "vreg_n+1"
HLocal* dum_; // "vreg_n+2"
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index aa60fd646d..2b63ec8971 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -65,7 +65,8 @@ class LICMTest : public CommonCompilerTest {
// Provide boiler-plate instructions.
parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);
entry_->AddInstruction(parameter_);
- constant_ = graph_->GetIntConstant(42);
+ int_constant_ = graph_->GetIntConstant(42);
+ float_constant_ = graph_->GetFloatConstant(42.0f);
loop_preheader_->AddInstruction(new (&allocator_) HGoto());
loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
loop_body_->AddInstruction(new (&allocator_) HGoto());
@@ -95,7 +96,8 @@ class LICMTest : public CommonCompilerTest {
HBasicBlock* exit_;
HInstruction* parameter_; // "this"
- HInstruction* constant_;
+ HInstruction* int_constant_;
+ HInstruction* float_constant_;
};
//
@@ -118,7 +120,7 @@ TEST_F(LICMTest, FieldHoisting) {
0);
loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
- parameter_, constant_, Primitive::kPrimInt, MemberOffset(20),
+ parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20),
false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0);
loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
@@ -167,11 +169,13 @@ TEST_F(LICMTest, ArrayHoisting) {
BuildLoop();
// Populate the loop with instructions: set/get array with different types.
+ // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+ // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimByte, 0);
+ parameter_, int_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, constant_, constant_, Primitive::kPrimShort, 0);
+ parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
@@ -185,11 +189,13 @@ TEST_F(LICMTest, NoArrayHoisting) {
BuildLoop();
// Populate the loop with instructions: set/get array with same types.
+ // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+ // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimByte, 0);
+ parameter_, int_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, get_array, constant_, Primitive::kPrimByte, 0);
+ parameter_, get_array, float_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 727f2bb717..2b313f6b81 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -678,16 +678,6 @@ class LSEVisitor : public HGraphVisitor {
}
}
- static bool IsIntFloatAlias(Primitive::Type type1, Primitive::Type type2) {
- return (type1 == Primitive::kPrimFloat && type2 == Primitive::kPrimInt) ||
- (type2 == Primitive::kPrimFloat && type1 == Primitive::kPrimInt);
- }
-
- static bool IsLongDoubleAlias(Primitive::Type type1, Primitive::Type type2) {
- return (type1 == Primitive::kPrimDouble && type2 == Primitive::kPrimLong) ||
- (type2 == Primitive::kPrimDouble && type1 == Primitive::kPrimLong);
- }
-
void VisitGetLocation(HInstruction* instruction,
HInstruction* ref,
size_t offset,
@@ -716,22 +706,14 @@ class LSEVisitor : public HGraphVisitor {
// Get the real heap value of the store.
heap_value = store->InputAt(1);
}
- if ((heap_value != kUnknownHeapValue) &&
- // Keep the load due to possible I/F, J/D array aliasing.
- // See b/22538329 for details.
- !IsIntFloatAlias(heap_value->GetType(), instruction->GetType()) &&
- !IsLongDoubleAlias(heap_value->GetType(), instruction->GetType())) {
- removed_loads_.push_back(instruction);
- substitute_instructions_for_loads_.push_back(heap_value);
- TryRemovingNullCheck(instruction);
- return;
- }
-
- // Load isn't eliminated.
if (heap_value == kUnknownHeapValue) {
- // Put the load as the value into the HeapLocation.
+ // Load isn't eliminated. Put the load as the value into the HeapLocation.
// This acts like GVN but with better aliasing analysis.
heap_values[idx] = instruction;
+ } else {
+ removed_loads_.push_back(instruction);
+ substitute_instructions_for_loads_.push_back(heap_value);
+ TryRemovingNullCheck(instruction);
}
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fc12224783..c85e573557 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2060,6 +2060,16 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) {
new_pre_header->SetTryCatchInformation(try_catch_info);
}
+static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo upper_bound_rti)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (rti.IsValid()) {
+ DCHECK(upper_bound_rti.IsSupertypeOf(rti))
+ << " upper_bound_rti: " << upper_bound_rti
+ << " rti: " << rti;
+ DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact());
+ }
+}
+
void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
if (kIsDebugBuild) {
DCHECK_EQ(GetType(), Primitive::kPrimNot);
@@ -2068,16 +2078,23 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
if (IsBoundType()) {
// Having the test here spares us from making the method virtual just for
// the sake of a DCHECK.
- ReferenceTypeInfo upper_bound_rti = AsBoundType()->GetUpperBound();
- DCHECK(upper_bound_rti.IsSupertypeOf(rti))
- << " upper_bound_rti: " << upper_bound_rti
- << " rti: " << rti;
- DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact());
+ CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound());
}
}
reference_type_info_ = rti;
}
+void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) {
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(upper_bound.IsValid());
+ DCHECK(!upper_bound_.IsValid()) << "Upper bound should only be set once.";
+ CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound);
+ }
+ upper_bound_ = upper_bound;
+ upper_can_be_null_ = can_be_null;
+}
+
ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 5b072cf71c..1a7cbdeb5a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -101,7 +101,7 @@ enum IfCondition {
enum BuildSsaResult {
kBuildSsaFailNonNaturalLoop,
kBuildSsaFailThrowCatchLoop,
- kBuildSsaFailAmbiguousArrayGet,
+ kBuildSsaFailAmbiguousArrayOp,
kBuildSsaSuccess,
};
@@ -240,7 +240,7 @@ class ReferenceTypeInfo : ValueObject {
// Returns true if the type information provide the same amount of details.
// Note that it does not mean that the instructions have the same actual type
// (because the type can be the result of a merge).
- bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool IsEqual(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
if (!IsValid() && !rti.IsValid()) {
// Invalid types are equal.
return true;
@@ -5431,24 +5431,19 @@ class HInstanceOf : public HExpression<2> {
class HBoundType : public HExpression<1> {
public:
- // Constructs an HBoundType with the given upper_bound.
- // Ensures that the upper_bound is valid.
- HBoundType(HInstruction* input,
- ReferenceTypeInfo upper_bound,
- bool upper_can_be_null,
- uint32_t dex_pc = kNoDexPc)
+ HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc)
: HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc),
- upper_bound_(upper_bound),
- upper_can_be_null_(upper_can_be_null),
- can_be_null_(upper_can_be_null) {
+ upper_bound_(ReferenceTypeInfo::CreateInvalid()),
+ upper_can_be_null_(true),
+ can_be_null_(true) {
DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
SetRawInputAt(0, input);
- SetReferenceTypeInfo(upper_bound_);
}
- // GetUpper* should only be used in reference type propagation.
+ // {Get,Set}Upper* should only be used in reference type propagation.
const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
bool GetUpperCanBeNull() const { return upper_can_be_null_; }
+ void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null);
void SetCanBeNull(bool can_be_null) {
DCHECK(upper_can_be_null_ || !can_be_null);
@@ -5466,10 +5461,10 @@ class HBoundType : public HExpression<1> {
// if (x instanceof ClassX) {
// // uper_bound_ will be ClassX
// }
- const ReferenceTypeInfo upper_bound_;
+ ReferenceTypeInfo upper_bound_;
// Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
// is false then can_be_null_ cannot be true).
- const bool upper_can_be_null_;
+ bool upper_can_be_null_;
bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HBoundType);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3f9e151201..3eb72744ee 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -786,8 +786,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
case kBuildSsaFailThrowCatchLoop:
MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop);
break;
- case kBuildSsaFailAmbiguousArrayGet:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayGet);
+ case kBuildSsaFailAmbiguousArrayOp:
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp);
break;
case kBuildSsaSuccess:
UNREACHABLE();
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 4713514bb2..bca1632e31 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -40,7 +40,7 @@ enum MethodCompilationStat {
kNotCompiledBranchOutsideMethodCode,
kNotCompiledNonNaturalLoop,
kNotCompiledThrowCatchLoop,
- kNotCompiledAmbiguousArrayGet,
+ kNotCompiledAmbiguousArrayOp,
kNotCompiledHugeMethod,
kNotCompiledLargeMethodNoBranches,
kNotCompiledMalformedOpcode,
@@ -108,7 +108,7 @@ class OptimizingCompilerStats {
case kNotCompiledBranchOutsideMethodCode: name = "NotCompiledBranchOutsideMethodCode"; break;
case kNotCompiledNonNaturalLoop : name = "NotCompiledNonNaturalLoop"; break;
case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break;
- case kNotCompiledAmbiguousArrayGet : name = "NotCompiledAmbiguousArrayGet"; break;
+ case kNotCompiledAmbiguousArrayOp : name = "NotCompiledAmbiguousArrayOp"; break;
case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break;
case kNotCompiledLargeMethodNoBranches : name = "NotCompiledLargeMethodNoBranches"; break;
case kNotCompiledMalformedOpcode : name = "NotCompiledMalformedOpcode"; break;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 8113e65121..1c25e4824c 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -56,6 +56,7 @@ class RTPVisitor : public HGraphDelegateVisitor {
void VisitInvoke(HInvoke* instr) OVERRIDE;
void VisitArrayGet(HArrayGet* instr) OVERRIDE;
void VisitCheckCast(HCheckCast* instr) OVERRIDE;
+ void VisitBoundType(HBoundType* instr) OVERRIDE;
void VisitNullCheck(HNullCheck* instr) OVERRIDE;
void VisitFakeString(HFakeString* instr) OVERRIDE;
void UpdateReferenceTypeInfo(HInstruction* instr,
@@ -160,34 +161,6 @@ void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
BoundTypeForIfInstanceOf(block);
}
-// Create a bound type for the given object narrowing the type as much as possible.
-// The BoundType upper values for the super type and can_be_null will be taken from
-// load_class.GetLoadedClassRTI() and upper_can_be_null.
-static HBoundType* CreateBoundType(ArenaAllocator* arena,
- HInstruction* obj,
- HLoadClass* load_class,
- bool upper_can_be_null)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
- DCHECK(class_rti.IsValid());
- HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
- // Narrow the type as much as possible.
- if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
- bound_type->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
- } else if (obj_rti.IsValid() && class_rti.IsSupertypeOf(obj_rti)) {
- bound_type->SetReferenceTypeInfo(obj_rti);
- } else {
- bound_type->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
- }
- if (upper_can_be_null) {
- bound_type->SetCanBeNull(obj->CanBeNull());
- }
- return bound_type;
-}
-
// Check if we should create a bound type for the given object at the specified
// position. Because of inlining and the fact we run RTP more than once and we
// might have a HBoundType already. If we do, we should not create a new one.
@@ -273,8 +246,8 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
object_class_handle_, /* is_exact */ true);
if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
- bound_type = new (graph_->GetArena()) HBoundType(
- obj, object_rti, /* bound_can_be_null */ false);
+ bound_type = new (graph_->GetArena()) HBoundType(obj);
+ bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
if (obj->GetReferenceTypeInfo().IsValid()) {
bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
}
@@ -408,11 +381,8 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
ScopedObjectAccess soa(Thread::Current());
HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
- bound_type = CreateBoundType(
- graph_->GetArena(),
- obj,
- load_class,
- false /* InstanceOf ensures the object is not null. */);
+ bound_type = new (graph_->GetArena()) HBoundType(obj);
+ bound_type->SetUpperBound(class_rti, /* InstanceOf fails for null. */ false);
instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
} else {
// We already have a bound type on the position we would need to insert
@@ -602,43 +572,61 @@ void RTPVisitor::VisitFakeString(HFakeString* instr) {
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true));
}
+void RTPVisitor::VisitBoundType(HBoundType* instr) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ ReferenceTypeInfo class_rti = instr->GetUpperBound();
+ if (class_rti.IsValid()) {
+ // Narrow the type as much as possible.
+ HInstruction* obj = instr->InputAt(0);
+ ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
+ if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
+ } else if (obj_rti.IsValid()) {
+ if (class_rti.IsSupertypeOf(obj_rti)) {
+ // Object type is more specific.
+ instr->SetReferenceTypeInfo(obj_rti);
+ } else {
+ // Upper bound is more specific.
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
+ }
+ } else {
+ // Object not typed yet. Leave BoundType untyped for now rather than
+ // assign the type conservatively.
+ }
+ instr->SetCanBeNull(obj->CanBeNull() && instr->GetUpperCanBeNull());
+ } else {
+ // The owner of the BoundType was already visited. If the class is unresolved,
+ // the BoundType should have been removed from the data flow and this method
+ // should remove it from the graph.
+ DCHECK(!instr->HasUses());
+ instr->GetBlock()->RemoveInstruction(instr);
+ }
+}
+
void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+ ScopedObjectAccess soa(Thread::Current());
+
HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
- {
- ScopedObjectAccess soa(Thread::Current());
- if (!class_rti.IsValid()) {
- // He have loaded an unresolved class. Don't bother bounding the type.
- return;
- }
+ HBoundType* bound_type = check_cast->GetNext()->AsBoundType();
+ if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) {
+ // The next instruction is not an uninitialized BoundType. This must be
+ // an RTP pass after SsaBuilder and we do not need to do anything.
+ return;
}
- HInstruction* obj = check_cast->InputAt(0);
- HBoundType* bound_type = nullptr;
- for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
- if (check_cast->StrictlyDominates(user)) {
- if (bound_type == nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) {
- bound_type = CreateBoundType(
- GetGraph()->GetArena(),
- obj,
- load_class,
- true /* CheckCast succeeds for nulls. */);
- check_cast->GetBlock()->InsertInstructionAfter(bound_type, check_cast);
- } else {
- // Update nullability of the existing bound type, which may not have known
- // that its input was not null when it was being created.
- bound_type = check_cast->GetNext()->AsBoundType();
- bound_type->SetCanBeNull(obj->CanBeNull());
- // We already have a bound type on the position we would need to insert
- // the new one. The existing bound type should dominate all the users
- // (dchecked) so there's no need to continue.
- break;
- }
- }
- user->ReplaceInput(bound_type, it.Current()->GetIndex());
- }
+ DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0));
+
+ if (class_rti.IsValid()) {
+ // This is the first run of RTP and class is resolved.
+ bound_type->SetUpperBound(class_rti, /* CheckCast succeeds for nulls. */ true);
+ } else {
+ // This is the first run of RTP and class is unresolved. Remove the binding.
+ // The instruction itself is removed in VisitBoundType so as to not
+ // invalidate HInstructionIterator.
+ bound_type->ReplaceWith(bound_type->InputAt(0));
}
}
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 9ea352dde4..f6bab8efcb 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -317,27 +317,15 @@ static HArrayGet* CreateFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
return equivalent;
}
-// Returns true if the array input of `aget` is either of type int[] or long[].
-// Should only be called on ArrayGets with ambiguous type (int/float, long/double)
-// on arrays which were typed to an array class by RTP.
-static bool IsArrayGetOnIntegralArray(HArrayGet* aget) SHARED_REQUIRES(Locks::mutator_lock_) {
- ReferenceTypeInfo array_type = aget->GetArray()->GetReferenceTypeInfo();
+static Primitive::Type GetPrimitiveArrayComponentType(HInstruction* array)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ReferenceTypeInfo array_type = array->GetReferenceTypeInfo();
DCHECK(array_type.IsPrimitiveArrayClass());
- ReferenceTypeInfo::TypeHandle array_type_handle = array_type.GetTypeHandle();
-
- bool is_integral_type;
- if (Primitive::Is64BitType(aget->GetType())) {
- is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveLong();
- DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveDouble());
- } else {
- is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveInt();
- DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveFloat());
- }
- return is_integral_type;
+ return array_type.GetTypeHandle()->GetComponentType()->GetPrimitiveType();
}
-bool SsaBuilder::FixAmbiguousArrayGets() {
- if (ambiguous_agets_.empty()) {
+bool SsaBuilder::FixAmbiguousArrayOps() {
+ if (ambiguous_agets_.empty() && ambiguous_asets_.empty()) {
return true;
}
@@ -351,13 +339,17 @@ bool SsaBuilder::FixAmbiguousArrayGets() {
ScopedObjectAccess soa(Thread::Current());
for (HArrayGet* aget_int : ambiguous_agets_) {
- if (!aget_int->GetArray()->GetReferenceTypeInfo().IsPrimitiveArrayClass()) {
+ HInstruction* array = aget_int->GetArray();
+ if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) {
// RTP did not type the input array. Bail.
return false;
}
HArrayGet* aget_float = FindFloatOrDoubleEquivalentOfArrayGet(aget_int);
- if (IsArrayGetOnIntegralArray(aget_int)) {
+ Primitive::Type array_type = GetPrimitiveArrayComponentType(array);
+ DCHECK_EQ(Primitive::Is64BitType(aget_int->GetType()), Primitive::Is64BitType(array_type));
+
+ if (Primitive::IsIntOrLongType(array_type)) {
if (aget_float != nullptr) {
// There is a float/double equivalent. We must replace it and re-run
// primitive type propagation on all dependent instructions.
@@ -366,6 +358,7 @@ bool SsaBuilder::FixAmbiguousArrayGets() {
AddDependentInstructionsToWorklist(aget_int, &worklist);
}
} else {
+ DCHECK(Primitive::IsFloatingPointType(array_type));
if (aget_float == nullptr) {
// This is a float/double ArrayGet but there were no typed uses which
// would create the typed equivalent. Create it now.
@@ -379,11 +372,47 @@ bool SsaBuilder::FixAmbiguousArrayGets() {
AddDependentInstructionsToWorklist(aget_float, &worklist);
}
}
- }
- // Set a flag stating that types of ArrayGets have been resolved. This is used
- // by GetFloatOrDoubleEquivalentOfArrayGet to report conflict.
- agets_fixed_ = true;
+ // Set a flag stating that types of ArrayGets have been resolved. Requesting
+ // equivalent of the wrong type with GetFloatOrDoubleEquivalentOfArrayGet
+ // will fail from now on.
+ agets_fixed_ = true;
+
+ for (HArraySet* aset : ambiguous_asets_) {
+ HInstruction* array = aset->GetArray();
+ if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) {
+ // RTP did not type the input array. Bail.
+ return false;
+ }
+
+ HInstruction* value = aset->GetValue();
+ Primitive::Type value_type = value->GetType();
+ Primitive::Type array_type = GetPrimitiveArrayComponentType(array);
+ DCHECK_EQ(Primitive::Is64BitType(value_type), Primitive::Is64BitType(array_type));
+
+ if (Primitive::IsFloatingPointType(array_type)) {
+ if (!Primitive::IsFloatingPointType(value_type)) {
+ DCHECK(Primitive::IsIntegralType(value_type));
+ // Array elements are floating-point but the value has not been replaced
+ // with its floating-point equivalent. The replacement must always
+ // succeed in code validated by the verifier.
+ HInstruction* equivalent = GetFloatOrDoubleEquivalent(value, array_type);
+ DCHECK(equivalent != nullptr);
+ aset->ReplaceInput(equivalent, /* input_index */ 2);
+ if (equivalent->IsPhi()) {
+ // Returned equivalent is a phi which may not have had its inputs
+ // replaced yet. We need to run primitive type propagation on it.
+ worklist.push_back(equivalent->AsPhi());
+ }
+ }
+ } else {
+ // Array elements are integral and the value assigned to it initially
+ // was integral too. Nothing to do.
+ DCHECK(Primitive::IsIntegralType(array_type));
+ DCHECK(Primitive::IsIntegralType(value_type));
+ }
+ }
+ }
if (!worklist.empty()) {
ProcessPrimitiveTypePropagationWorklist(&worklist);
@@ -429,10 +458,11 @@ BuildSsaResult SsaBuilder::BuildSsa() {
ReferenceTypePropagation(GetGraph(), handles_).Run();
// 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float
- // or long/double). Now that RTP computed the type of the array input, the
- // ambiguity can be resolved and the correct equivalent kept.
- if (!FixAmbiguousArrayGets()) {
- return kBuildSsaFailAmbiguousArrayGet;
+ // or long/double) and marked ArraySets with ambiguous input type. Now that RTP
+ // computed the type of the array input, the ambiguity can be resolved and the
+ // correct equivalents kept.
+ if (!FixAmbiguousArrayOps()) {
+ return kBuildSsaFailAmbiguousArrayOp;
}
// 8) Mark dead phis. This will mark phis which are not used by instructions
@@ -702,7 +732,7 @@ HArrayGet* SsaBuilder::GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
// int/long. Requesting a float/double equivalent should lead to a conflict.
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
- DCHECK(IsArrayGetOnIntegralArray(aget));
+ DCHECK(Primitive::IsIntOrLongType(GetPrimitiveArrayComponentType(aget->GetArray())));
}
return nullptr;
} else {
@@ -847,4 +877,12 @@ void SsaBuilder::VisitArrayGet(HArrayGet* aget) {
VisitInstruction(aget);
}
+void SsaBuilder::VisitArraySet(HArraySet* aset) {
+ Primitive::Type type = aset->GetValue()->GetType();
+ if (Primitive::IsIntOrLongType(type)) {
+ ambiguous_asets_.push_back(aset);
+ }
+ VisitInstruction(aset);
+}
+
} // namespace art
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index ed6f5cab51..0fcc3a1306 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -56,6 +56,7 @@ class SsaBuilder : public HGraphVisitor {
current_locals_(nullptr),
loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
locals_for_(graph->GetBlocks().size(),
ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
@@ -75,6 +76,7 @@ class SsaBuilder : public HGraphVisitor {
void VisitInstruction(HInstruction* instruction);
void VisitTemporary(HTemporary* instruction);
void VisitArrayGet(HArrayGet* aget);
+ void VisitArraySet(HArraySet* aset);
static constexpr const char* kSsaBuilderPassName = "ssa_builder";
@@ -85,10 +87,10 @@ class SsaBuilder : public HGraphVisitor {
void EquivalentPhisCleanup();
void RunPrimitiveTypePropagation();
- // Attempts to resolve types of aget and aget-wide instructions from reference
- // type information on the input array. Returns false if the type of the array
- // is unknown.
- bool FixAmbiguousArrayGets();
+ // Attempts to resolve types of aget(-wide) instructions and type values passed
+ // to aput(-wide) instructions from reference type information on the array
+ // input. Returns false if the type of an array is unknown.
+ bool FixAmbiguousArrayOps();
bool TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist);
bool UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist);
@@ -115,6 +117,7 @@ class SsaBuilder : public HGraphVisitor {
ArenaVector<HBasicBlock*> loop_headers_;
ArenaVector<HArrayGet*> ambiguous_agets_;
+ ArenaVector<HArraySet*> ambiguous_asets_;
// HEnvironment for each block.
ArenaVector<ArenaVector<HInstruction*>> locals_for_;