Remove unnecessary null checks in CheckCast and InstanceOf
Change-Id: I6fd81cabd8673be360f369e6318df0de8b18b634
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 507b3cd..c506be1 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3874,9 +3874,11 @@
SlowPathCodeARM* slow_path = nullptr;
// Return 0 if `obj` is null.
- // TODO: avoid this check if we know obj is not null.
- __ cmp(obj, ShifterOperand(0));
- __ b(&zero, EQ);
+ // avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ cmp(obj, ShifterOperand(0));
+ __ b(&zero, EQ);
+ }
// Compare the class of `obj` with `cls`.
__ LoadFromOffset(kLoadWord, out, obj, class_offset);
__ cmp(out, ShifterOperand(cls));
@@ -3895,8 +3897,12 @@
__ LoadImmediate(out, 1);
__ b(&done);
}
- __ Bind(&zero);
- __ LoadImmediate(out, 0);
+
+ if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) {
+ __ Bind(&zero);
+ __ LoadImmediate(out, 0);
+ }
+
if (slow_path != nullptr) {
__ Bind(slow_path->GetExitLabel());
}
@@ -3922,9 +3928,11 @@
instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
- // TODO: avoid this check if we know obj is not null.
- __ cmp(obj, ShifterOperand(0));
- __ b(slow_path->GetExitLabel(), EQ);
+ // avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ cmp(obj, ShifterOperand(0));
+ __ b(slow_path->GetExitLabel(), EQ);
+ }
// Compare the class of `obj` with `cls`.
__ LoadFromOffset(kLoadWord, temp, obj, class_offset);
__ cmp(temp, ShifterOperand(cls));
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index f6ec729..3619656 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1373,8 +1373,10 @@
instruction, locations->InAt(1), LocationFrom(obj_cls), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
- // TODO: avoid this check if we know obj is not null.
- __ Cbz(obj, slow_path->GetExitLabel());
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ Cbz(obj, slow_path->GetExitLabel());
+ }
// Compare the class of `obj` with `cls`.
__ Ldr(obj_cls, HeapOperand(obj, mirror::Object::ClassOffset()));
__ Cmp(obj_cls, cls);
@@ -1821,9 +1823,11 @@
vixl::Label done;
// Return 0 if `obj` is null.
- // TODO: Avoid this check if we know `obj` is not null.
- __ Mov(out, 0);
- __ Cbz(obj, &done);
+ // Avoid null check if we know `obj` is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ Mov(out, 0);
+ __ Cbz(obj, &done);
+ }
// Compare the class of `obj` with `cls`.
__ Ldr(out, HeapOperand(obj, mirror::Object::ClassOffset()));
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0cc377c..b48a4d5 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4225,9 +4225,11 @@
SlowPathCodeX86* slow_path = nullptr;
// Return 0 if `obj` is null.
- // TODO: avoid this check if we know obj is not null.
- __ testl(obj, obj);
- __ j(kEqual, &zero);
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &zero);
+ }
__ movl(out, Address(obj, class_offset));
// Compare the class of `obj` with `cls`.
if (cls.IsRegister()) {
@@ -4252,8 +4254,12 @@
__ movl(out, Immediate(1));
__ jmp(&done);
}
- __ Bind(&zero);
- __ movl(out, Immediate(0));
+
+ if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) {
+ __ Bind(&zero);
+ __ movl(out, Immediate(0));
+ }
+
if (slow_path != nullptr) {
__ Bind(slow_path->GetExitLabel());
}
@@ -4278,11 +4284,13 @@
instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
- // TODO: avoid this check if we know obj is not null.
- __ testl(obj, obj);
- __ j(kEqual, slow_path->GetExitLabel());
- __ movl(temp, Address(obj, class_offset));
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, slow_path->GetExitLabel());
+ }
+ __ movl(temp, Address(obj, class_offset));
// Compare the class of `obj` with `cls`.
if (cls.IsRegister()) {
__ cmpl(temp, cls.AsRegister<Register>());
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index fb0f4ca..4e5aaf5 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4033,9 +4033,11 @@
SlowPathCodeX86_64* slow_path = nullptr;
// Return 0 if `obj` is null.
- // TODO: avoid this check if we know obj is not null.
- __ testl(obj, obj);
- __ j(kEqual, &zero);
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &zero);
+ }
// Compare the class of `obj` with `cls`.
__ movl(out, Address(obj, class_offset));
if (cls.IsRegister()) {
@@ -4059,8 +4061,12 @@
__ movl(out, Immediate(1));
__ jmp(&done);
}
- __ Bind(&zero);
- __ movl(out, Immediate(0));
+
+ if (instruction->MustDoNullCheck() || instruction->IsClassFinal()) {
+ __ Bind(&zero);
+ __ movl(out, Immediate(0));
+ }
+
if (slow_path != nullptr) {
__ Bind(slow_path->GetExitLabel());
}
@@ -4085,9 +4091,11 @@
instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
- // TODO: avoid this check if we know obj is not null.
- __ testl(obj, obj);
- __ j(kEqual, slow_path->GetExitLabel());
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, slow_path->GetExitLabel());
+ }
// Compare the class of `obj` with `cls`.
__ movl(temp, Address(obj, class_offset));
if (cls.IsRegister()) {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index afbc490..40ff098 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -60,6 +60,7 @@
void VisitSub(HSub* instruction) OVERRIDE;
void VisitUShr(HUShr* instruction) OVERRIDE;
void VisitXor(HXor* instruction) OVERRIDE;
+ void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -161,6 +162,10 @@
void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+ if (!check_cast->InputAt(0)->CanBeNull()) {
+ check_cast->ClearMustDoNullCheck();
+ }
+
if (!load_class->IsResolved()) {
// If the class couldn't be resolve it's not safe to compare against it. It's
// default type would be Top which might be wider that the actual class type
@@ -178,6 +183,12 @@
}
}
+void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
+ if (!instruction->InputAt(0)->CanBeNull()) {
+ instruction->ClearMustDoNullCheck();
+ }
+}
+
void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
HBasicBlock* block = check->GetBlock();
// Currently always keep the suspend check at entry.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index fe47939..db1e505 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3329,6 +3329,7 @@
uint32_t dex_pc)
: HExpression(Primitive::kPrimBoolean, SideEffects::None()),
class_is_final_(class_is_final),
+ must_do_null_check_(true),
dex_pc_(dex_pc) {
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
@@ -3348,10 +3349,15 @@
bool IsClassFinal() const { return class_is_final_; }
+ // Used only in code generation.
+ bool MustDoNullCheck() const { return must_do_null_check_; }
+ void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+
DECLARE_INSTRUCTION(InstanceOf);
private:
const bool class_is_final_;
+ bool must_do_null_check_;
const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
@@ -3392,6 +3398,7 @@
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::None()),
class_is_final_(class_is_final),
+ must_do_null_check_(true),
dex_pc_(dex_pc) {
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
@@ -3410,6 +3417,9 @@
bool CanThrow() const OVERRIDE { return true; }
+ bool MustDoNullCheck() const { return must_do_null_check_; }
+ void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+
uint32_t GetDexPc() const { return dex_pc_; }
bool IsClassFinal() const { return class_is_final_; }
@@ -3418,6 +3428,7 @@
private:
const bool class_is_final_;
+ bool must_do_null_check_;
const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HCheckCast);