optimizing: NullCheck elimination
How it works:
- run a type analysis to propagate null information on instructions
- during the last instruction simplifier remove null checks for which
the input is known to be not null
The current type analysis is actually a nullability analysis but it will
be reused in follow up CLs to propagate type information: so it keeps
the more convenient name.
Change-Id: I54bb1d32ab24604b4d677d1ecdaf8d60a5ff5ce9
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 1d4f5a3..c92d7f5 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -110,7 +110,8 @@
optimizing/ssa_builder.cc \
optimizing/ssa_liveness_analysis.cc \
optimizing/ssa_phi_elimination.cc \
- optimizing/ssa_type_propagation.cc \
+ optimizing/primitive_type_propagation.cc \
+ optimizing/reference_type_propagation.cc \
trampolines/trampoline_compiler.cc \
utils/arena_allocator.cc \
utils/arena_bit_vector.cc \
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 9c2facb..21c5859 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -161,7 +161,7 @@
if (!dex_compilation_unit_->IsStatic()) {
// Add the implicit 'this' argument, not expressed in the signature.
HParameterValue* parameter =
- new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot);
+ new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot, true);
entry_block_->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 63bc4ae..17c8f33 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -27,6 +27,7 @@
void VisitEqual(HEqual* equal) OVERRIDE;
void VisitArraySet(HArraySet* equal) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+ void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
};
void InstructionSimplifier::Run() {
@@ -34,6 +35,14 @@
visitor.VisitInsertionOrder();
}
+void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) {
+ HInstruction* obj = null_check->InputAt(0);
+ if (!obj->CanBeNull()) {
+ null_check->ReplaceWith(obj);
+ null_check->GetBlock()->RemoveInstruction(null_check);
+ }
+}
+
void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
HBasicBlock* block = check->GetBlock();
// Currently always keep the suspend check at entry.
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index 7068c7f..bca6697 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -27,8 +27,8 @@
*/
class InstructionSimplifier : public HOptimization {
public:
- explicit InstructionSimplifier(HGraph* graph)
- : HOptimization(graph, true, "instruction_simplifier") {}
+ explicit InstructionSimplifier(HGraph* graph, const char* name = "instruction_simplifier")
+ : HOptimization(graph, true, name) {}
void Run() OVERRIDE;
};
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2cc021c..bddcf37 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -847,6 +847,10 @@
virtual bool CanThrow() const { return false; }
bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
+ // Does not apply for all instructions, but having this at top level greatly
+ // simplifies the null check elimination.
+ virtual bool CanBeNull() const { return true; }
+
virtual bool CanDoImplicitNullCheck() const { return false; }
void AddUseAt(HInstruction* user, size_t index) {
@@ -1802,6 +1806,8 @@
// TODO: optimize when possible.
bool CanThrow() const OVERRIDE { return true; }
+ bool CanBeNull() const OVERRIDE { return false; }
+
DECLARE_INSTRUCTION(NewInstance);
private:
@@ -1838,7 +1844,9 @@
uint16_t GetTypeIndex() const { return type_index_; }
// Calls runtime so needs an environment.
- virtual bool NeedsEnvironment() const { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE { return false; }
DECLARE_INSTRUCTION(NewArray);
@@ -2088,18 +2096,23 @@
// the calling convention.
class HParameterValue : public HExpression<0> {
public:
- HParameterValue(uint8_t index, Primitive::Type parameter_type)
- : HExpression(parameter_type, SideEffects::None()), index_(index) {}
+ HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
+ : HExpression(parameter_type, SideEffects::None()), index_(index), is_this_(is_this) {}
uint8_t GetIndex() const { return index_; }
+ bool CanBeNull() const OVERRIDE { return !is_this_; }
+
DECLARE_INSTRUCTION(ParameterValue);
private:
// The index of this parameter in the parameters list. Must be less
- // than HGraph::number_of_in_vregs_;
+ // than HGraph::number_of_in_vregs_.
const uint8_t index_;
+ // Whether or not the parameter value corresponds to 'this' argument.
+ const bool is_this_;
+
DISALLOW_COPY_AND_ASSIGN(HParameterValue);
};
@@ -2158,22 +2171,26 @@
inputs_(arena, number_of_inputs),
reg_number_(reg_number),
type_(type),
- is_live_(false) {
+ is_live_(false),
+ can_be_null_(true) {
inputs_.SetSize(number_of_inputs);
}
- virtual size_t InputCount() const { return inputs_.Size(); }
- virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
+ size_t InputCount() const OVERRIDE { return inputs_.Size(); }
+ HInstruction* InputAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
- virtual void SetRawInputAt(size_t index, HInstruction* input) {
+ void SetRawInputAt(size_t index, HInstruction* input) OVERRIDE {
inputs_.Put(index, input);
}
void AddInput(HInstruction* input);
- virtual Primitive::Type GetType() const { return type_; }
+ Primitive::Type GetType() const OVERRIDE { return type_; }
void SetType(Primitive::Type type) { type_ = type; }
+ bool CanBeNull() const OVERRIDE { return can_be_null_; }
+ void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+
uint32_t GetRegNumber() const { return reg_number_; }
void SetDead() { is_live_ = false; }
@@ -2188,6 +2205,7 @@
const uint32_t reg_number_;
Primitive::Type type_;
bool is_live_;
+ bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
};
@@ -2199,15 +2217,17 @@
SetRawInputAt(0, value);
}
- virtual bool CanBeMoved() const { return true; }
- virtual bool InstructionDataEquals(HInstruction* other) const {
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
UNUSED(other);
return true;
}
- virtual bool NeedsEnvironment() const { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return true; }
- virtual bool CanThrow() const { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE { return false; }
uint32_t GetDexPc() const { return dex_pc_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4f05afa..705345b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -43,6 +43,7 @@
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "ssa_liveness_analysis.h"
+#include "reference_type_propagation.h"
#include "utils/arena_allocator.h"
namespace art {
@@ -196,6 +197,18 @@
return code_item.tries_size_ == 0;
}
+static void RunOptimizations(HOptimization* optimizations[],
+ size_t length,
+ const HGraphVisualizer& visualizer) {
+ for (size_t i = 0; i < length; ++i) {
+ HOptimization* optimization = optimizations[i];
+ visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/false);
+ optimization->Run();
+ visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/true);
+ optimization->Check();
+ }
+}
+
static void RunOptimizations(HGraph* graph,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
@@ -213,7 +226,8 @@
SideEffectsAnalysis side_effects(graph);
GVNOptimization gvn(graph, side_effects);
BoundsCheckElimination bce(graph);
- InstructionSimplifier simplify2(graph);
+ ReferenceTypePropagation type_propagation(graph);
+ InstructionSimplifier simplify2(graph, "instruction_simplifier_after_types");
IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
@@ -229,16 +243,11 @@
&side_effects,
&gvn,
&bce,
+ &type_propagation,
&simplify2
};
- for (size_t i = 0; i < arraysize(optimizations); ++i) {
- HOptimization* optimization = optimizations[i];
- visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/false);
- optimization->Run();
- visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/true);
- optimization->Check();
- }
+ RunOptimizations(optimizations, arraysize(optimizations), visualizer);
}
// The stack map we generate must be 4-byte aligned on ARM. Since existing
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
similarity index 90%
rename from compiler/optimizing/ssa_type_propagation.cc
rename to compiler/optimizing/primitive_type_propagation.cc
index 947427b..7e274f6 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "ssa_builder.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
#include "nodes.h"
+#include "ssa_builder.h"
namespace art {
@@ -39,7 +39,7 @@
// Re-compute and update the type of the instruction. Returns
// whether or not the type was changed.
-bool SsaTypePropagation::UpdateType(HPhi* phi) {
+bool PrimitiveTypePropagation::UpdateType(HPhi* phi) {
Primitive::Type existing = phi->GetType();
Primitive::Type new_type = existing;
@@ -67,14 +67,14 @@
return existing != new_type;
}
-void SsaTypePropagation::Run() {
+void PrimitiveTypePropagation::Run() {
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
VisitBasicBlock(it.Current());
}
ProcessWorklist();
}
-void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+void PrimitiveTypePropagation::VisitBasicBlock(HBasicBlock* block) {
if (block->IsLoopHeader()) {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
@@ -100,7 +100,7 @@
}
}
-void SsaTypePropagation::ProcessWorklist() {
+void PrimitiveTypePropagation::ProcessWorklist() {
while (!worklist_.IsEmpty()) {
HPhi* instruction = worklist_.Pop();
if (UpdateType(instruction)) {
@@ -109,11 +109,11 @@
}
}
-void SsaTypePropagation::AddToWorklist(HPhi* instruction) {
+void PrimitiveTypePropagation::AddToWorklist(HPhi* instruction) {
worklist_.Add(instruction);
}
-void SsaTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+void PrimitiveTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->GetUser()->AsPhi();
if (phi != nullptr) {
diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/primitive_type_propagation.h
similarity index 72%
rename from compiler/optimizing/ssa_type_propagation.h
rename to compiler/optimizing/primitive_type_propagation.h
index f4d3d63..1374cbb 100644
--- a/compiler/optimizing/ssa_type_propagation.h
+++ b/compiler/optimizing/primitive_type_propagation.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
-#define ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#ifndef ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
#include "nodes.h"
namespace art {
-// Compute and propagate types of phis in the graph.
-class SsaTypePropagation : public ValueObject {
+// Compute and propagate primitive types of phis in the graph.
+class PrimitiveTypePropagation : public ValueObject {
public:
- explicit SsaTypePropagation(HGraph* graph)
+ explicit PrimitiveTypePropagation(HGraph* graph)
: graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
void Run();
@@ -41,9 +41,9 @@
static constexpr size_t kDefaultWorklistSize = 8;
- DISALLOW_COPY_AND_ASSIGN(SsaTypePropagation);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveTypePropagation);
};
} // namespace art
-#endif // ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#endif // ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
new file mode 100644
index 0000000..24e6837
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reference_type_propagation.h"
+
+namespace art {
+
+// TODO: Only do the analysis on reference types. We currently have to handle
+// the `null` constant, that is represented as a `HIntConstant` and therefore
+// has the Primitive::kPrimInt type.
+
+void ReferenceTypePropagation::Run() {
+ // Compute null status for instructions.
+
+ // To properly propagate not-null info we need to visit in the dominator-based order.
+ // Reverse post order guarantees a node's dominators are visited first.
+ // We take advantage of this order in `VisitBasicBlock`.
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+ ProcessWorklist();
+}
+
+// Re-computes and updates the nullability of the instruction. Returns whether or
+// not the nullability was changed.
+bool ReferenceTypePropagation::UpdateNullability(HPhi* phi) {
+ bool existing_can_be_null = phi->CanBeNull();
+ bool new_can_be_null = false;
+ for (size_t i = 0; i < phi->InputCount(); i++) {
+ new_can_be_null |= phi->InputAt(i)->CanBeNull();
+ }
+ phi->SetCanBeNull(new_can_be_null);
+
+ return existing_can_be_null != new_can_be_null;
+}
+
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+ if (block->IsLoopHeader()) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Set the initial type for the phi. Use the non back edge input for reaching
+ // a fixed point faster.
+ HPhi* phi = it.Current()->AsPhi();
+ AddToWorklist(phi);
+ phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
+ }
+ } else {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Eagerly compute the type of the phi, for quicker convergence. Note
+ // that we don't need to add users to the worklist because we are
+ // doing a reverse post-order visit, therefore either the phi users are
+ // non-loop phi and will be visited later in the visit, or are loop-phis,
+ // and they are already in the work list.
+ UpdateNullability(it.Current()->AsPhi());
+ }
+ }
+}
+
+void ReferenceTypePropagation::ProcessWorklist() {
+ while (!worklist_.IsEmpty()) {
+ HPhi* instruction = worklist_.Pop();
+ if (UpdateNullability(instruction)) {
+ AddDependentInstructionsToWorklist(instruction);
+ }
+ }
+}
+
+void ReferenceTypePropagation::AddToWorklist(HPhi* instruction) {
+ worklist_.Add(instruction);
+}
+
+void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->GetUser()->AsPhi();
+ if (phi != nullptr) {
+ AddToWorklist(phi);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
new file mode 100644
index 0000000..a74319d
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Propagates reference types to instructions.
+ * TODO: Currently only nullability is computed.
+ */
+class ReferenceTypePropagation : public HOptimization {
+ public:
+ explicit ReferenceTypePropagation(HGraph* graph)
+ : HOptimization(graph, true, "reference_type_propagation"),
+ worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block);
+ void ProcessWorklist();
+ void AddToWorklist(HPhi* phi);
+ void AddDependentInstructionsToWorklist(HPhi* phi);
+ bool UpdateNullability(HPhi* phi);
+
+ GrowableArray<HPhi*> worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 4f9c3b8..c9a21aa 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -17,7 +17,7 @@
#include "ssa_builder.h"
#include "nodes.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
#include "ssa_phi_elimination.h"
namespace art {
@@ -52,7 +52,7 @@
// 4) Propagate types of phis. At this point, phis are typed void in the general
// case, or float or double when we created a floating-point equivalent. So we
// need to propagate the types across phis to give them a correct type.
- SsaTypePropagation type_propagation(GetGraph());
+ PrimitiveTypePropagation type_propagation(GetGraph());
type_propagation.Run();
// 5) Clear locals.