|  | //===- subzero/src/IceCfg.h - Control flow graph ----------------*- C++ -*-===// | 
|  | // | 
|  | //                        The Subzero Code Generator | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | /// | 
|  | /// \file | 
|  | /// \brief Declares the Cfg class, which represents the control flow graph and | 
|  | /// the overall per-function compilation context. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef SUBZERO_SRC_ICECFG_H | 
|  | #define SUBZERO_SRC_ICECFG_H | 
|  |  | 
|  | #include "IceAssembler.h" | 
|  | #include "IceClFlags.h" | 
|  | #include "IceDefs.h" | 
|  | #include "IceGlobalContext.h" | 
|  | #include "IceLoopAnalyzer.h" | 
|  | #include "IceStringPool.h" | 
|  | #include "IceTypes.h" | 
|  |  | 
|  | namespace Ice { | 
|  |  | 
|  | class Cfg { | 
|  | Cfg() = delete; | 
|  | Cfg(const Cfg &) = delete; | 
|  | Cfg &operator=(const Cfg &) = delete; | 
|  |  | 
|  | std::unique_ptr<ArenaAllocator> Allocator; | 
|  |  | 
|  | public: | 
|  | ~Cfg(); | 
|  |  | 
|  | static std::unique_ptr<Cfg> create(GlobalContext *Ctx, | 
|  | uint32_t SequenceNumber) { | 
|  | return std::unique_ptr<Cfg>(new Cfg(Ctx, SequenceNumber)); | 
|  | } | 
|  |  | 
|  | GlobalContext *getContext() const { return Ctx; } | 
|  | uint32_t getSequenceNumber() const { return SequenceNumber; } | 
|  | OptLevel getOptLevel() const { return OptimizationLevel; } | 
|  |  | 
|  | static constexpr VerboseMask defaultVerboseMask() { | 
|  | return (IceV_NO_PER_PASS_DUMP_BEYOND << 1) - 1; | 
|  | } | 
|  | /// Returns true if any of the specified options in the verbose mask are set. | 
|  | /// If the argument is omitted, it checks if any verbose options at all are | 
|  | /// set. | 
|  | bool isVerbose(VerboseMask Mask = defaultVerboseMask()) const { | 
|  | return VMask & Mask; | 
|  | } | 
|  | void setVerbose(VerboseMask Mask) { VMask = Mask; } | 
|  |  | 
|  | /// \name Manage the name and return type of the function being translated. | 
|  | /// @{ | 
|  | void setFunctionName(GlobalString Name) { FunctionName = Name; } | 
|  | GlobalString getFunctionName() const { return FunctionName; } | 
|  | std::string getFunctionNameAndSize() const; | 
|  | void setReturnType(Type Ty) { ReturnType = Ty; } | 
|  | Type getReturnType() const { return ReturnType; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage the "internal" attribute of the function. | 
|  | /// @{ | 
|  | void setInternal(bool Internal) { IsInternalLinkage = Internal; } | 
|  | bool getInternal() const { return IsInternalLinkage; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage errors. | 
|  | /// @{ | 
|  |  | 
|  | /// Translation error flagging. If support for some construct is known to be | 
|  | /// missing, instead of an assertion failure, setError() should be called and | 
|  | /// the error should be propagated back up. This way, we can gracefully fail | 
|  | /// to translate and let a fallback translator handle the function. | 
|  | void setError(const std::string &Message); | 
|  | bool hasError() const { return HasError; } | 
|  | std::string getError() const { return ErrorMessage; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage nodes (a.k.a. basic blocks, CfgNodes). | 
|  | /// @{ | 
|  | void setEntryNode(CfgNode *EntryNode) { Entry = EntryNode; } | 
|  | CfgNode *getEntryNode() const { return Entry; } | 
|  | /// Create a node and append it to the end of the linearized list. The loop | 
|  | /// nest depth of the new node may not be valid if it is created after | 
|  | /// computeLoopNestDepth. | 
|  | CfgNode *makeNode(); | 
|  | SizeT getNumNodes() const { return Nodes.size(); } | 
|  | const NodeList &getNodes() const { return Nodes; } | 
|  | /// Swap nodes of Cfg with given list of nodes.  The number of nodes must | 
|  | /// remain unchanged. | 
|  | void swapNodes(NodeList &NewNodes); | 
|  | /// @} | 
|  |  | 
|  | /// String pool for CfgNode::Name values. | 
|  | StringPool *getNodeStrings() const { return NodeStrings.get(); } | 
|  | /// String pool for Variable::Name values. | 
|  | StringPool *getVarStrings() const { return VarStrings.get(); } | 
|  |  | 
|  | /// \name Manage instruction numbering. | 
|  | /// @{ | 
|  | InstNumberT newInstNumber() { return NextInstNumber++; } | 
|  | InstNumberT getNextInstNumber() const { return NextInstNumber; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage Variables. | 
|  | /// @{ | 
|  |  | 
|  | /// Create a new Variable with a particular type and an optional name. The | 
|  | /// Node argument is the node where the variable is defined. | 
|  | // TODO(jpp): untemplate this with separate methods: makeVariable and | 
|  | // makeStackVariable. | 
|  | template <typename T = Variable> T *makeVariable(Type Ty) { | 
|  | SizeT Index = Variables.size(); | 
|  | auto *Var = T::create(this, Ty, Index); | 
|  | Variables.push_back(Var); | 
|  | return Var; | 
|  | } | 
|  | SizeT getNumVariables() const { return Variables.size(); } | 
|  | const VarList &getVariables() const { return Variables; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage arguments to the function. | 
|  | /// @{ | 
|  | void addArg(Variable *Arg); | 
|  | const VarList &getArgs() const { return Args; } | 
|  | VarList &getArgs() { return Args; } | 
|  | void addImplicitArg(Variable *Arg); | 
|  | const VarList &getImplicitArgs() const { return ImplicitArgs; } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage the jump tables. | 
|  | /// @{ | 
|  | void addJumpTable(InstJumpTable *JumpTable) { | 
|  | JumpTables.emplace_back(JumpTable); | 
|  | } | 
|  | /// @} | 
|  |  | 
|  | /// \name Manage the Globals used by this function. | 
|  | /// @{ | 
|  | std::unique_ptr<VariableDeclarationList> getGlobalInits() { | 
|  | return std::move(GlobalInits); | 
|  | } | 
|  | void addGlobal(VariableDeclaration *Global) { | 
|  | if (GlobalInits == nullptr) { | 
|  | GlobalInits.reset(new VariableDeclarationList); | 
|  | } | 
|  | GlobalInits->push_back(Global); | 
|  | } | 
|  | VariableDeclarationList *getGlobalPool() { | 
|  | if (GlobalInits == nullptr) { | 
|  | GlobalInits.reset(new VariableDeclarationList); | 
|  | } | 
|  | return GlobalInits.get(); | 
|  | } | 
|  | /// @} | 
|  |  | 
|  | /// \name Miscellaneous accessors. | 
|  | /// @{ | 
|  | TargetLowering *getTarget() const { return Target.get(); } | 
|  | VariablesMetadata *getVMetadata() const { return VMetadata.get(); } | 
|  | Liveness *getLiveness() const { return Live.get(); } | 
|  | template <typename T = Assembler> T *getAssembler() const { | 
|  | return llvm::dyn_cast<T>(TargetAssembler.get()); | 
|  | } | 
|  | std::unique_ptr<Assembler> releaseAssembler() { | 
|  | return std::move(TargetAssembler); | 
|  | } | 
|  | bool hasComputedFrame() const; | 
|  | bool getFocusedTiming() const { return FocusedTiming; } | 
|  | void setFocusedTiming() { FocusedTiming = true; } | 
|  | uint32_t getConstantBlindingCookie() const { return ConstantBlindingCookie; } | 
|  | /// @} | 
|  |  | 
|  | /// Returns true if Var is a global variable that is used by the profiling | 
|  | /// code. | 
|  | static bool isProfileGlobal(const VariableDeclaration &Var); | 
|  |  | 
|  | /// Passes over the CFG. | 
|  | void translate(); | 
|  | /// After the CFG is fully constructed, iterate over the nodes and compute the | 
|  | /// predecessor and successor edges, in the form of CfgNode::InEdges[] and | 
|  | /// CfgNode::OutEdges[]. | 
|  | void computeInOutEdges(); | 
|  | /// Renumber the non-deleted instructions in the Cfg.  This needs to be done | 
|  | /// in preparation for live range analysis.  The instruction numbers in a | 
|  | /// block must be monotonically increasing.  The range of instruction numbers | 
|  | /// in a block, from lowest to highest, must not overlap with the range of any | 
|  | /// other block. | 
|  | /// | 
|  | /// Also, if the configuration specifies to do so, remove/unlink all deleted | 
|  | /// instructions from the Cfg, to speed up later passes over the instructions. | 
|  | void renumberInstructions(); | 
|  | void placePhiLoads(); | 
|  | void placePhiStores(); | 
|  | void deletePhis(); | 
|  | void advancedPhiLowering(); | 
|  | void reorderNodes(); | 
|  | void shuffleNodes(); | 
|  | void localCSE(bool AssumeSSA); | 
|  | void floatConstantCSE(); | 
|  | void shortCircuitJumps(); | 
|  | void loopInvariantCodeMotion(); | 
|  |  | 
|  | /// Scan allocas to determine whether we need to use a frame pointer. | 
|  | /// If SortAndCombine == true, merge all the fixed-size allocas in the | 
|  | /// entry block and emit stack or frame pointer-relative addressing. | 
|  | void processAllocas(bool SortAndCombine); | 
|  | void doAddressOpt(); | 
|  | /// Find clusters of insertelement/extractelement instructions that can be | 
|  | /// replaced by a shufflevector instruction. | 
|  | void materializeVectorShuffles(); | 
|  | void doArgLowering(); | 
|  | void doNopInsertion(); | 
|  | void genCode(); | 
|  | void genFrame(); | 
|  | void generateLoopInfo(); | 
|  | void livenessLightweight(); | 
|  | void liveness(LivenessMode Mode); | 
|  | bool validateLiveness() const; | 
|  | void contractEmptyNodes(); | 
|  | void doBranchOpt(); | 
|  | void markNodesForSandboxing(); | 
|  |  | 
|  | /// \name  Manage the CurrentNode field. | 
|  | /// CurrentNode is used for validating the Variable::DefNode field during | 
|  | /// dumping/emitting. | 
|  | /// @{ | 
|  | void setCurrentNode(const CfgNode *Node) { CurrentNode = Node; } | 
|  | void resetCurrentNode() { setCurrentNode(nullptr); } | 
|  | const CfgNode *getCurrentNode() const { return CurrentNode; } | 
|  | /// @} | 
|  |  | 
|  | /// Get the total amount of memory held by the per-Cfg allocator. | 
|  | size_t getTotalMemoryMB() const; | 
|  |  | 
|  | /// Get the current memory usage due to liveness data structures. | 
|  | size_t getLivenessMemoryMB() const; | 
|  |  | 
|  | void emit(); | 
|  | void emitIAS(); | 
|  | static void emitTextHeader(GlobalString Name, GlobalContext *Ctx, | 
|  | const Assembler *Asm); | 
|  | void dump(const char *Message = ""); | 
|  |  | 
|  | /// Allocate data of type T using the per-Cfg allocator. | 
|  | template <typename T> T *allocate() { return Allocator->Allocate<T>(); } | 
|  |  | 
|  | /// Allocate an array of data of type T using the per-Cfg allocator. | 
|  | template <typename T> T *allocateArrayOf(size_t NumElems) { | 
|  | return Allocator->Allocate<T>(NumElems); | 
|  | } | 
|  |  | 
|  | /// Deallocate data that was allocated via allocate<T>(). | 
|  | template <typename T> void deallocate(T *Object) { | 
|  | Allocator->Deallocate(Object); | 
|  | } | 
|  |  | 
|  | /// Deallocate data that was allocated via allocateArrayOf<T>(). | 
|  | template <typename T> void deallocateArrayOf(T *Array) { | 
|  | Allocator->Deallocate(Array); | 
|  | } | 
|  |  | 
|  | /// Update Phi labels with InEdges. | 
|  | /// | 
|  | /// The WASM translator cannot always determine the right incoming edge for a | 
|  | /// value due to the CFG being built incrementally. The fixPhiNodes pass fills | 
|  | /// in the correct information once everything is known. | 
|  | void fixPhiNodes(); | 
|  |  | 
|  | private: | 
|  | friend class CfgAllocatorTraits; // for Allocator access. | 
|  |  | 
|  | Cfg(GlobalContext *Ctx, uint32_t SequenceNumber); | 
|  |  | 
|  | /// Adds a call to the ProfileSummary runtime function as the first | 
|  | /// instruction in this CFG's entry block. | 
|  | void addCallToProfileSummary(); | 
|  |  | 
|  | /// Iterates over the basic blocks in this CFG, adding profiling code to each | 
|  | /// one of them. It returns a list with all the globals that the profiling | 
|  | /// code needs to be defined. | 
|  | void profileBlocks(); | 
|  |  | 
|  | void createNodeNameDeclaration(const std::string &NodeAsmName); | 
|  | void | 
|  | createBlockProfilingInfoDeclaration(const std::string &NodeAsmName, | 
|  | VariableDeclaration *NodeNameDeclaration); | 
|  |  | 
|  | /// Iterate through the registered jump tables and emit them. | 
|  | void emitJumpTables(); | 
|  |  | 
|  | enum AllocaBaseVariableType { | 
|  | BVT_StackPointer, | 
|  | BVT_FramePointer, | 
|  | BVT_UserPointer | 
|  | }; | 
|  | void sortAndCombineAllocas(CfgVector<InstAlloca *> &Allocas, | 
|  | uint32_t CombinedAlignment, InstList &Insts, | 
|  | AllocaBaseVariableType BaseVariableType); | 
|  | void findRematerializable(); | 
|  | CfgVector<Inst *> | 
|  | findLoopInvariantInstructions(const CfgUnorderedSet<SizeT> &Body); | 
|  |  | 
|  | static ArenaAllocator *createAllocator(); | 
|  |  | 
|  | GlobalContext *Ctx; | 
|  | uint32_t SequenceNumber; /// output order for emission | 
|  | OptLevel OptimizationLevel = Opt_m1; | 
|  | uint32_t ConstantBlindingCookie = 0; /// cookie for constant blinding | 
|  | VerboseMask VMask; | 
|  | GlobalString FunctionName; | 
|  | Type ReturnType = IceType_void; | 
|  | bool IsInternalLinkage = false; | 
|  | bool HasError = false; | 
|  | bool FocusedTiming = false; | 
|  | std::string ErrorMessage = ""; | 
|  | CfgNode *Entry = nullptr; /// entry basic block | 
|  | NodeList Nodes;           /// linearized node list; Entry should be first | 
|  | InstNumberT NextInstNumber; | 
|  | VarList Variables; | 
|  | VarList Args;         /// subset of Variables, in argument order | 
|  | VarList ImplicitArgs; /// subset of Variables | 
|  | // Separate string pools for CfgNode and Variable names, due to a combination | 
|  | // of the uniqueness requirement, and assumptions in lit tests. | 
|  | std::unique_ptr<StringPool> NodeStrings; | 
|  | std::unique_ptr<StringPool> VarStrings; | 
|  | std::unique_ptr<Liveness> Live; | 
|  | std::unique_ptr<TargetLowering> Target; | 
|  | std::unique_ptr<VariablesMetadata> VMetadata; | 
|  | std::unique_ptr<Assembler> TargetAssembler; | 
|  | /// Globals required by this CFG. Mostly used for the profiler's globals. | 
|  | std::unique_ptr<VariableDeclarationList> GlobalInits; | 
|  | CfgVector<InstJumpTable *> JumpTables; | 
|  | /// CurrentNode is maintained during dumping/emitting just for validating | 
|  | /// Variable::DefNode. Normally, a traversal over CfgNodes maintains this, but | 
|  | /// before global operations like register allocation, resetCurrentNode() | 
|  | /// should be called to avoid spurious validation failures. | 
|  | const CfgNode *CurrentNode = nullptr; | 
|  | CfgVector<Loop> LoopInfo; | 
|  |  | 
|  | public: | 
|  | static void TlsInit() { CfgAllocatorTraits::init(); } | 
|  | }; | 
|  |  | 
|  | template <> Variable *Cfg::makeVariable<Variable>(Type Ty); | 
|  |  | 
|  | struct NodeStringPoolTraits { | 
|  | using OwnerType = Cfg; | 
|  | static StringPool *getStrings(const OwnerType *PoolOwner) { | 
|  | return PoolOwner->getNodeStrings(); | 
|  | } | 
|  | }; | 
|  | using NodeString = StringID<NodeStringPoolTraits>; | 
|  |  | 
|  | struct VariableStringPoolTraits { | 
|  | using OwnerType = Cfg; | 
|  | static StringPool *getStrings(const OwnerType *PoolOwner) { | 
|  | return PoolOwner->getVarStrings(); | 
|  | } | 
|  | }; | 
|  | using VariableString = StringID<VariableStringPoolTraits>; | 
|  |  | 
|  | } // end of namespace Ice | 
|  |  | 
|  | #endif // SUBZERO_SRC_ICECFG_H |