/* * Copyright (C) 2013 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_SEA_IR_SEA_H_ #define ART_COMPILER_SEA_IR_SEA_H_ #include #include #include "dex_file.h" #include "dex_instruction.h" #include "instruction_tools.h" #include "instruction_nodes.h" #include "utils/scoped_hashtable.h" namespace sea_ir { // Reverse post-order numbering constants enum RegionNumbering { NOT_VISITED = -1, VISITING = -2 }; class Region; class InstructionNode; class PhiInstructionNode; class SignatureNode; // A SignatureNode is a declaration of one parameter in the function signature. // This class is used to provide place-holder definitions to which instructions // can return from the GetSSAUses() calls, instead of having missing SSA edges. class SignatureNode: public InstructionNode { public: explicit SignatureNode(unsigned int parameter_register):InstructionNode(NULL), parameter_register_(parameter_register) { } void ToDot(std::string& result) const { result += StringId() +" [label=\"signature:"; result += art::StringPrintf("r%d", GetResultRegister()); result += "\"] // signature node\n"; } int GetResultRegister() const { return parameter_register_; } std::vector GetUses() { return std::vector(); } void Accept(IRVisitor* v) { v->Visit(this); v->Traverse(this); } private: unsigned int parameter_register_; }; class PhiInstructionNode: public InstructionNode { public: explicit PhiInstructionNode(int register_no): InstructionNode(NULL), register_no_(register_no), definition_edges_() {} // Appends to @result the .dot string representation of the instruction. void ToDot(std::string& result) const; // Returns the register on which this phi-function is used. int GetRegisterNumber() const { return register_no_; } // Renames the use of @reg_no to refer to the instruction @definition. // Phi-functions are different than normal instructions in that they // have multiple predecessor regions; this is why RenameToSSA has // the additional parameter specifying that @parameter_id is the incoming // edge for @definition, essentially creating SSA form. void RenameToSSA(int reg_no, InstructionNode* definition, unsigned int predecessor_id) { DCHECK(NULL != definition) << "Tried to rename to SSA using a NULL definition for " << StringId() << " register " << reg_no; if (definition_edges_.size() < predecessor_id+1) { definition_edges_.resize(predecessor_id+1, NULL); } if (NULL == definition_edges_.at(predecessor_id)) { definition_edges_[predecessor_id] = new std::vector(); } definition_edges_[predecessor_id]->push_back(definition); } // Returns the instruction that defines the phi register from predecessor // on position @predecessor_pos. Note that the return value is vector<> just // for consistency with the return value of GetSSAUses() on regular instructions, // The returned vector should always have a single element because the IR is SSA. std::vector* GetSSAUses(int predecessor_pos) { return definition_edges_.at(predecessor_pos); } void Accept(IRVisitor* v) { v->Visit(this); v->Traverse(this); } private: int register_no_; std::vector*> definition_edges_; }; // This class corresponds to a basic block in traditional compiler IRs. // The dataflow analysis relies on this class both during execution and // for storing its results. class Region : public SeaNode { public: explicit Region(): SeaNode(), successors_(), predecessors_(), reaching_defs_size_(0), rpo_number_(NOT_VISITED), idom_(NULL), idominated_set_(), df_(), phi_set_() {} // Adds @instruction as an instruction node child in the current region. void AddChild(sea_ir::InstructionNode* instruction); // Returns the last instruction node child of the current region. // This child has the CFG successors pointing to the new regions. SeaNode* GetLastChild() const; // Returns all the child instructions of this region, in program order. std::vector* GetInstructions() { return &instructions_; } // Appends to @result a dot language formatted string representing the node and // (by convention) outgoing edges, so that the composition of theToDot() of all nodes // builds a complete dot graph (without prolog and epilog though). virtual void ToDot(std::string& result) const; // Computes Downward Exposed Definitions for the current node. void ComputeDownExposedDefs(); const std::map* GetDownExposedDefs() const; // Performs one iteration of the reaching definitions algorithm // and returns true if the reaching definitions set changed. bool UpdateReachingDefs(); // Returns the set of reaching definitions for the current region. std::map* >* GetReachingDefs(); void SetRPO(int rpo) { rpo_number_ = rpo; } int GetRPO() { return rpo_number_; } void SetIDominator(Region* dom) { idom_ = dom; } Region* GetIDominator() const { return idom_; } void AddToIDominatedSet(Region* dominated) { idominated_set_.insert(dominated); } const std::set* GetIDominatedSet() { return &idominated_set_; } // Adds @df_reg to the dominance frontier of the current region. void AddToDominanceFrontier(Region* df_reg) { df_.insert(df_reg); } // Returns the dominance frontier of the current region. // Preconditions: SeaGraph.ComputeDominanceFrontier() std::set* GetDominanceFrontier() { return &df_; } // Returns true if the region contains a phi function for @reg_no. bool ContainsPhiFor(int reg_no) { return (phi_set_.end() != phi_set_.find(reg_no)); } // Returns the phi-functions from the region. std::vector* GetPhiNodes() { return &phi_instructions_; } // Adds a phi-function for @reg_no to this region. // Note: The insertion order does not matter, as phi-functions // are conceptually executed at the same time. bool InsertPhiFor(int reg_no); // Sets the phi-function uses to be as defined in @scoped_table for predecessor @@predecessor. void SetPhiDefinitionsForUses(const utils::ScopedHashtable* scoped_table, Region* predecessor); void Accept(IRVisitor* v) { v->Visit(this); v->Traverse(this); } void AddSuccessor(Region* successor) { DCHECK(successor) << "Tried to add NULL successor to SEA node."; successors_.push_back(successor); return; } void AddPredecessor(Region* predecessor) { DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node."; predecessors_.push_back(predecessor); } std::vector* GetSuccessors() { return &successors_; } std::vector* GetPredecessors() { return &predecessors_; } private: std::vector successors_; // CFG successor nodes (regions) std::vector predecessors_; // CFG predecessor nodes (instructions/regions) std::vector instructions_; std::map de_defs_; std::map* > reaching_defs_; int reaching_defs_size_; int rpo_number_; // reverse postorder number of the region // Immediate dominator node. Region* idom_; // The set of nodes immediately dominated by the region. std::set idominated_set_; // Records the dominance frontier. std::set df_; // Records the set of register numbers that have phi nodes in this region. std::set phi_set_; std::vector phi_instructions_; }; // A SeaGraph instance corresponds to a source code function. // Its main point is to encapsulate the SEA IR representation of it // and acts as starting point for visitors (ex: during code generation). class SeaGraph: IVisitable { public: static SeaGraph* GetCurrentGraph(); void CompileMethod(const art::DexFile::CodeItem* code_item, uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file); // Returns all regions corresponding to this SeaGraph. std::vector* GetRegions() { return ®ions_; } // Returns a string representation of the region and its Instruction children. void DumpSea(std::string filename) const; // Recursively computes the reverse postorder value for @crt_bb and successors. static void ComputeRPO(Region* crt_bb, int& crt_rpo); // Returns the "lowest common ancestor" of @i and @j in the dominator tree. static Region* Intersect(Region* i, Region* j); //Returns the vector of parameters of the function. std::vector* GetParameterNodes() { return ¶meters_; } uint32_t class_def_idx_; uint32_t method_idx_; private: SeaGraph(): class_def_idx_(0), method_idx_(0), regions_(), parameters_() { } // Registers @childReg as a region belonging to the SeaGraph instance. void AddRegion(Region* childReg); // Returns new region and registers it with the SeaGraph instance. Region* GetNewRegion(); // Adds a (formal) parameter node to the vector of parameters of the function. void AddParameterNode(SignatureNode* parameterNode) { parameters_.push_back(parameterNode); } // Adds a CFG edge from @src node to @dst node. void AddEdge(Region* src, Region* dst) const; // Builds the non-SSA sea-ir representation of the function @code_item from @dex_file // with class id @class_def_idx and method id @method_idx. void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item, const art::DexFile& dex_file, uint32_t class_def_idx, uint32_t method_idx); // Computes immediate dominators for each region. // Precondition: ComputeMethodSeaGraph() void ComputeIDominators(); // Computes Downward Exposed Definitions for all regions in the graph. void ComputeDownExposedDefs(); // Computes the reaching definitions set following the equations from // Cooper & Torczon, "Engineering a Compiler", second edition, page 491. // Precondition: ComputeDEDefs() void ComputeReachingDefs(); // Computes the reverse-postorder numbering for the region nodes. // Precondition: ComputeDEDefs() void ComputeRPO(); // Computes the dominance frontier for all regions in the graph, // following the algorithm from // Cooper & Torczon, "Engineering a Compiler", second edition, page 499. // Precondition: ComputeIDominators() void ComputeDominanceFrontier(); // Converts the IR to semi-pruned SSA form. void ConvertToSSA(); // Performs the renaming phase of the SSA transformation during ConvertToSSA() execution. void RenameAsSSA(); // Identifies the definitions corresponding to uses for region @node // by using the scoped hashtable of names @ scoped_table. void RenameAsSSA(Region* node, utils::ScopedHashtable* scoped_table); virtual void Accept(IRVisitor* visitor) { visitor->Initialize(this); visitor->Visit(this); visitor->Traverse(this); } virtual ~SeaGraph() {} // Generate LLVM IR for the method. // Precondition: ConvertToSSA(). void GenerateLLVM(); static SeaGraph graph_; std::vector regions_; std::vector parameters_; }; } // end namespace sea_ir #endif // ART_COMPILER_SEA_IR_SEA_H_