diff options
Diffstat (limited to 'compiler/optimizing/nodes.h')
| -rw-r--r-- | compiler/optimizing/nodes.h | 713 |
1 files changed, 348 insertions, 365 deletions
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 6f3e536d05..da84afbb39 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -36,6 +36,8 @@ #include "offsets.h" #include "primitive.h" #include "utils/array_ref.h" +#include "utils/intrusive_forward_list.h" +#include "utils/transform_array_ref.h" namespace art { @@ -169,7 +171,7 @@ class ReferenceTypeInfo : ValueObject { return handle.GetReference() != nullptr; } - bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) { + bool IsValid() const { return IsValidHandle(type_handle_); } @@ -649,6 +651,7 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { : header_(header), suspend_check_(nullptr), irreducible_(false), + contains_irreducible_loop_(false), back_edges_(graph->GetArena()->Adapter(kArenaAllocLoopInfoBackEdges)), // Make bit vector growable, as the number of blocks may change. blocks_(graph->GetArena(), graph->GetBlocks().size(), true, kArenaAllocLoopInfoBackEdges) { @@ -656,6 +659,7 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { } bool IsIrreducible() const { return irreducible_; } + bool ContainsIrreducibleLoop() const { return contains_irreducible_loop_; } void Dump(std::ostream& os); @@ -724,6 +728,14 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { blocks_.ClearAllBits(); } + bool HasBackEdgeNotDominatedByHeader() const; + + bool IsPopulated() const { + return blocks_.GetHighestBitSet() != -1; + } + + bool DominatesAllBackEdges(HBasicBlock* block); + private: // Internal recursive implementation of `Populate`. void PopulateRecursive(HBasicBlock* block); @@ -732,6 +744,7 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { HBasicBlock* header_; HSuspendCheck* suspend_check_; bool irreducible_; + bool contains_irreducible_loop_; ArenaVector<HBasicBlock*> back_edges_; ArenaBitVector blocks_; @@ -1321,12 +1334,12 @@ class HLoopInformationOutwardIterator : public ValueObject { FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION -#define DECLARE_INSTRUCTION(type) \ - InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \ - const char* DebugName() const OVERRIDE { return #type; } \ - bool InstructionTypeEquals(HInstruction* other) const OVERRIDE { \ - return other->Is##type(); \ - } \ +#define DECLARE_INSTRUCTION(type) \ + InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \ + const char* DebugName() const OVERRIDE { return #type; } \ + bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \ + return other->Is##type(); \ + } \ void Accept(HGraphVisitor* visitor) OVERRIDE #define DECLARE_ABSTRACT_INSTRUCTION(type) \ @@ -1334,127 +1347,31 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) const H##type* As##type() const { return this; } \ H##type* As##type() { return this; } -template <typename T> class HUseList; - template <typename T> class HUseListNode : public ArenaObject<kArenaAllocUseListNode> { public: - HUseListNode* GetPrevious() const { return prev_; } - HUseListNode* GetNext() const { return next_; } T GetUser() const { return user_; } size_t GetIndex() const { return index_; } void SetIndex(size_t index) { index_ = index; } + // Hook for the IntrusiveForwardList<>. + // TODO: Hide this better. + IntrusiveForwardListHook hook; + private: HUseListNode(T user, size_t index) - : user_(user), index_(index), prev_(nullptr), next_(nullptr) {} + : user_(user), index_(index) {} T const user_; size_t index_; - HUseListNode<T>* prev_; - HUseListNode<T>* next_; - friend class HUseList<T>; + friend class HInstruction; DISALLOW_COPY_AND_ASSIGN(HUseListNode); }; template <typename T> -class HUseList : public ValueObject { - public: - HUseList() : first_(nullptr) {} - - void Clear() { - first_ = nullptr; - } - - // Adds a new entry at the beginning of the use list and returns - // the newly created node. - HUseListNode<T>* AddUse(T user, size_t index, ArenaAllocator* arena) { - HUseListNode<T>* new_node = new (arena) HUseListNode<T>(user, index); - if (IsEmpty()) { - first_ = new_node; - } else { - first_->prev_ = new_node; - new_node->next_ = first_; - first_ = new_node; - } - return new_node; - } - - HUseListNode<T>* GetFirst() const { - return first_; - } - - void Remove(HUseListNode<T>* node) { - DCHECK(node != nullptr); - DCHECK(Contains(node)); - - if (node->prev_ != nullptr) { - node->prev_->next_ = node->next_; - } - if (node->next_ != nullptr) { - node->next_->prev_ = node->prev_; - } - if (node == first_) { - first_ = node->next_; - } - } - - bool Contains(const HUseListNode<T>* node) const { - if (node == nullptr) { - return false; - } - for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) { - if (current == node) { - return true; - } - } - return false; - } - - bool IsEmpty() const { - return first_ == nullptr; - } - - bool HasOnlyOneUse() const { - return first_ != nullptr && first_->next_ == nullptr; - } - - size_t SizeSlow() const { - size_t count = 0; - for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) { - ++count; - } - return count; - } - - private: - HUseListNode<T>* first_; -}; - -template<typename T> -class HUseIterator : public ValueObject { - public: - explicit HUseIterator(const HUseList<T>& uses) : current_(uses.GetFirst()) {} - - bool Done() const { return current_ == nullptr; } - - void Advance() { - DCHECK(!Done()); - current_ = current_->GetNext(); - } - - HUseListNode<T>* Current() const { - DCHECK(!Done()); - return current_; - } - - private: - HUseListNode<T>* current_; - - friend class HValue; -}; +using HUseList = IntrusiveForwardList<HUseListNode<T>>; // This class is used by HEnvironment and HInstruction classes to record the // instructions they use and pointers to the corresponding HUseListNodes kept @@ -1462,25 +1379,26 @@ class HUseIterator : public ValueObject { template <typename T> class HUserRecord : public ValueObject { public: - HUserRecord() : instruction_(nullptr), use_node_(nullptr) {} - explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), use_node_(nullptr) {} + HUserRecord() : instruction_(nullptr), before_use_node_() {} + explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), before_use_node_() {} - HUserRecord(const HUserRecord<T>& old_record, HUseListNode<T>* use_node) - : instruction_(old_record.instruction_), use_node_(use_node) { + HUserRecord(const HUserRecord<T>& old_record, typename HUseList<T>::iterator before_use_node) + : HUserRecord(old_record.instruction_, before_use_node) {} + HUserRecord(HInstruction* instruction, typename HUseList<T>::iterator before_use_node) + : instruction_(instruction), before_use_node_(before_use_node) { DCHECK(instruction_ != nullptr); - DCHECK(use_node_ != nullptr); - DCHECK(old_record.use_node_ == nullptr); } HInstruction* GetInstruction() const { return instruction_; } - HUseListNode<T>* GetUseNode() const { return use_node_; } + typename HUseList<T>::iterator GetBeforeUseNode() const { return before_use_node_; } + typename HUseList<T>::iterator GetUseNode() const { return ++GetBeforeUseNode(); } private: // Instruction used by the user. HInstruction* instruction_; - // Corresponding entry in the use list kept by 'instruction_'. - HUseListNode<T>* use_node_; + // Iterator before the corresponding entry in the use list kept by 'instruction_'. + typename HUseList<T>::iterator before_use_node_; }; /** @@ -1805,14 +1723,6 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { } private: - // Record instructions' use entries of this environment for constant-time removal. - // It should only be called by HInstruction when a new environment use is added. - void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) { - DCHECK(env_use->GetUser() == this); - size_t index = env_use->GetIndex(); - vregs_[index] = HUserRecord<HEnvironment*>(vregs_[index], env_use); - } - ArenaVector<HUserRecord<HEnvironment*>> vregs_; ArenaVector<Location> locations_; HEnvironment* parent_; @@ -1872,16 +1782,41 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { return IsLoopHeaderPhi() && GetBlock()->GetLoopInformation()->IsIrreducible(); } - virtual size_t InputCount() const = 0; + virtual ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() = 0; + + ArrayRef<const HUserRecord<HInstruction*>> GetInputRecords() const { + // One virtual method is enough, just const_cast<> and then re-add the const. + return ArrayRef<const HUserRecord<HInstruction*>>( + const_cast<HInstruction*>(this)->GetInputRecords()); + } + + auto GetInputs() { + return MakeTransformArrayRef( + GetInputRecords(), + [](HUserRecord<HInstruction*>& record) -> HInstruction* { + return record.GetInstruction(); + }); + } + + auto GetInputs() const { + return MakeTransformArrayRef( + GetInputRecords(), + [](const HUserRecord<HInstruction*>& record) -> const HInstruction* { + return record.GetInstruction(); + }); + } + + size_t InputCount() const { return GetInputRecords().size(); } HInstruction* InputAt(size_t i) const { return InputRecordAt(i).GetInstruction(); } + void SetRawInputAt(size_t index, HInstruction* input) { + SetRawInputRecordAt(index, HUserRecord<HInstruction*>(input)); + } + virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; } - void SetRawInputAt(size_t index, HInstruction* input) { - SetRawInputRecordAt(index, HUserRecord<HInstruction*>(input)); - } virtual bool NeedsEnvironment() const { return false; } @@ -1916,36 +1851,52 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, - GetPackedFlag<kFlagReferenceTypeIsExact>());; + GetPackedFlag<kFlagReferenceTypeIsExact>()); } void AddUseAt(HInstruction* user, size_t index) { DCHECK(user != nullptr); - HUseListNode<HInstruction*>* use = - uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena()); - user->SetRawInputRecordAt(index, HUserRecord<HInstruction*>(user->InputRecordAt(index), use)); + // Note: fixup_end remains valid across push_front(). + auto fixup_end = uses_.empty() ? uses_.begin() : ++uses_.begin(); + HUseListNode<HInstruction*>* new_node = + new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HInstruction*>(user, index); + uses_.push_front(*new_node); + FixUpUserRecordsAfterUseInsertion(fixup_end); } void AddEnvUseAt(HEnvironment* user, size_t index) { DCHECK(user != nullptr); - HUseListNode<HEnvironment*>* env_use = - env_uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena()); - user->RecordEnvUse(env_use); + // Note: env_fixup_end remains valid across push_front(). + auto env_fixup_end = env_uses_.empty() ? env_uses_.begin() : ++env_uses_.begin(); + HUseListNode<HEnvironment*>* new_node = + new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HEnvironment*>(user, index); + env_uses_.push_front(*new_node); + FixUpUserRecordsAfterEnvUseInsertion(env_fixup_end); } void RemoveAsUserOfInput(size_t input) { HUserRecord<HInstruction*> input_use = InputRecordAt(input); - input_use.GetInstruction()->uses_.Remove(input_use.GetUseNode()); + HUseList<HInstruction*>::iterator before_use_node = input_use.GetBeforeUseNode(); + input_use.GetInstruction()->uses_.erase_after(before_use_node); + input_use.GetInstruction()->FixUpUserRecordsAfterUseRemoval(before_use_node); + } + + void RemoveAsUserOfAllInputs() { + for (const HUserRecord<HInstruction*>& input_use : GetInputRecords()) { + HUseList<HInstruction*>::iterator before_use_node = input_use.GetBeforeUseNode(); + input_use.GetInstruction()->uses_.erase_after(before_use_node); + input_use.GetInstruction()->FixUpUserRecordsAfterUseRemoval(before_use_node); + } } const HUseList<HInstruction*>& GetUses() const { return uses_; } const HUseList<HEnvironment*>& GetEnvUses() const { return env_uses_; } - bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); } - bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); } - bool HasNonEnvironmentUses() const { return !uses_.IsEmpty(); } + bool HasUses() const { return !uses_.empty() || !env_uses_.empty(); } + bool HasEnvironmentUses() const { return !env_uses_.empty(); } + bool HasNonEnvironmentUses() const { return !uses_.empty(); } bool HasOnlyOneNonEnvironmentUse() const { - return !HasEnvironmentUses() && GetUses().HasOnlyOneUse(); + return !HasEnvironmentUses() && GetUses().HasExactlyOneElement(); } // Does this instruction strictly dominate `other_instruction`? @@ -2042,21 +1993,21 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { virtual bool CanBeMoved() const { return false; } // Returns whether the two instructions are of the same kind. - virtual bool InstructionTypeEquals(HInstruction* other ATTRIBUTE_UNUSED) const { + virtual bool InstructionTypeEquals(const HInstruction* other ATTRIBUTE_UNUSED) const { return false; } // Returns whether any data encoded in the two instructions is equal. // This method does not look at the inputs. Both instructions must be // of the same type, otherwise the method has undefined behavior. - virtual bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const { + virtual bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const { return false; } // Returns whether two instructions are equal, that is: // 1) They have the same type and contain the same data (InstructionDataEquals). // 2) Their inputs are identical. - bool Equals(HInstruction* other) const; + bool Equals(const HInstruction* other) const; // TODO: Remove this indirection when the [[pure]] attribute proposal (n3744) // is adopted and implemented by our C++ compiler(s). Fow now, we need to hide @@ -2067,8 +2018,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { virtual size_t ComputeHashCode() const { size_t result = GetKind(); - for (size_t i = 0, e = InputCount(); i < e; ++i) { - result = (result * 31) + InputAt(i)->GetId(); + for (const HInstruction* input : GetInputs()) { + result = (result * 31) + input->GetId(); } return result; } @@ -2118,8 +2069,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1; static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; - virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0; - virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0; + const HUserRecord<HInstruction*> InputRecordAt(size_t i) const { + return GetInputRecords()[i]; + } + + void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) { + ArrayRef<HUserRecord<HInstruction*>> input_records = GetInputRecords(); + input_records[index] = input; + } uint32_t GetPackedFields() const { return packed_fields_; @@ -2147,7 +2104,45 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { } private: - void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); } + void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) { + auto before_use_node = uses_.before_begin(); + for (auto use_node = uses_.begin(); use_node != fixup_end; ++use_node) { + HInstruction* user = use_node->GetUser(); + size_t input_index = use_node->GetIndex(); + user->SetRawInputRecordAt(input_index, HUserRecord<HInstruction*>(this, before_use_node)); + before_use_node = use_node; + } + } + + void FixUpUserRecordsAfterUseRemoval(HUseList<HInstruction*>::iterator before_use_node) { + auto next = ++HUseList<HInstruction*>::iterator(before_use_node); + if (next != uses_.end()) { + HInstruction* next_user = next->GetUser(); + size_t next_index = next->GetIndex(); + DCHECK(next_user->InputRecordAt(next_index).GetInstruction() == this); + next_user->SetRawInputRecordAt(next_index, HUserRecord<HInstruction*>(this, before_use_node)); + } + } + + void FixUpUserRecordsAfterEnvUseInsertion(HUseList<HEnvironment*>::iterator env_fixup_end) { + auto before_env_use_node = env_uses_.before_begin(); + for (auto env_use_node = env_uses_.begin(); env_use_node != env_fixup_end; ++env_use_node) { + HEnvironment* user = env_use_node->GetUser(); + size_t input_index = env_use_node->GetIndex(); + user->vregs_[input_index] = HUserRecord<HEnvironment*>(this, before_env_use_node); + before_env_use_node = env_use_node; + } + } + + void FixUpUserRecordsAfterEnvUseRemoval(HUseList<HEnvironment*>::iterator before_env_use_node) { + auto next = ++HUseList<HEnvironment*>::iterator(before_env_use_node); + if (next != env_uses_.end()) { + HEnvironment* next_user = next->GetUser(); + size_t next_index = next->GetIndex(); + DCHECK(next_user->vregs_[next_index].GetInstruction() == this); + next_user->vregs_[next_index] = HUserRecord<HEnvironment*>(this, before_env_use_node); + } + } HInstruction* previous_; HInstruction* next_; @@ -2202,21 +2197,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { }; std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs); -class HInputIterator : public ValueObject { - public: - explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) {} - - bool Done() const { return index_ == instruction_->InputCount(); } - HInstruction* Current() const { return instruction_->InputAt(index_); } - void Advance() { index_++; } - - private: - HInstruction* instruction_; - size_t index_; - - DISALLOW_COPY_AND_ASSIGN(HInputIterator); -}; - class HInstructionIterator : public ValueObject { public: explicit HInstructionIterator(const HInstructionList& instructions) @@ -2266,17 +2246,9 @@ class HTemplateInstruction: public HInstruction { : HInstruction(side_effects, dex_pc), inputs_() {} virtual ~HTemplateInstruction() {} - size_t InputCount() const OVERRIDE { return N; } - - protected: - const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { - DCHECK_LT(i, N); - return inputs_[i]; - } - - void SetRawInputRecordAt(size_t i, const HUserRecord<HInstruction*>& input) OVERRIDE { - DCHECK_LT(i, N); - inputs_[i] = input; + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>(inputs_); } private: @@ -2294,18 +2266,9 @@ class HTemplateInstruction<0>: public HInstruction { virtual ~HTemplateInstruction() {} - size_t InputCount() const OVERRIDE { return 0; } - - protected: - const HUserRecord<HInstruction*> InputRecordAt(size_t i ATTRIBUTE_UNUSED) const OVERRIDE { - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); - } - - void SetRawInputRecordAt(size_t i ATTRIBUTE_UNUSED, - const HUserRecord<HInstruction*>& input ATTRIBUTE_UNUSED) OVERRIDE { - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>(); } private: @@ -2337,7 +2300,7 @@ class HExpression : public HTemplateInstruction<N> { // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. -class HReturnVoid : public HTemplateInstruction<0> { +class HReturnVoid FINAL : public HTemplateInstruction<0> { public: explicit HReturnVoid(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2352,7 +2315,7 @@ class HReturnVoid : public HTemplateInstruction<0> { // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. -class HReturn : public HTemplateInstruction<1> { +class HReturn FINAL : public HTemplateInstruction<1> { public: explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) { @@ -2367,7 +2330,7 @@ class HReturn : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HReturn); }; -class HPhi : public HInstruction { +class HPhi FINAL : public HInstruction { public: HPhi(ArenaAllocator* arena, uint32_t reg_number, @@ -2393,7 +2356,10 @@ class HPhi : public HInstruction { bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } - size_t InputCount() const OVERRIDE { return inputs_.size(); } + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>(inputs_); + } void AddInput(HInstruction* input); void RemoveInputAt(size_t index); @@ -2443,15 +2409,6 @@ class HPhi : public HInstruction { 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 = @@ -2462,7 +2419,7 @@ class HPhi : public HInstruction { static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; - ArenaVector<HUserRecord<HInstruction*> > inputs_; + ArenaVector<HUserRecord<HInstruction*>> inputs_; const uint32_t reg_number_; DISALLOW_COPY_AND_ASSIGN(HPhi); @@ -2471,7 +2428,7 @@ class HPhi : public HInstruction { // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HThrow and HReturn) must branch to the // exit block. -class HExit : public HTemplateInstruction<0> { +class HExit FINAL : public HTemplateInstruction<0> { public: explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2484,7 +2441,7 @@ class HExit : public HTemplateInstruction<0> { }; // Jumps from one block to another. -class HGoto : public HTemplateInstruction<0> { +class HGoto FINAL : public HTemplateInstruction<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2524,9 +2481,9 @@ class HConstant : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HConstant); }; -class HNullConstant : public HConstant { +class HNullConstant FINAL : public HConstant { public: - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -2548,7 +2505,7 @@ class HNullConstant : public HConstant { // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). -class HIntConstant : public HConstant { +class HIntConstant FINAL : public HConstant { public: int32_t GetValue() const { return value_; } @@ -2556,7 +2513,7 @@ class HIntConstant : public HConstant { return static_cast<uint64_t>(static_cast<uint32_t>(value_)); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { DCHECK(other->IsIntConstant()) << other->DebugName(); return other->AsIntConstant()->value_ == value_; } @@ -2589,13 +2546,13 @@ class HIntConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; -class HLongConstant : public HConstant { +class HLongConstant FINAL : public HConstant { public: int64_t GetValue() const { return value_; } uint64_t GetValueAsUint64() const OVERRIDE { return value_; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { DCHECK(other->IsLongConstant()) << other->DebugName(); return other->AsLongConstant()->value_ == value_; } @@ -2619,7 +2576,7 @@ class HLongConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; -class HFloatConstant : public HConstant { +class HFloatConstant FINAL : public HConstant { public: float GetValue() const { return value_; } @@ -2627,7 +2584,7 @@ class HFloatConstant : public HConstant { return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_)); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { DCHECK(other->IsFloatConstant()) << other->DebugName(); return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64(); } @@ -2672,13 +2629,13 @@ class HFloatConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HFloatConstant); }; -class HDoubleConstant : public HConstant { +class HDoubleConstant FINAL : public HConstant { public: double GetValue() const { return value_; } uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { DCHECK(other->IsDoubleConstant()) << other->DebugName(); return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64(); } @@ -2725,7 +2682,7 @@ class HDoubleConstant : public HConstant { // Conditional branch. A block ending with an HIf instruction must have // two successors. -class HIf : public HTemplateInstruction<1> { +class HIf FINAL : public HTemplateInstruction<1> { public: explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) { @@ -2754,7 +2711,7 @@ class HIf : public HTemplateInstruction<1> { // non-exceptional control flow. // Normal-flow successor is stored at index zero, exception handlers under // higher indices in no particular order. -class HTryBoundary : public HTemplateInstruction<0> { +class HTryBoundary FINAL : public HTemplateInstruction<0> { public: enum class BoundaryKind { kEntry, @@ -2812,7 +2769,7 @@ class HTryBoundary : public HTemplateInstruction<0> { }; // Deoptimize to interpreter, upon checking a condition. -class HDeoptimize : public HTemplateInstruction<1> { +class HDeoptimize FINAL : public HTemplateInstruction<1> { public: // We set CanTriggerGC to prevent any intermediate address to be live // at the point of the `HDeoptimize`. @@ -2822,7 +2779,7 @@ class HDeoptimize : public HTemplateInstruction<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } @@ -2837,7 +2794,7 @@ class HDeoptimize : public HTemplateInstruction<1> { // Represents the ArtMethod that was passed as a first argument to // the method. It is used by instructions that depend on it, like // instructions that work with the dex cache. -class HCurrentMethod : public HExpression<0> { +class HCurrentMethod FINAL : public HExpression<0> { public: explicit HCurrentMethod(Primitive::Type type, uint32_t dex_pc = kNoDexPc) : HExpression(type, SideEffects::None(), dex_pc) {} @@ -2850,7 +2807,7 @@ class HCurrentMethod : public HExpression<0> { // Fetches an ArtMethod from the virtual table or the interface method table // of a class. -class HClassTableGet : public HExpression<1> { +class HClassTableGet FINAL : public HExpression<1> { public: enum class TableKind { kVTable, @@ -2869,7 +2826,7 @@ class HClassTableGet : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && other->AsClassTableGet()->GetPackedFields() == GetPackedFields(); } @@ -2897,7 +2854,7 @@ class HClassTableGet : public HExpression<1> { // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will // have one successor for each entry in the switch table, and the final successor // will be the block containing the next Dex opcode. -class HPackedSwitch : public HTemplateInstruction<1> { +class HPackedSwitch FINAL : public HTemplateInstruction<1> { public: HPackedSwitch(int32_t start_value, uint32_t num_entries, @@ -2939,7 +2896,7 @@ class HUnaryOperation : public HExpression<1> { Primitive::Type GetResultType() const { return GetType(); } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -3011,7 +2968,7 @@ class HBinaryOperation : public HExpression<2> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -3084,7 +3041,7 @@ class HCondition : public HBinaryOperation { ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); } void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return GetPackedFields() == other->AsCondition()->GetPackedFields(); } @@ -3142,7 +3099,7 @@ class HCondition : public HBinaryOperation { }; // Instruction to check if two inputs are equal to each other. -class HEqual : public HCondition { +class HEqual FINAL : public HCondition { public: HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3186,7 +3143,7 @@ class HEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HEqual); }; -class HNotEqual : public HCondition { +class HNotEqual FINAL : public HCondition { public: HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3229,7 +3186,7 @@ class HNotEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; -class HLessThan : public HCondition { +class HLessThan FINAL : public HCondition { public: HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3266,7 +3223,7 @@ class HLessThan : public HCondition { DISALLOW_COPY_AND_ASSIGN(HLessThan); }; -class HLessThanOrEqual : public HCondition { +class HLessThanOrEqual FINAL : public HCondition { public: HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3303,7 +3260,7 @@ class HLessThanOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; -class HGreaterThan : public HCondition { +class HGreaterThan FINAL : public HCondition { public: HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3340,7 +3297,7 @@ class HGreaterThan : public HCondition { DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; -class HGreaterThanOrEqual : public HCondition { +class HGreaterThanOrEqual FINAL : public HCondition { public: HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3377,7 +3334,7 @@ class HGreaterThanOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; -class HBelow : public HCondition { +class HBelow FINAL : public HCondition { public: HBelow(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3417,7 +3374,7 @@ class HBelow : public HCondition { DISALLOW_COPY_AND_ASSIGN(HBelow); }; -class HBelowOrEqual : public HCondition { +class HBelowOrEqual FINAL : public HCondition { public: HBelowOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3457,7 +3414,7 @@ class HBelowOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); }; -class HAbove : public HCondition { +class HAbove FINAL : public HCondition { public: HAbove(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3497,7 +3454,7 @@ class HAbove : public HCondition { DISALLOW_COPY_AND_ASSIGN(HAbove); }; -class HAboveOrEqual : public HCondition { +class HAboveOrEqual FINAL : public HCondition { public: HAboveOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3539,7 +3496,7 @@ class HAboveOrEqual : public HCondition { // Instruction to check how two inputs compare to each other. // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. -class HCompare : public HBinaryOperation { +class HCompare FINAL : public HBinaryOperation { public: // Note that `comparison_type` is the type of comparison performed // between the comparison's inputs, not the type of the instantiated @@ -3588,7 +3545,7 @@ class HCompare : public HBinaryOperation { return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return GetPackedFields() == other->AsCompare()->GetPackedFields(); } @@ -3628,21 +3585,21 @@ class HCompare : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HCompare); }; -class HNewInstance : public HExpression<2> { +class HNewInstance FINAL : public HExpression<2> { public: HNewInstance(HInstruction* cls, HCurrentMethod* current_method, uint32_t dex_pc, uint16_t type_index, const DexFile& dex_file, - bool can_throw, + bool needs_access_check, bool finalizable, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), entrypoint_(entrypoint) { - SetPackedFlag<kFlagCanThrow>(can_throw); + SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); @@ -3654,10 +3611,11 @@ class HNewInstance : public HExpression<2> { // Calls runtime so needs an environment. bool NeedsEnvironment() const OVERRIDE { return true; } - // It may throw when called on type that's not instantiable/accessible. - // It can throw OOME. - // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; } + // Can throw errors when out-of-memory or if it's not instantiable/accessible. + bool CanThrow() const OVERRIDE { return true; } + + // Needs to call into runtime to make sure it's instantiable/accessible. + bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } @@ -3674,8 +3632,8 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: - static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; - static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -3717,10 +3675,13 @@ enum IntrinsicExceptions { class HInvoke : public HInstruction { public: - size_t InputCount() const OVERRIDE { return inputs_.size(); } - bool NeedsEnvironment() const OVERRIDE; + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE { + return ArrayRef<HUserRecord<HInstruction*>>(inputs_); + } + void SetArgumentAt(size_t index, HInstruction* argument) { SetRawInputAt(index, argument); } @@ -3757,7 +3718,7 @@ class HInvoke : public HInstruction { bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return intrinsic_ != Intrinsics::kNone && intrinsic_ == other->AsInvoke()->intrinsic_; } @@ -3808,14 +3769,6 @@ class HInvoke : public HInstruction { SetPackedFlag<kFlagCanThrow>(true); } - 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; - } - void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); } uint32_t number_of_arguments_; @@ -3830,7 +3783,7 @@ class HInvoke : public HInstruction { DISALLOW_COPY_AND_ASSIGN(HInvoke); }; -class HInvokeUnresolved : public HInvoke { +class HInvokeUnresolved FINAL : public HInvoke { public: HInvokeUnresolved(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -3853,7 +3806,7 @@ class HInvokeUnresolved : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); }; -class HInvokeStaticOrDirect : public HInvoke { +class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class // initialization (clinit) check of its declaring class. @@ -3982,6 +3935,25 @@ class HInvokeStaticOrDirect : public HInvoke { InsertInputAt(GetSpecialInputIndex(), input); } + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE { + ArrayRef<HUserRecord<HInstruction*>> input_records = HInvoke::GetInputRecords(); + if (kIsDebugBuild && IsStaticWithExplicitClinitCheck()) { + DCHECK(!input_records.empty()); + DCHECK_GT(input_records.size(), GetNumberOfArguments()); + HInstruction* last_input = input_records.back().GetInstruction(); + // Note: `last_input` may be null during arguments setup. + if (last_input != nullptr) { + // `last_input` is the last input of a static invoke marked as having + // an explicit clinit check. It must either be: + // - an art::HClinitCheck instruction, set by art::HGraphBuilder; or + // - an art::HLoadClass instruction, set by art::PrepareForRegisterAllocation. + DCHECK(last_input->IsClinitCheck() || last_input->IsLoadClass()) << last_input->DebugName(); + } + } + return input_records; + } + bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // We access the method via the dex cache so we can't do an implicit null check. // TODO: for intrinsics we can generate implicit null checks. @@ -4066,8 +4038,8 @@ class HInvokeStaticOrDirect : public HInvoke { // instruction; only relevant for static calls with explicit clinit check. void RemoveExplicitClinitCheck(ClinitCheckRequirement new_requirement) { DCHECK(IsStaticWithExplicitClinitCheck()); - size_t last_input_index = InputCount() - 1; - HInstruction* last_input = InputAt(last_input_index); + size_t last_input_index = inputs_.size() - 1u; + HInstruction* last_input = inputs_.back().GetInstruction(); DCHECK(last_input != nullptr); DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); @@ -4096,20 +4068,6 @@ class HInvokeStaticOrDirect : public HInvoke { DECLARE_INSTRUCTION(InvokeStaticOrDirect); protected: - const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { - const HUserRecord<HInstruction*> input_record = HInvoke::InputRecordAt(i); - if (kIsDebugBuild && IsStaticWithExplicitClinitCheck() && (i == InputCount() - 1)) { - HInstruction* input = input_record.GetInstruction(); - // `input` is the last input of a static invoke marked as having - // an explicit clinit check. It must either be: - // - an art::HClinitCheck instruction, set by art::HGraphBuilder; or - // - an art::HLoadClass instruction, set by art::PrepareForRegisterAllocation. - DCHECK(input != nullptr); - DCHECK(input->IsClinitCheck() || input->IsLoadClass()) << input->DebugName(); - } - return input_record; - } - void InsertInputAt(size_t index, HInstruction* input); void RemoveInputAt(size_t index); @@ -4142,7 +4100,7 @@ class HInvokeStaticOrDirect : public HInvoke { std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs); std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs); -class HInvokeVirtual : public HInvoke { +class HInvokeVirtual FINAL : public HInvoke { public: HInvokeVirtual(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -4168,7 +4126,7 @@ class HInvokeVirtual : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual); }; -class HInvokeInterface : public HInvoke { +class HInvokeInterface FINAL : public HInvoke { public: HInvokeInterface(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -4195,7 +4153,7 @@ class HInvokeInterface : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeInterface); }; -class HNeg : public HUnaryOperation { +class HNeg FINAL : public HUnaryOperation { public: HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(result_type, input, dex_pc) { @@ -4223,7 +4181,7 @@ class HNeg : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNeg); }; -class HNewArray : public HExpression<2> { +class HNewArray FINAL : public HExpression<2> { public: HNewArray(HInstruction* length, HCurrentMethod* current_method, @@ -4262,7 +4220,7 @@ class HNewArray : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HNewArray); }; -class HAdd : public HBinaryOperation { +class HAdd FINAL : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, @@ -4297,7 +4255,7 @@ class HAdd : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HAdd); }; -class HSub : public HBinaryOperation { +class HSub FINAL : public HBinaryOperation { public: HSub(Primitive::Type result_type, HInstruction* left, @@ -4330,7 +4288,7 @@ class HSub : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HSub); }; -class HMul : public HBinaryOperation { +class HMul FINAL : public HBinaryOperation { public: HMul(Primitive::Type result_type, HInstruction* left, @@ -4365,7 +4323,7 @@ class HMul : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HMul); }; -class HDiv : public HBinaryOperation { +class HDiv FINAL : public HBinaryOperation { public: HDiv(Primitive::Type result_type, HInstruction* left, @@ -4417,7 +4375,7 @@ class HDiv : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HDiv); }; -class HRem : public HBinaryOperation { +class HRem FINAL : public HBinaryOperation { public: HRem(Primitive::Type result_type, HInstruction* left, @@ -4468,7 +4426,7 @@ class HRem : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HRem); }; -class HDivZeroCheck : public HExpression<1> { +class HDivZeroCheck FINAL : public HExpression<1> { public: // `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException` // constructor. @@ -4481,7 +4439,7 @@ class HDivZeroCheck : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4494,7 +4452,7 @@ class HDivZeroCheck : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); }; -class HShl : public HBinaryOperation { +class HShl FINAL : public HBinaryOperation { public: HShl(Primitive::Type result_type, HInstruction* value, @@ -4540,7 +4498,7 @@ class HShl : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HShl); }; -class HShr : public HBinaryOperation { +class HShr FINAL : public HBinaryOperation { public: HShr(Primitive::Type result_type, HInstruction* value, @@ -4586,7 +4544,7 @@ class HShr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HShr); }; -class HUShr : public HBinaryOperation { +class HUShr FINAL : public HBinaryOperation { public: HUShr(Primitive::Type result_type, HInstruction* value, @@ -4634,7 +4592,7 @@ class HUShr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HUShr); }; -class HAnd : public HBinaryOperation { +class HAnd FINAL : public HBinaryOperation { public: HAnd(Primitive::Type result_type, HInstruction* left, @@ -4671,7 +4629,7 @@ class HAnd : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HAnd); }; -class HOr : public HBinaryOperation { +class HOr FINAL : public HBinaryOperation { public: HOr(Primitive::Type result_type, HInstruction* left, @@ -4708,7 +4666,7 @@ class HOr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HOr); }; -class HXor : public HBinaryOperation { +class HXor FINAL : public HBinaryOperation { public: HXor(Primitive::Type result_type, HInstruction* left, @@ -4745,7 +4703,7 @@ class HXor : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HXor); }; -class HRor : public HBinaryOperation { +class HRor FINAL : public HBinaryOperation { public: HRor(Primitive::Type result_type, HInstruction* value, HInstruction* distance) : HBinaryOperation(result_type, value, distance) { @@ -4798,7 +4756,7 @@ class HRor : public HBinaryOperation { // The value of a parameter in this method. Its location depends on // the calling convention. -class HParameterValue : public HExpression<0> { +class HParameterValue FINAL : public HExpression<0> { public: HParameterValue(const DexFile& dex_file, uint16_t type_index, @@ -4840,13 +4798,13 @@ class HParameterValue : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; -class HNot : public HUnaryOperation { +class HNot FINAL : public HUnaryOperation { public: HNot(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(result_type, input, dex_pc) {} bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4873,13 +4831,13 @@ class HNot : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNot); }; -class HBooleanNot : public HUnaryOperation { +class HBooleanNot FINAL : public HUnaryOperation { public: explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(Primitive::Type::kPrimBoolean, input, dex_pc) {} bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4910,7 +4868,7 @@ class HBooleanNot : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HBooleanNot); }; -class HTypeConversion : public HExpression<1> { +class HTypeConversion FINAL : public HExpression<1> { public: // Instantiate a type conversion of `input` to `result_type`. HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc) @@ -4927,7 +4885,9 @@ class HTypeConversion : public HExpression<1> { Primitive::Type GetResultType() const { return GetType(); } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } // Try to statically evaluate the conversion and return a HConstant // containing the result. If the input cannot be converted, return nullptr. @@ -4953,7 +4913,7 @@ class HTypeConversion : public HExpression<1> { static constexpr uint32_t kNoRegNumber = -1; -class HNullCheck : public HExpression<1> { +class HNullCheck FINAL : public HExpression<1> { public: // `HNullCheck` can trigger GC, as it may call the `NullPointerException` // constructor. @@ -4963,7 +4923,7 @@ class HNullCheck : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -5015,7 +4975,7 @@ class FieldInfo : public ValueObject { const Handle<mirror::DexCache> dex_cache_; }; -class HInstanceFieldGet : public HExpression<1> { +class HInstanceFieldGet FINAL : public HExpression<1> { public: HInstanceFieldGet(HInstruction* value, Primitive::Type field_type, @@ -5041,8 +5001,8 @@ class HInstanceFieldGet : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - HInstanceFieldGet* other_get = other->AsInstanceFieldGet(); + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { + const HInstanceFieldGet* other_get = other->AsInstanceFieldGet(); return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue(); } @@ -5067,7 +5027,7 @@ class HInstanceFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); }; -class HInstanceFieldSet : public HTemplateInstruction<2> { +class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, @@ -5118,22 +5078,16 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; -class HArrayGet : public HExpression<2> { +class HArrayGet FINAL : public HExpression<2> { public: - HArrayGet(HInstruction* array, - HInstruction* index, - Primitive::Type type, - uint32_t dex_pc, - SideEffects additional_side_effects = SideEffects::None()) - : HExpression(type, - SideEffects::ArrayReadOfType(type).Union(additional_side_effects), - dex_pc) { + HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc) + : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) { SetRawInputAt(0, array); SetRawInputAt(1, index); } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { @@ -5170,17 +5124,14 @@ class HArrayGet : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; -class HArraySet : public HTemplateInstruction<3> { +class HArraySet FINAL : public HTemplateInstruction<3> { public: HArraySet(HInstruction* array, HInstruction* index, HInstruction* value, Primitive::Type expected_component_type, - uint32_t dex_pc, - SideEffects additional_side_effects = SideEffects::None()) - : HTemplateInstruction( - SideEffectsForArchRuntimeCalls(value->GetType()).Union(additional_side_effects), - dex_pc) { + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::None(), dex_pc) { SetPackedField<ExpectedComponentTypeField>(expected_component_type); SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); SetPackedFlag<kFlagValueCanBeNull>(true); @@ -5188,8 +5139,8 @@ class HArraySet : public HTemplateInstruction<3> { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); - // We can now call component type logic to set correct type-based side effects. - AddSideEffects(SideEffects::ArrayWriteOfType(GetComponentType())); + // Make a best guess now, may be refined during SSA building. + ComputeSideEffects(); } bool NeedsEnvironment() const OVERRIDE { @@ -5242,6 +5193,12 @@ class HArraySet : public HTemplateInstruction<3> { return GetPackedField<ExpectedComponentTypeField>(); } + void ComputeSideEffects() { + Primitive::Type type = GetComponentType(); + SetSideEffects(SideEffects::ArrayWriteOfType(type).Union( + SideEffectsForArchRuntimeCalls(type))); + } + static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { return (value_type == Primitive::kPrimNot) ? SideEffects::CanTriggerGC() : SideEffects::None(); } @@ -5267,7 +5224,7 @@ class HArraySet : public HTemplateInstruction<3> { DISALLOW_COPY_AND_ASSIGN(HArraySet); }; -class HArrayLength : public HExpression<1> { +class HArrayLength FINAL : public HExpression<1> { public: HArrayLength(HInstruction* array, uint32_t dex_pc) : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { @@ -5277,20 +5234,33 @@ class HArrayLength : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { return obj == InputAt(0); } + void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); } + bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); } + DECLARE_INSTRUCTION(ArrayLength); private: + // We treat a String as an array, creating the HArrayLength from String.length() + // or String.isEmpty() intrinsic in the instruction simplifier. We can always + // determine whether a particular HArrayLength is actually a String.length() by + // looking at the type of the input but that requires holding the mutator lock, so + // we prefer to use a flag, so that code generators don't need to do the locking. + static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; + static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + DISALLOW_COPY_AND_ASSIGN(HArrayLength); }; -class HBoundsCheck : public HExpression<2> { +class HBoundsCheck FINAL : public HExpression<2> { public: // `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException` // constructor. @@ -5302,7 +5272,7 @@ class HBoundsCheck : public HExpression<2> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -5318,7 +5288,7 @@ class HBoundsCheck : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; -class HSuspendCheck : public HTemplateInstruction<0> { +class HSuspendCheck FINAL : public HTemplateInstruction<0> { public: explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {} @@ -5360,7 +5330,7 @@ class HNativeDebugInfo : public HTemplateInstruction<0> { /** * Instruction to load a Class object. */ -class HLoadClass : public HExpression<1> { +class HLoadClass FINAL : public HExpression<1> { public: HLoadClass(HCurrentMethod* current_method, uint16_t type_index, @@ -5386,7 +5356,7 @@ class HLoadClass : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { // Note that we don't need to test for generate_clinit_check_. // Whether or not we need to generate the clinit check is processed in // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. @@ -5464,7 +5434,7 @@ class HLoadClass : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HLoadClass); }; -class HLoadString : public HExpression<1> { +class HLoadString FINAL : public HInstruction { public: // Determines how to load the String. enum class LoadKind { @@ -5503,12 +5473,12 @@ class HLoadString : public HExpression<1> { uint32_t string_index, const DexFile& dex_file, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), string_index_(string_index) { SetPackedFlag<kFlagIsInDexCache>(false); SetPackedField<LoadKindField>(LoadKind::kDexCacheViaMethod); load_data_.ref.dex_file = &dex_file; - SetRawInputAt(0, current_method); } void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) { @@ -5555,7 +5525,7 @@ class HLoadString : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE; + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE; size_t ComputeHashCode() const OVERRIDE { return string_index_; } @@ -5588,18 +5558,25 @@ class HLoadString : public HExpression<1> { SetPackedFlag<kFlagIsInDexCache>(true); DCHECK(!NeedsEnvironment()); RemoveEnvironment(); + SetSideEffects(SideEffects::None()); } - size_t InputCount() const OVERRIDE { - return (InputAt(0) != nullptr) ? 1u : 0u; + void AddSpecialInput(HInstruction* special_input); + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - void AddSpecialInput(HInstruction* special_input); + Primitive::Type GetType() const OVERRIDE { + return Primitive::kPrimNot; + } DECLARE_INSTRUCTION(LoadString); private: - static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsInDexCache = kNumberOfGenericPackedBits; static constexpr size_t kFieldLoadKind = kFlagIsInDexCache + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast)); @@ -5623,6 +5600,8 @@ class HLoadString : public HExpression<1> { void SetLoadKindInternal(LoadKind load_kind); + HUserRecord<HInstruction*> special_input_; + // String index serves also as the hash code and it's also needed for slow-paths, // so it must not be overwritten with other load data. uint32_t string_index_; @@ -5657,15 +5636,17 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind(); - DCHECK(InputAt(0) == nullptr); - SetRawInputAt(0u, special_input); + // HLoadString::GetInputRecords() returns an empty array at this point, + // so use the GetInputRecords() from the base class to set the input record. + DCHECK(special_input_.GetInstruction() == nullptr); + special_input_ = HUserRecord<HInstruction*>(special_input); special_input->AddUseAt(this, 0); } /** * Performs an initialization check on its Class object input. */ -class HClinitCheck : public HExpression<1> { +class HClinitCheck FINAL : public HExpression<1> { public: HClinitCheck(HLoadClass* constant, uint32_t dex_pc) : HExpression( @@ -5676,7 +5657,7 @@ class HClinitCheck : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -5695,7 +5676,7 @@ class HClinitCheck : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HClinitCheck); }; -class HStaticFieldGet : public HExpression<1> { +class HStaticFieldGet FINAL : public HExpression<1> { public: HStaticFieldGet(HInstruction* cls, Primitive::Type field_type, @@ -5722,8 +5703,8 @@ class HStaticFieldGet : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - HStaticFieldGet* other_get = other->AsStaticFieldGet(); + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { + const HStaticFieldGet* other_get = other->AsStaticFieldGet(); return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue(); } @@ -5744,7 +5725,7 @@ class HStaticFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet); }; -class HStaticFieldSet : public HTemplateInstruction<2> { +class HStaticFieldSet FINAL : public HTemplateInstruction<2> { public: HStaticFieldSet(HInstruction* cls, HInstruction* value, @@ -5792,7 +5773,7 @@ class HStaticFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; -class HUnresolvedInstanceFieldGet : public HExpression<1> { +class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { public: HUnresolvedInstanceFieldGet(HInstruction* obj, Primitive::Type field_type, @@ -5817,7 +5798,7 @@ class HUnresolvedInstanceFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet); }; -class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { +class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { public: HUnresolvedInstanceFieldSet(HInstruction* obj, HInstruction* value, @@ -5855,7 +5836,7 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); }; -class HUnresolvedStaticFieldGet : public HExpression<0> { +class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { public: HUnresolvedStaticFieldGet(Primitive::Type field_type, uint32_t field_index, @@ -5878,7 +5859,7 @@ class HUnresolvedStaticFieldGet : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet); }; -class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { +class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { public: HUnresolvedStaticFieldSet(HInstruction* value, Primitive::Type field_type, @@ -5915,7 +5896,7 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { }; // Implement the move-exception DEX instruction. -class HLoadException : public HExpression<0> { +class HLoadException FINAL : public HExpression<0> { public: explicit HLoadException(uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc) {} @@ -5930,7 +5911,7 @@ class HLoadException : public HExpression<0> { // Implicit part of move-exception which clears thread-local exception storage. // Must not be removed because the runtime expects the TLS to get cleared. -class HClearException : public HTemplateInstruction<0> { +class HClearException FINAL : public HTemplateInstruction<0> { public: explicit HClearException(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {} @@ -5941,7 +5922,7 @@ class HClearException : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HClearException); }; -class HThrow : public HTemplateInstruction<1> { +class HThrow FINAL : public HTemplateInstruction<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { @@ -5978,7 +5959,7 @@ enum class TypeCheckKind { std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf : public HExpression<2> { +class HInstanceOf FINAL : public HExpression<2> { public: HInstanceOf(HInstruction* object, HLoadClass* constant, @@ -5995,7 +5976,7 @@ class HInstanceOf : public HExpression<2> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -6032,7 +6013,7 @@ class HInstanceOf : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; -class HBoundType : public HExpression<1> { +class HBoundType FINAL : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), @@ -6076,7 +6057,7 @@ class HBoundType : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HBoundType); }; -class HCheckCast : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, HLoadClass* constant, @@ -6091,7 +6072,7 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -6121,7 +6102,7 @@ class HCheckCast : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; -class HMemoryBarrier : public HTemplateInstruction<0> { +class HMemoryBarrier FINAL : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( @@ -6146,7 +6127,7 @@ class HMemoryBarrier : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; -class HMonitorOperation : public HTemplateInstruction<1> { +class HMonitorOperation FINAL : public HTemplateInstruction<1> { public: enum class OperationKind { kEnter, @@ -6191,7 +6172,7 @@ class HMonitorOperation : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; -class HSelect : public HExpression<3> { +class HSelect FINAL : public HExpression<3> { public: HSelect(HInstruction* condition, HInstruction* true_value, @@ -6214,7 +6195,9 @@ class HSelect : public HExpression<3> { HInstruction* GetCondition() const { return InputAt(2); } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } bool CanBeNull() const OVERRIDE { return GetTrueValue()->CanBeNull() || GetFalseValue()->CanBeNull(); @@ -6304,7 +6287,7 @@ std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs); static constexpr size_t kDefaultNumberOfMoves = 4; -class HParallelMove : public HTemplateInstruction<0> { +class HParallelMove FINAL : public HTemplateInstruction<0> { public: explicit HParallelMove(ArenaAllocator* arena, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc), |