/* * Copyright (C) 2014 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_NODES_H_ #define ART_COMPILER_OPTIMIZING_NODES_H_ #include "locations.h" #include "offsets.h" #include "utils/allocation.h" #include "utils/arena_bit_vector.h" #include "utils/growable_array.h" namespace art { class HBasicBlock; class HEnvironment; class HInstruction; class HIntConstant; class HGraphVisitor; class HPhi; class LiveInterval; class LocationSummary; static const int kDefaultNumberOfBlocks = 8; static const int kDefaultNumberOfSuccessors = 2; static const int kDefaultNumberOfPredecessors = 2; static const int kDefaultNumberOfBackEdges = 1; enum IfCondition { kCondEQ, kCondNE, kCondLT, kCondLE, kCondGT, kCondGE, }; class HInstructionList { public: HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {} void AddInstruction(HInstruction* instruction); void RemoveInstruction(HInstruction* instruction); private: HInstruction* first_instruction_; HInstruction* last_instruction_; friend class HBasicBlock; friend class HInstructionIterator; friend class HBackwardInstructionIterator; DISALLOW_COPY_AND_ASSIGN(HInstructionList); }; // Control-flow graph of a method. Contains a list of basic blocks. class HGraph : public ArenaObject { public: explicit HGraph(ArenaAllocator* arena) : arena_(arena), blocks_(arena, kDefaultNumberOfBlocks), reverse_post_order_(arena, kDefaultNumberOfBlocks), maximum_number_of_out_vregs_(0), number_of_vregs_(0), number_of_in_vregs_(0), number_of_temporaries_(0), current_instruction_id_(0) {} ArenaAllocator* GetArena() const { return arena_; } const GrowableArray& GetBlocks() const { return blocks_; } HBasicBlock* GetEntryBlock() const { return entry_block_; } HBasicBlock* GetExitBlock() const { return exit_block_; } void SetEntryBlock(HBasicBlock* block) { entry_block_ = block; } void SetExitBlock(HBasicBlock* block) { exit_block_ = block; } void AddBlock(HBasicBlock* block); void BuildDominatorTree(); void TransformToSSA(); void SimplifyCFG(); // Find all natural loops in this graph. Aborts computation and returns false // if one loop is not natural, that is the header does not dominate the back // edge. bool FindNaturalLoops() const; void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); void SimplifyLoop(HBasicBlock* header); int GetNextInstructionId() { return current_instruction_id_++; } uint16_t GetMaximumNumberOfOutVRegs() const { return maximum_number_of_out_vregs_; } void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) { maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_); } void UpdateNumberOfTemporaries(size_t count) { number_of_temporaries_ = std::max(count, number_of_temporaries_); } size_t GetNumberOfTemporaries() const { return number_of_temporaries_; } void SetNumberOfVRegs(uint16_t number_of_vregs) { number_of_vregs_ = number_of_vregs; } uint16_t GetNumberOfVRegs() const { return number_of_vregs_; } void SetNumberOfInVRegs(uint16_t value) { number_of_in_vregs_ = value; } uint16_t GetNumberOfInVRegs() const { return number_of_in_vregs_; } const GrowableArray& GetReversePostOrder() const { return reverse_post_order_; } private: HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; void VisitBlockForDominatorTree(HBasicBlock* block, HBasicBlock* predecessor, GrowableArray* visits); void FindBackEdges(ArenaBitVector* visited); void VisitBlockForBackEdges(HBasicBlock* block, ArenaBitVector* visited, ArenaBitVector* visiting); void RemoveDeadBlocks(const ArenaBitVector& visited) const; ArenaAllocator* const arena_; // List of blocks in insertion order. GrowableArray blocks_; // List of blocks to perform a reverse post order tree traversal. GrowableArray reverse_post_order_; HBasicBlock* entry_block_; HBasicBlock* exit_block_; // The maximum number of virtual registers arguments passed to a HInvoke in this graph. uint16_t maximum_number_of_out_vregs_; // The number of virtual registers in this method. Contains the parameters. uint16_t number_of_vregs_; // The number of virtual registers used by parameters of this method. uint16_t number_of_in_vregs_; // The number of temporaries that will be needed for the baseline compiler. size_t number_of_temporaries_; // The current id to assign to a newly added instruction. See HInstruction.id_. int current_instruction_id_; DISALLOW_COPY_AND_ASSIGN(HGraph); }; class HLoopInformation : public ArenaObject { public: HLoopInformation(HBasicBlock* header, HGraph* graph) : header_(header), back_edges_(graph->GetArena(), kDefaultNumberOfBackEdges), blocks_(graph->GetArena(), graph->GetBlocks().Size(), false) {} HBasicBlock* GetHeader() const { return header_; } void AddBackEdge(HBasicBlock* back_edge) { back_edges_.Add(back_edge); } void RemoveBackEdge(HBasicBlock* back_edge) { back_edges_.Delete(back_edge); } bool IsBackEdge(HBasicBlock* block) { for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) { if (back_edges_.Get(i) == block) return true; } return false; } int NumberOfBackEdges() const { return back_edges_.Size(); } HBasicBlock* GetPreHeader() const; const GrowableArray& GetBackEdges() const { return back_edges_; } void ClearBackEdges() { back_edges_.Reset(); } // Find blocks that are part of this loop. Returns whether the loop is a natural loop, // that is the header dominates the back edge. bool Populate(); // Returns whether this loop information contains `block`. // Note that this loop information *must* be populated before entering this function. bool Contains(const HBasicBlock& block) const; // Returns whether this loop information is an inner loop of `other`. // Note that `other` *must* be populated before entering this function. bool IsIn(const HLoopInformation& other) const; const ArenaBitVector& GetBlocks() const { return blocks_; } private: // Internal recursive implementation of `Populate`. void PopulateRecursive(HBasicBlock* block); HBasicBlock* header_; GrowableArray back_edges_; ArenaBitVector blocks_; DISALLOW_COPY_AND_ASSIGN(HLoopInformation); }; static constexpr size_t kNoLifetime = -1; // A block in a method. Contains the list of instructions represented // as a double linked list. Each block knows its predecessors and // successors. class HBasicBlock : public ArenaObject { public: explicit HBasicBlock(HGraph* graph) : graph_(graph), predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors), successors_(graph->GetArena(), kDefaultNumberOfSuccessors), loop_information_(nullptr), dominator_(nullptr), block_id_(-1), lifetime_start_(kNoLifetime), lifetime_end_(kNoLifetime) {} const GrowableArray& GetPredecessors() const { return predecessors_; } const GrowableArray& GetSuccessors() const { return successors_; } void AddBackEdge(HBasicBlock* back_edge) { if (loop_information_ == nullptr) { loop_information_ = new (graph_->GetArena()) HLoopInformation(this, graph_); } DCHECK_EQ(loop_information_->GetHeader(), this); loop_information_->AddBackEdge(back_edge); } HGraph* GetGraph() const { return graph_; } int GetBlockId() const { return block_id_; } void SetBlockId(int id) { block_id_ = id; } HBasicBlock* GetDominator() const { return dominator_; } void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; } int NumberOfBackEdges() const { return loop_information_ == nullptr ? 0 : loop_information_->NumberOfBackEdges(); } HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; } HInstruction* GetLastInstruction() const { return instructions_.last_instruction_; } const HInstructionList& GetInstructions() const { return instructions_; } const HInstructionList& GetPhis() const { return phis_; } HInstruction* GetFirstPhi() const { return phis_.first_instruction_; } void AddSuccessor(HBasicBlock* block) { successors_.Add(block); block->predecessors_.Add(this); } void ReplaceSuccessor(HBasicBlock* existing, HBasicBlock* new_block) { size_t successor_index = GetSuccessorIndexOf(existing); DCHECK_NE(successor_index, static_cast(-1)); existing->RemovePredecessor(this); new_block->predecessors_.Add(this); successors_.Put(successor_index, new_block); } void RemovePredecessor(HBasicBlock* block) { predecessors_.Delete(block); } void ClearAllPredecessors() { predecessors_.Reset(); } void AddPredecessor(HBasicBlock* block) { predecessors_.Add(block); block->successors_.Add(this); } size_t GetPredecessorIndexOf(HBasicBlock* predecessor) { for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { if (predecessors_.Get(i) == predecessor) { return i; } } return -1; } size_t GetSuccessorIndexOf(HBasicBlock* successor) { for (size_t i = 0, e = successors_.Size(); i < e; ++i) { if (successors_.Get(i) == successor) { return i; } } return -1; } void AddInstruction(HInstruction* instruction); void RemoveInstruction(HInstruction* instruction); void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor); void AddPhi(HPhi* phi); void RemovePhi(HPhi* phi); bool IsLoopHeader() const { return (loop_information_ != nullptr) && (loop_information_->GetHeader() == this); } HLoopInformation* GetLoopInformation() const { return loop_information_; } // Set the loop_information_ on this block. This method overrides the current // loop_information if it is an outer loop of the passed loop information. void SetInLoop(HLoopInformation* info) { if (IsLoopHeader()) { // Nothing to do. This just means `info` is an outer loop. } else if (loop_information_ == nullptr) { loop_information_ = info; } else if (loop_information_->Contains(*info->GetHeader())) { // Block is currently part of an outer loop. Make it part of this inner loop. // Note that a non loop header having a loop information means this loop information // has already been populated loop_information_ = info; } else { // Block is part of an inner loop. Do not update the loop information. // Note that we cannot do the check `info->Contains(loop_information_)->GetHeader()` // at this point, because this method is being called while populating `info`. } } bool IsInLoop() const { return loop_information_ != nullptr; } // Returns wheter this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; size_t GetLifetimeStart() const { return lifetime_start_; } size_t GetLifetimeEnd() const { return lifetime_end_; } void SetLifetimeStart(size_t start) { lifetime_start_ = start; } void SetLifetimeEnd(size_t end) { lifetime_end_ = end; } private: HGraph* const graph_; GrowableArray predecessors_; GrowableArray successors_; HInstructionList instructions_; HInstructionList phis_; HLoopInformation* loop_information_; HBasicBlock* dominator_; int block_id_; size_t lifetime_start_; size_t lifetime_end_; DISALLOW_COPY_AND_ASSIGN(HBasicBlock); }; #define FOR_EACH_INSTRUCTION(M) \ M(Add) \ M(Condition) \ M(Equal) \ M(NotEqual) \ M(LessThan) \ M(LessThanOrEqual) \ M(GreaterThan) \ M(GreaterThanOrEqual) \ M(Exit) \ M(Goto) \ M(If) \ M(IntConstant) \ M(InvokeStatic) \ M(LoadLocal) \ M(Local) \ M(LongConstant) \ M(NewInstance) \ M(Not) \ M(ParameterValue) \ M(ParallelMove) \ M(Phi) \ M(Return) \ M(ReturnVoid) \ M(StoreLocal) \ M(Sub) \ M(Compare) \ M(InstanceFieldGet) \ M(InstanceFieldSet) \ M(NullCheck) \ M(Temporary) \ #define FORWARD_DECLARATION(type) class H##type; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION #define DECLARE_INSTRUCTION(type) \ virtual const char* DebugName() const { return #type; } \ virtual H##type* As##type() { return this; } \ virtual void Accept(HGraphVisitor* visitor) \ template class HUseListNode : public ArenaObject { public: HUseListNode(T* user, size_t index, HUseListNode* tail) : user_(user), index_(index), tail_(tail) {} HUseListNode* GetTail() const { return tail_; } T* GetUser() const { return user_; } size_t GetIndex() const { return index_; } void SetTail(HUseListNode* node) { tail_ = node; } private: T* const user_; const size_t index_; HUseListNode* tail_; DISALLOW_COPY_AND_ASSIGN(HUseListNode); }; class HInstruction : public ArenaObject { public: HInstruction() : previous_(nullptr), next_(nullptr), block_(nullptr), id_(-1), ssa_index_(-1), uses_(nullptr), env_uses_(nullptr), environment_(nullptr), locations_(nullptr), live_interval_(nullptr), lifetime_position_(kNoLifetime) {} virtual ~HInstruction() {} HInstruction* GetNext() const { return next_; } HInstruction* GetPrevious() const { return previous_; } HBasicBlock* GetBlock() const { return block_; } void SetBlock(HBasicBlock* block) { block_ = block; } bool IsInBlock() const { return block_ != nullptr; } bool IsInLoop() const { return block_->IsInLoop(); } virtual size_t InputCount() const = 0; virtual HInstruction* InputAt(size_t i) const = 0; virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; } virtual void SetRawInputAt(size_t index, HInstruction* input) = 0; virtual bool NeedsEnvironment() const { return false; } virtual bool IsControlFlow() const { return false; } void AddUseAt(HInstruction* user, size_t index) { uses_ = new (block_->GetGraph()->GetArena()) HUseListNode(user, index, uses_); } void AddEnvUseAt(HEnvironment* user, size_t index) { env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode( user, index, env_uses_); } void RemoveUser(HInstruction* user, size_t index); HUseListNode* GetUses() const { return uses_; } HUseListNode* GetEnvUses() const { return env_uses_; } bool HasUses() const { return uses_ != nullptr || env_uses_ != nullptr; } bool HasEnvironmentUses() const { return env_uses_ != nullptr; } size_t NumberOfUses() const { // TODO: Optimize this method if it is used outside of the HGraphVisualizer. size_t result = 0; HUseListNode* current = uses_; while (current != nullptr) { current = current->GetTail(); ++result; } return result; } int GetId() const { return id_; } void SetId(int id) { id_ = id; } int GetSsaIndex() const { return ssa_index_; } void SetSsaIndex(int ssa_index) { ssa_index_ = ssa_index; } bool HasSsaIndex() const { return ssa_index_ != -1; } bool HasEnvironment() const { return environment_ != nullptr; } HEnvironment* GetEnvironment() const { return environment_; } void SetEnvironment(HEnvironment* environment) { environment_ = environment; } LocationSummary* GetLocations() const { return locations_; } void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); bool HasOnlyOneUse() const { return uses_ != nullptr && uses_->GetTail() == nullptr; } #define INSTRUCTION_TYPE_CHECK(type) \ bool Is##type() { return (As##type() != nullptr); } \ virtual H##type* As##type() { return nullptr; } FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK size_t GetLifetimePosition() const { return lifetime_position_; } void SetLifetimePosition(size_t position) { lifetime_position_ = position; } LiveInterval* GetLiveInterval() const { return live_interval_; } void SetLiveInterval(LiveInterval* interval) { live_interval_ = interval; } bool HasLiveInterval() const { return live_interval_ != nullptr; } private: HInstruction* previous_; HInstruction* next_; HBasicBlock* block_; // An instruction gets an id when it is added to the graph. // It reflects creation order. A negative id means the instruction // has not beed added to the graph. int id_; // When doing liveness analysis, instructions that have uses get an SSA index. int ssa_index_; // List of instructions that have this instruction as input. HUseListNode* uses_; // List of environments that contain this instruction. HUseListNode* env_uses_; HEnvironment* environment_; // Set by the code generator. LocationSummary* locations_; // Set by the liveness analysis. LiveInterval* live_interval_; // Set by the liveness analysis, this is the position in a linear // order of blocks where this instruction's live interval start. size_t lifetime_position_; friend class HBasicBlock; friend class HInstructionList; DISALLOW_COPY_AND_ASSIGN(HInstruction); }; template class HUseIterator : public ValueObject { public: explicit HUseIterator(HUseListNode* uses) : current_(uses) {} bool Done() const { return current_ == nullptr; } void Advance() { DCHECK(!Done()); current_ = current_->GetTail(); } HUseListNode* Current() const { DCHECK(!Done()); return current_; } private: HUseListNode* current_; friend class HValue; }; // A HEnvironment object contains the values of virtual registers at a given location. class HEnvironment : public ArenaObject { public: HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) { vregs_.SetSize(number_of_vregs); for (size_t i = 0; i < number_of_vregs; i++) { vregs_.Put(i, nullptr); } } void Populate(const GrowableArray& env) { for (size_t i = 0; i < env.Size(); i++) { HInstruction* instruction = env.Get(i); vregs_.Put(i, instruction); if (instruction != nullptr) { instruction->AddEnvUseAt(this, i); } } } void SetRawEnvAt(size_t index, HInstruction* instruction) { vregs_.Put(index, instruction); } GrowableArray* GetVRegs() { return &vregs_; } private: GrowableArray vregs_; DISALLOW_COPY_AND_ASSIGN(HEnvironment); }; 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) : instruction_(instructions.first_instruction_) { next_ = Done() ? nullptr : instruction_->GetNext(); } bool Done() const { return instruction_ == nullptr; } HInstruction* Current() const { return instruction_; } void Advance() { instruction_ = next_; next_ = Done() ? nullptr : instruction_->GetNext(); } private: HInstruction* instruction_; HInstruction* next_; DISALLOW_COPY_AND_ASSIGN(HInstructionIterator); }; class HBackwardInstructionIterator : public ValueObject { public: explicit HBackwardInstructionIterator(const HInstructionList& instructions) : instruction_(instructions.last_instruction_) { next_ = Done() ? nullptr : instruction_->GetPrevious(); } bool Done() const { return instruction_ == nullptr; } HInstruction* Current() const { return instruction_; } void Advance() { instruction_ = next_; next_ = Done() ? nullptr : instruction_->GetPrevious(); } private: HInstruction* instruction_; HInstruction* next_; DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator); }; // An embedded container with N elements of type T. Used (with partial // specialization for N=0) because embedded arrays cannot have size 0. template class EmbeddedArray { public: EmbeddedArray() : elements_() {} intptr_t GetLength() const { return N; } const T& operator[](intptr_t i) const { DCHECK_LT(i, GetLength()); return elements_[i]; } T& operator[](intptr_t i) { DCHECK_LT(i, GetLength()); return elements_[i]; } const T& At(intptr_t i) const { return (*this)[i]; } void SetAt(intptr_t i, const T& val) { (*this)[i] = val; } private: T elements_[N]; }; template class EmbeddedArray { public: intptr_t length() const { return 0; } const T& operator[](intptr_t i) const { LOG(FATAL) << "Unreachable"; static T sentinel = 0; return sentinel; } T& operator[](intptr_t i) { LOG(FATAL) << "Unreachable"; static T sentinel = 0; return sentinel; } }; template class HTemplateInstruction: public HInstruction { public: HTemplateInstruction() : inputs_() {} virtual ~HTemplateInstruction() {} virtual size_t InputCount() const { return N; } virtual HInstruction* InputAt(size_t i) const { return inputs_[i]; } protected: virtual void SetRawInputAt(size_t i, HInstruction* instruction) { inputs_[i] = instruction; } private: EmbeddedArray inputs_; friend class SsaBuilder; }; template class HExpression: public HTemplateInstruction { public: explicit HExpression(Primitive::Type type) : type_(type) {} virtual ~HExpression() {} virtual Primitive::Type GetType() const { return type_; } private: const Primitive::Type type_; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. class HReturnVoid : public HTemplateInstruction<0> { public: HReturnVoid() {} virtual bool IsControlFlow() const { return true; } DECLARE_INSTRUCTION(ReturnVoid); private: DISALLOW_COPY_AND_ASSIGN(HReturnVoid); }; // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. class HReturn : public HTemplateInstruction<1> { public: explicit HReturn(HInstruction* value) { SetRawInputAt(0, value); } virtual bool IsControlFlow() const { return true; } DECLARE_INSTRUCTION(Return); private: DISALLOW_COPY_AND_ASSIGN(HReturn); }; // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HTrow and HReturn) must branch to the // exit block. class HExit : public HTemplateInstruction<0> { public: HExit() {} virtual bool IsControlFlow() const { return true; } DECLARE_INSTRUCTION(Exit); private: DISALLOW_COPY_AND_ASSIGN(HExit); }; // Jumps from one block to another. class HGoto : public HTemplateInstruction<0> { public: HGoto() {} HBasicBlock* GetSuccessor() const { return GetBlock()->GetSuccessors().Get(0); } virtual bool IsControlFlow() const { return true; } DECLARE_INSTRUCTION(Goto); private: DISALLOW_COPY_AND_ASSIGN(HGoto); }; // Conditional branch. A block ending with an HIf instruction must have // two successors. class HIf : public HTemplateInstruction<1> { public: explicit HIf(HInstruction* input) { SetRawInputAt(0, input); } HBasicBlock* IfTrueSuccessor() const { return GetBlock()->GetSuccessors().Get(0); } HBasicBlock* IfFalseSuccessor() const { return GetBlock()->GetSuccessors().Get(1); } virtual bool IsControlFlow() const { return true; } DECLARE_INSTRUCTION(If); virtual bool IsIfInstruction() const { return true; } private: DISALLOW_COPY_AND_ASSIGN(HIf); }; class HBinaryOperation : public HExpression<2> { public: HBinaryOperation(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HExpression(result_type) { SetRawInputAt(0, left); SetRawInputAt(1, right); } HInstruction* GetLeft() const { return InputAt(0); } HInstruction* GetRight() const { return InputAt(1); } Primitive::Type GetResultType() const { return GetType(); } virtual bool IsCommutative() { return false; } private: DISALLOW_COPY_AND_ASSIGN(HBinaryOperation); }; class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second) : HBinaryOperation(Primitive::kPrimBoolean, first, second) {} virtual bool IsCommutative() { return true; } bool NeedsMaterialization() const; DECLARE_INSTRUCTION(Condition); virtual IfCondition GetCondition() const = 0; private: DISALLOW_COPY_AND_ASSIGN(HCondition); }; // Instruction to check if two inputs are equal to each other. class HEqual : public HCondition { public: HEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(Equal); virtual IfCondition GetCondition() const { return kCondEQ; } private: DISALLOW_COPY_AND_ASSIGN(HEqual); }; class HNotEqual : public HCondition { public: HNotEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(NotEqual); virtual IfCondition GetCondition() const { return kCondNE; } private: DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; class HLessThan : public HCondition { public: HLessThan(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(LessThan); virtual IfCondition GetCondition() const { return kCondLT; } private: DISALLOW_COPY_AND_ASSIGN(HLessThan); }; class HLessThanOrEqual : public HCondition { public: HLessThanOrEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(LessThanOrEqual); virtual IfCondition GetCondition() const { return kCondLE; } private: DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; class HGreaterThan : public HCondition { public: HGreaterThan(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(GreaterThan); virtual IfCondition GetCondition() const { return kCondGT; } private: DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; class HGreaterThanOrEqual : public HCondition { public: HGreaterThanOrEqual(HInstruction* first, HInstruction* second) : HCondition(first, second) {} DECLARE_INSTRUCTION(GreaterThanOrEqual); virtual IfCondition GetCondition() const { return kCondGE; } private: DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; // 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 { public: HCompare(Primitive::Type type, HInstruction* first, HInstruction* second) : HBinaryOperation(Primitive::kPrimInt, first, second) { DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } DECLARE_INSTRUCTION(Compare); private: 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) : 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: explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) { SetRawInputAt(0, local); } HLocal* GetLocal() const { return reinterpret_cast(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) { SetRawInputAt(0, local); SetRawInputAt(1, value); } HLocal* GetLocal() const { return reinterpret_cast(InputAt(0)); } DECLARE_INSTRUCTION(StoreLocal); private: DISALLOW_COPY_AND_ASSIGN(HStoreLocal); }; // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). class HIntConstant : public HExpression<0> { public: explicit HIntConstant(int32_t value) : HExpression(Primitive::kPrimInt), value_(value) {} int32_t GetValue() const { return value_; } DECLARE_INSTRUCTION(IntConstant); private: const int32_t value_; DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; class HLongConstant : public HExpression<0> { public: explicit HLongConstant(int64_t value) : HExpression(Primitive::kPrimLong), value_(value) {} int64_t GetValue() const { return value_; } virtual Primitive::Type GetType() const { return Primitive::kPrimLong; } DECLARE_INSTRUCTION(LongConstant); private: const int64_t value_; DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; class HInvoke : public HInstruction { public: HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, Primitive::Type return_type, uint32_t dex_pc) : inputs_(arena, number_of_arguments), return_type_(return_type), dex_pc_(dex_pc) { inputs_.SetSize(number_of_arguments); } virtual size_t InputCount() const { return inputs_.Size(); } virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); } // Runtime needs to walk the stack, so Dex -> Dex calls need to // know their environment. virtual bool NeedsEnvironment() const { return true; } void SetArgumentAt(size_t index, HInstruction* argument) { SetRawInputAt(index, argument); } virtual void SetRawInputAt(size_t index, HInstruction* input) { inputs_.Put(index, input); } virtual Primitive::Type GetType() const { return return_type_; } uint32_t GetDexPc() const { return dex_pc_; } protected: GrowableArray inputs_; const Primitive::Type return_type_; const uint32_t dex_pc_; private: DISALLOW_COPY_AND_ASSIGN(HInvoke); }; class HInvokeStatic : public HInvoke { public: HInvokeStatic(ArenaAllocator* arena, uint32_t number_of_arguments, Primitive::Type return_type, uint32_t dex_pc, uint32_t index_in_dex_cache) : HInvoke(arena, number_of_arguments, return_type, dex_pc), index_in_dex_cache_(index_in_dex_cache) {} uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; } DECLARE_INSTRUCTION(InvokeStatic); private: const uint32_t index_in_dex_cache_; DISALLOW_COPY_AND_ASSIGN(HInvokeStatic); }; class HNewInstance : public HExpression<0> { public: HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot), dex_pc_(dex_pc), type_index_(type_index) {} uint32_t GetDexPc() const { return dex_pc_; } uint16_t GetTypeIndex() const { return type_index_; } // Calls runtime so needs an environment. virtual bool NeedsEnvironment() const { return true; } DECLARE_INSTRUCTION(NewInstance); private: const uint32_t dex_pc_; const uint16_t type_index_; DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; class HAdd : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} virtual bool IsCommutative() { return true; } DECLARE_INSTRUCTION(Add); private: DISALLOW_COPY_AND_ASSIGN(HAdd); }; class HSub : public HBinaryOperation { public: HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right) : HBinaryOperation(result_type, left, right) {} virtual bool IsCommutative() { return false; } DECLARE_INSTRUCTION(Sub); private: DISALLOW_COPY_AND_ASSIGN(HSub); }; // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue : public HExpression<0> { public: HParameterValue(uint8_t index, Primitive::Type parameter_type) : HExpression(parameter_type), index_(index) {} uint8_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(ParameterValue); private: // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_; const uint8_t index_; DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; class HNot : public HExpression<1> { public: explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) { SetRawInputAt(0, input); } DECLARE_INSTRUCTION(Not); private: DISALLOW_COPY_AND_ASSIGN(HNot); }; class HPhi : public HInstruction { public: HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type) : inputs_(arena, number_of_inputs), reg_number_(reg_number), type_(type), is_live_(false) { inputs_.SetSize(number_of_inputs); } virtual size_t InputCount() const { return inputs_.Size(); } virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); } virtual void SetRawInputAt(size_t index, HInstruction* input) { inputs_.Put(index, input); } void AddInput(HInstruction* input); virtual Primitive::Type GetType() const { return type_; } void SetType(Primitive::Type type) { type_ = type; } uint32_t GetRegNumber() const { return reg_number_; } void SetDead() { is_live_ = false; } void SetLive() { is_live_ = true; } bool IsDead() const { return !is_live_; } bool IsLive() const { return is_live_; } DECLARE_INSTRUCTION(Phi); protected: GrowableArray inputs_; const uint32_t reg_number_; Primitive::Type type_; bool is_live_; private: DISALLOW_COPY_AND_ASSIGN(HPhi); }; class HNullCheck : public HExpression<1> { public: HNullCheck(HInstruction* value, uint32_t dex_pc) : HExpression(value->GetType()), dex_pc_(dex_pc) { SetRawInputAt(0, value); } virtual bool NeedsEnvironment() const { return true; } uint32_t GetDexPc() const { return dex_pc_; } DECLARE_INSTRUCTION(NullCheck); private: const uint32_t dex_pc_; DISALLOW_COPY_AND_ASSIGN(HNullCheck); }; class FieldInfo : public ValueObject { public: explicit FieldInfo(MemberOffset field_offset) : field_offset_(field_offset) {} MemberOffset GetFieldOffset() const { return field_offset_; } private: const MemberOffset field_offset_; }; class HInstanceFieldGet : public HExpression<1> { public: HInstanceFieldGet(HInstruction* value, Primitive::Type field_type, MemberOffset field_offset) : HExpression(field_type), field_info_(field_offset) { SetRawInputAt(0, value); } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DECLARE_INSTRUCTION(InstanceFieldGet); private: const FieldInfo field_info_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); }; class HInstanceFieldSet : public HTemplateInstruction<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, MemberOffset field_offset) : field_info_(field_offset) { SetRawInputAt(0, object); SetRawInputAt(1, value); } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DECLARE_INSTRUCTION(InstanceFieldSet); private: const FieldInfo field_info_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; /** * Some DEX instructions are folded into multiple HInstructions that need * to stay live until the last HInstruction. This class * is used as a marker for the baseline compiler to ensure its preceding * HInstruction stays live. `index` is the temporary number that is used * for knowing the stack offset where to store the instruction. */ class HTemporary : public HTemplateInstruction<0> { public: explicit HTemporary(size_t index) : index_(index) {} size_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(Temporary); private: const size_t index_; DISALLOW_COPY_AND_ASSIGN(HTemporary); }; class MoveOperands : public ArenaObject { public: MoveOperands(Location source, Location destination) : source_(source), destination_(destination) {} Location GetSource() const { return source_; } Location GetDestination() const { return destination_; } void SetSource(Location value) { source_ = value; } void SetDestination(Location value) { destination_ = value; } // The parallel move resolver marks moves as "in-progress" by clearing the // destination (but not the source). Location MarkPending() { DCHECK(!IsPending()); Location dest = destination_; destination_ = Location::NoLocation(); return dest; } void ClearPending(Location dest) { DCHECK(IsPending()); destination_ = dest; } bool IsPending() const { DCHECK(!source_.IsInvalid() || destination_.IsInvalid()); return destination_.IsInvalid() && !source_.IsInvalid(); } // True if this blocks a move from the given location. bool Blocks(Location loc) const { return !IsEliminated() && source_.Equals(loc); } // A move is redundant if it's been eliminated, if its source and // destination are the same, or if its destination is unneeded. bool IsRedundant() const { return IsEliminated() || destination_.IsInvalid() || source_.Equals(destination_); } // We clear both operands to indicate move that's been eliminated. void Eliminate() { source_ = destination_ = Location::NoLocation(); } bool IsEliminated() const { DCHECK(!source_.IsInvalid() || destination_.IsInvalid()); return source_.IsInvalid(); } private: Location source_; Location destination_; DISALLOW_COPY_AND_ASSIGN(MoveOperands); }; static constexpr size_t kDefaultNumberOfMoves = 4; class HParallelMove : public HTemplateInstruction<0> { public: explicit HParallelMove(ArenaAllocator* arena) : moves_(arena, kDefaultNumberOfMoves) {} void AddMove(MoveOperands* move) { moves_.Add(move); } MoveOperands* MoveOperandsAt(size_t index) const { return moves_.Get(index); } size_t NumMoves() const { return moves_.Size(); } DECLARE_INSTRUCTION(ParallelMove); private: GrowableArray moves_; DISALLOW_COPY_AND_ASSIGN(HParallelMove); }; class HGraphVisitor : public ValueObject { public: explicit HGraphVisitor(HGraph* graph) : graph_(graph) {} virtual ~HGraphVisitor() {} virtual void VisitInstruction(HInstruction* instruction) {} virtual void VisitBasicBlock(HBasicBlock* block); void VisitInsertionOrder(); HGraph* GetGraph() const { return graph_; } // Visit functions for instruction classes. #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr) { VisitInstruction(instr); } FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION private: HGraph* graph_; DISALLOW_COPY_AND_ASSIGN(HGraphVisitor); }; class HInsertionOrderIterator : public ValueObject { public: explicit HInsertionOrderIterator(const HGraph& graph) : graph_(graph), index_(0) {} bool Done() const { return index_ == graph_.GetBlocks().Size(); } HBasicBlock* Current() const { return graph_.GetBlocks().Get(index_); } void Advance() { ++index_; } private: const HGraph& graph_; size_t index_; DISALLOW_COPY_AND_ASSIGN(HInsertionOrderIterator); }; class HReversePostOrderIterator : public ValueObject { public: explicit HReversePostOrderIterator(const HGraph& graph) : graph_(graph), index_(0) {} bool Done() const { return index_ == graph_.GetReversePostOrder().Size(); } HBasicBlock* Current() const { return graph_.GetReversePostOrder().Get(index_); } void Advance() { ++index_; } private: const HGraph& graph_; size_t index_; DISALLOW_COPY_AND_ASSIGN(HReversePostOrderIterator); }; class HPostOrderIterator : public ValueObject { public: explicit HPostOrderIterator(const HGraph& graph) : graph_(graph), index_(graph_.GetReversePostOrder().Size()) {} bool Done() const { return index_ == 0; } HBasicBlock* Current() const { return graph_.GetReversePostOrder().Get(index_ - 1); } void Advance() { --index_; } private: const HGraph& graph_; size_t index_; DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator); }; } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_H_