Revert "Revert "Refactor HGraphBuilder and SsaBuilder to remove HLocals""
This patch merges the instruction-building phases from HGraphBuilder
and SsaBuilder into a single HInstructionBuilder class. As a result,
it is not necessary to generate HLocal, HLoadLocal and HStoreLocal
instructions any more, as the builder produces SSA form directly.
Saves 5-15% of arena-allocated memory (see bug for more data):
GMS 20.46MB => 19.26MB (-5.86%)
Maps 24.12MB => 21.47MB (-10.98%)
YouTube 28.60MB => 26.01MB (-9.05%)
This CL fixed an issue with parsing quickened instructions.
Bug: 27894376
Bug: 27998571
Bug: 27995065
Change-Id: I20dbe1bf2d0fe296377478db98cb86cba695e694
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8a2e83a..0088fed 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -427,6 +427,10 @@
number_of_in_vregs_ = value;
}
+ uint16_t GetNumberOfInVRegs() const {
+ return number_of_in_vregs_;
+ }
+
uint16_t GetNumberOfLocalVRegs() const {
DCHECK(!in_ssa_form_);
return number_of_vregs_ - number_of_in_vregs_;
@@ -1211,9 +1215,7 @@
M(LessThanOrEqual, Condition) \
M(LoadClass, Instruction) \
M(LoadException, Instruction) \
- M(LoadLocal, Instruction) \
M(LoadString, Instruction) \
- M(Local, Instruction) \
M(LongConstant, Constant) \
M(MemoryBarrier, Instruction) \
M(MonitorOperation, Instruction) \
@@ -1244,7 +1246,6 @@
M(UnresolvedStaticFieldGet, Instruction) \
M(UnresolvedStaticFieldSet, Instruction) \
M(Select, Instruction) \
- M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
M(Throw, Instruction) \
@@ -2383,6 +2384,107 @@
DISALLOW_COPY_AND_ASSIGN(HReturn);
};
+class HPhi : public HInstruction {
+ public:
+ HPhi(ArenaAllocator* arena,
+ uint32_t reg_number,
+ size_t number_of_inputs,
+ Primitive::Type type,
+ uint32_t dex_pc = kNoDexPc)
+ : HInstruction(SideEffects::None(), dex_pc),
+ inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+ reg_number_(reg_number) {
+ SetPackedField<TypeField>(ToPhiType(type));
+ DCHECK_NE(GetType(), Primitive::kPrimVoid);
+ // Phis are constructed live and marked dead if conflicting or unused.
+ // Individual steps of SsaBuilder should assume that if a phi has been
+ // marked dead, it can be ignored and will be removed by SsaPhiElimination.
+ SetPackedFlag<kFlagIsLive>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
+ }
+
+ // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
+ static Primitive::Type ToPhiType(Primitive::Type type) {
+ return Primitive::PrimitiveKind(type);
+ }
+
+ bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
+
+ size_t InputCount() const OVERRIDE { return inputs_.size(); }
+
+ void AddInput(HInstruction* input);
+ void RemoveInputAt(size_t index);
+
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
+ void SetType(Primitive::Type new_type) {
+ // Make sure that only valid type changes occur. The following are allowed:
+ // (1) int -> float/ref (primitive type propagation),
+ // (2) long -> double (primitive type propagation).
+ DCHECK(GetType() == new_type ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
+ (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
+ SetPackedField<TypeField>(new_type);
+ }
+
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
+
+ uint32_t GetRegNumber() const { return reg_number_; }
+
+ void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
+ void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
+ bool IsDead() const { return !IsLive(); }
+ bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
+
+ bool IsVRegEquivalentOf(HInstruction* other) const {
+ return other != nullptr
+ && other->IsPhi()
+ && other->AsPhi()->GetBlock() == GetBlock()
+ && other->AsPhi()->GetRegNumber() == GetRegNumber();
+ }
+
+ // Returns the next equivalent phi (starting from the current one) or null if there is none.
+ // An equivalent phi is a phi having the same dex register and type.
+ // It assumes that phis with the same dex register are adjacent.
+ HPhi* GetNextEquivalentPhiWithSameType() {
+ HInstruction* next = GetNext();
+ while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
+ if (next->GetType() == GetType()) {
+ return next->AsPhi();
+ }
+ next = next->GetNext();
+ }
+ return nullptr;
+ }
+
+ DECLARE_INSTRUCTION(Phi);
+
+ protected:
+ const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
+ return inputs_[index];
+ }
+
+ void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_[index] = input;
+ }
+
+ private:
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+ static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
+ static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
+ ArenaVector<HUserRecord<HInstruction*> > inputs_;
+ const uint32_t reg_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(HPhi);
+};
+
// The exit instruction is the only instruction of the exit block.
// Instructions aborting the method (HThrow and HReturn) must branch to the
// exit block.
@@ -3543,57 +3645,6 @@
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
-// A local in the graph. Corresponds to a Dex register.
-class HLocal : public HTemplateInstruction<0> {
- public:
- explicit HLocal(uint16_t reg_number)
- : HTemplateInstruction(SideEffects::None(), kNoDexPc), reg_number_(reg_number) {}
-
- DECLARE_INSTRUCTION(Local);
-
- uint16_t GetRegNumber() const { return reg_number_; }
-
- private:
- // The Dex register number.
- const uint16_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HLocal);
-};
-
-// Load a given local. The local is an input of this instruction.
-class HLoadLocal : public HExpression<1> {
- public:
- HLoadLocal(HLocal* local, Primitive::Type type, uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc) {
- SetRawInputAt(0, local);
- }
-
- HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
-
- DECLARE_INSTRUCTION(LoadLocal);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HLoadLocal);
-};
-
-// Store a value in a given local. This instruction has two inputs: the value
-// and the local.
-class HStoreLocal : public HTemplateInstruction<2> {
- public:
- HStoreLocal(HLocal* local, HInstruction* value, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {
- SetRawInputAt(0, local);
- SetRawInputAt(1, value);
- }
-
- HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
-
- DECLARE_INSTRUCTION(StoreLocal);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HStoreLocal);
-};
-
class HNewInstance : public HExpression<2> {
public:
HNewInstance(HInstruction* cls,
@@ -3914,8 +3965,7 @@
// potentially one other if the clinit check is explicit, and potentially
// one other if the method is a string factory.
(NeedsCurrentMethodInput(dispatch_info.method_load_kind) ? 1u : 0u) +
- (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u) +
- (dispatch_info.method_load_kind == MethodLoadKind::kStringInit ? 1u : 0u),
+ (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u),
return_type,
dex_pc,
method_index,
@@ -4043,15 +4093,6 @@
DCHECK(!IsStaticWithExplicitClinitCheck());
}
- HInstruction* GetAndRemoveThisArgumentOfStringInit() {
- DCHECK(IsStringInit());
- size_t index = InputCount() - 1;
- HInstruction* input = InputAt(index);
- RemoveAsUserOfInput(index);
- inputs_.pop_back();
- return input;
- }
-
// Is this a call to a static method whose declaring class has an
// explicit initialization check in the graph?
bool IsStaticWithExplicitClinitCheck() const {
@@ -4894,7 +4935,6 @@
SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
dex_pc) {
SetRawInputAt(0, input);
- DCHECK_NE(input->GetType(), result_type);
// Invariant: We should never generate a conversion to a Boolean value.
DCHECK_NE(Primitive::kPrimBoolean, result_type);
}
@@ -4930,115 +4970,6 @@
static constexpr uint32_t kNoRegNumber = -1;
-class HPhi : public HInstruction {
- public:
- HPhi(ArenaAllocator* arena,
- uint32_t reg_number,
- size_t number_of_inputs,
- Primitive::Type type,
- uint32_t dex_pc = kNoDexPc)
- : HInstruction(SideEffects::None(), dex_pc),
- inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
- reg_number_(reg_number) {
- SetPackedField<TypeField>(ToPhiType(type));
- DCHECK_NE(GetType(), Primitive::kPrimVoid);
- // Phis are constructed live and marked dead if conflicting or unused.
- // Individual steps of SsaBuilder should assume that if a phi has been
- // marked dead, it can be ignored and will be removed by SsaPhiElimination.
- SetPackedFlag<kFlagIsLive>(true);
- SetPackedFlag<kFlagCanBeNull>(true);
- }
-
- // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
- static Primitive::Type ToPhiType(Primitive::Type type) {
- switch (type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimShort:
- case Primitive::kPrimChar:
- return Primitive::kPrimInt;
- default:
- return type;
- }
- }
-
- bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
-
- size_t InputCount() const OVERRIDE { return inputs_.size(); }
-
- void AddInput(HInstruction* input);
- void RemoveInputAt(size_t index);
-
- Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
- void SetType(Primitive::Type new_type) {
- // Make sure that only valid type changes occur. The following are allowed:
- // (1) int -> float/ref (primitive type propagation),
- // (2) long -> double (primitive type propagation).
- DCHECK(GetType() == new_type ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- SetPackedField<TypeField>(new_type);
- }
-
- bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
- void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
-
- uint32_t GetRegNumber() const { return reg_number_; }
-
- void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
- void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
- bool IsDead() const { return !IsLive(); }
- bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
-
- bool IsVRegEquivalentOf(HInstruction* other) const {
- return other != nullptr
- && other->IsPhi()
- && other->AsPhi()->GetBlock() == GetBlock()
- && other->AsPhi()->GetRegNumber() == GetRegNumber();
- }
-
- // Returns the next equivalent phi (starting from the current one) or null if there is none.
- // An equivalent phi is a phi having the same dex register and type.
- // It assumes that phis with the same dex register are adjacent.
- HPhi* GetNextEquivalentPhiWithSameType() {
- HInstruction* next = GetNext();
- while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
- if (next->GetType() == GetType()) {
- return next->AsPhi();
- }
- next = next->GetNext();
- }
- return nullptr;
- }
-
- DECLARE_INSTRUCTION(Phi);
-
- protected:
- const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
- return inputs_[index];
- }
-
- void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
- inputs_[index] = input;
- }
-
- private:
- static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
- static constexpr size_t kFieldTypeSize =
- MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
- static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
- static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
- static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
- static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
- using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
-
- ArenaVector<HUserRecord<HInstruction*> > inputs_;
- const uint32_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HPhi);
-};
-
class HNullCheck : public HExpression<1> {
public:
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
@@ -5382,7 +5313,7 @@
// constructor.
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
: HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
- DCHECK(index->GetType() == Primitive::kPrimInt);
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
SetRawInputAt(0, index);
SetRawInputAt(1, length);
}
@@ -5913,7 +5844,7 @@
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(field_type, value->GetType());
+ DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
SetRawInputAt(0, obj);
SetRawInputAt(1, value);
}
@@ -5973,7 +5904,7 @@
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(field_type, value->GetType());
+ DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
SetRawInputAt(0, value);
}