Small optimization improvements.
- Tune CanBeNull for HBoundType.
- Remove LoadClass when we know the class is loaded.
- Tune CanBeNull for StringInit.
Change-Id: I564ed33a506d65e991a514342bdfd1610bed0cf5
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 931a1c3..df6e550 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -238,12 +238,19 @@
}
bool outcome;
- if (TypeCheckHasKnownOutcome(check_cast->InputAt(1)->AsLoadClass(), object, &outcome)) {
+ HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+ if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
if (outcome) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
if (stats_ != nullptr) {
stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
}
+ if (!load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the checkcast was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
+ }
} else {
// Don't do anything for exceptional cases for now. Ideally we should remove
// all instructions and blocks this instruction dominates.
@@ -268,7 +275,8 @@
}
bool outcome;
- if (TypeCheckHasKnownOutcome(instruction->InputAt(1)->AsLoadClass(), object, &outcome)) {
+ HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass();
+ if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
if (outcome && can_be_null) {
// Type test will succeed, we just need a null test.
HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object);
@@ -280,6 +288,12 @@
}
RecordSimplification();
instruction->GetBlock()->RemoveInstruction(instruction);
+ if (outcome && !load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the instanceof check was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
+ }
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index dba9233..efb7089 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3023,6 +3023,10 @@
return false;
}
+ bool CanBeNull() const OVERRIDE {
+ return return_type_ == Primitive::kPrimNot && !IsStringInit();
+ }
+
InvokeType GetInvokeType() const { return invoke_type_; }
bool IsRecursive() const { return is_recursive_; }
bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index d1c1134..1349df9 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -167,6 +167,9 @@
bound_type->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
}
+ if (upper_can_be_null) {
+ bound_type->SetCanBeNull(obj->CanBeNull());
+ }
return bound_type;
}
@@ -485,6 +488,10 @@
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.
diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java
index bff9c72..2eac6c9 100644
--- a/test/494-checker-instanceof-tests/src/Main.java
+++ b/test/494-checker-instanceof-tests/src/Main.java
@@ -129,6 +129,26 @@
return $inline$interfaceTypeTest(finalUnrelatedField);
}
+ // Check that we remove the LoadClass instruction from the graph.
+ /// CHECK-START: boolean Main.knownTestWithLoadedClass() register (after)
+ /// CHECK-NOT: LoadClass
+ public static boolean knownTestWithLoadedClass() {
+ return new String() instanceof String;
+ }
+
+ // Check that we do not remove the LoadClass instruction from the graph.
+ /// CHECK-START: boolean Main.knownTestWithUnloadedClass() register (after)
+ /// CHECK: <<Const:i\d+>> IntConstant 0
+ /// CHECK: LoadClass
+ /// CHECK: Return [<<Const>>]
+ public static boolean knownTestWithUnloadedClass() {
+ return $inline$returnMain() instanceof String;
+ }
+
+ public static Object $inline$returnMain() {
+ return new Main();
+ }
+
public static void expect(boolean expected, boolean actual) {
if (expected != actual) {
throw new Error("Unexpected result");
diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java
index aa6d5a7..4b2bf09 100644
--- a/test/495-checker-checkcast-tests/src/Main.java
+++ b/test/495-checker-checkcast-tests/src/Main.java
@@ -112,6 +112,33 @@
return $inline$interfaceTypeTest(finalUnrelatedField);
}
+ /// CHECK-START: java.lang.String Main.knownTestWithLoadedClass() register (after)
+ /// CHECK-NOT: LoadClass
+ public static String knownTestWithLoadedClass() {
+ return (String)$inline$getString();
+ }
+
+ /// CHECK-START: Itf Main.knownTestWithUnloadedClass() register (after)
+ /// CHECK: LoadClass
+ public static Itf knownTestWithUnloadedClass() {
+ return (Itf)$inline$getString();
+ }
+
+ public static Object $inline$getString() {
+ return new String();
+ }
+
+ public static Object $inline$getMain() {
+ return new Main();
+ }
+
+ /// CHECK-START: void Main.nonNullBoundType() register (after)
+ /// CHECK-NOT: NullCheck
+ public static void nonNullBoundType() {
+ Main main = (Main)$inline$getMain();
+ main.getClass();
+ }
+
public static void main(String[] args) {
classTypeTestNull();
try {