//===- 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