#pragma once #include "c-opast.hh" #include "c-opcomp.hh" #include struct T_SyncCurves; namespace opopt { // Persistent data for the various stages of the optimizer. struct T_OptData { // Logger F_OPLogger logger{ []( auto , auto ) {} }; // List of errors generated by the optimizer T_Array< ebcl::T_SRDError > errors; // If the size of the ouput is fixed, this field contains it as a // pair. T_Optional< std::pair< uint32_t , uint32_t > > fixedSize; // The curves that will be bound to the inputs. T_SyncCurves const* curves{ nullptr }; // A visitor to be used for the tree ebcl::T_Visitor< opast::A_Node > visitor{ opast::ASTVisitorBrowser }; //---------------------------------------------------------------------- // Elements of the optimizer's state enum class E_StateItem { INPUTS , NUMBERING , CFG , UDCHAINS }; using T_State = T_Flags< E_StateItem >; T_State state{}; //---------------------------------------------------------------------- // Table of input declarations; used to fold constant inputs. struct T_InputDecl { ebcl::T_SRDLocation location; float value; }; T_KeyValueTable< T_String , T_Array< T_InputDecl > > inputDecls; void findInputDecls( T_OpsParserOutput& program ) noexcept; //---------------------------------------------------------------------- // Data for instruction numbering struct T_InstrPos { uint32_t index; opast::A_InstructionNode* node; bool lastOfSequence; uint32_t funcIndex; }; T_HashIndex instrIndex; T_Array< T_InstrPos > instructions; void numberInstructions( T_OpsParserOutput& program ) noexcept; uint32_t indexOf( opast::A_InstructionNode const& instr ) noexcept; //---------------------------------------------------------------------- // Basic block of consecutive instructions struct T_BasicBlock { uint32_t first; uint32_t count; explicit T_BasicBlock( uint32_t first ) noexcept : first( first ) , count( 1 ) { } }; // Control flow graph edges and nodes struct T_CtrlFlowEdge { enum E_Type { FLOW , CALL , RET , BYPASS }; uint32_t target; E_Type type; T_CtrlFlowEdge( const uint32_t target , const E_Type type = FLOW ) noexcept : target{ target } , type{ type } {} }; struct T_CtrlFlowNode { T_Optional< T_BasicBlock > instructions; T_AutoArray< T_CtrlFlowEdge , 16 > inbound; T_AutoArray< T_CtrlFlowEdge , 16 > outbound; }; using P_CtrlFlowNode = T_OwnPtr< T_CtrlFlowNode >; // Data structure for call sites struct T_CallSite { T_String name; uint32_t callBlock; uint32_t retBlock; }; // Special nodes in the graph static constexpr uint32_t CFG_ENTER = 0; static constexpr uint32_t CFG_MAINLOOP = 1; static constexpr uint32_t CFG_END = 2; // Control flow graph T_Array< P_CtrlFlowNode > ctrlFlowGraph; T_Array< T_CallSite > callSites; T_KeyValueTable< T_String , T_BasicBlock > cfgFunctions; // Build the control flow graph. Instruction numbering must be // up-to-date before calling this. void buildControlFlowGraph( T_OpsParserOutput& program ) noexcept; //---------------------------------------------------------------------- // Type of variables used for use/define chains enum class E_UDVarType { GLOBAL , LOCAL , ARGUMENT }; // Variable identifier for the U/D chains struct T_VarId { E_UDVarType type; // Var type T_String name; // Var name T_String owner; // Function name (empty for globals) explicit T_VarId( T_String const& name ) noexcept : type{ E_UDVarType::GLOBAL } , name{ name } , owner{ } { } T_VarId( T_String const& name , T_String const& owner , const bool isArgument ) noexcept : type{ isArgument ? E_UDVarType::ARGUMENT : E_UDVarType::LOCAL } , name{ name } , owner{ owner } { assert( owner ); } bool operator ==( T_VarId const& other ) const noexcept { return type == other.type && name == other.name && owner == other.owner; } bool operator !=( T_VarId const& other ) const noexcept { return type != other.type || name != other.name || owner != other.owner; } }; // Use/define chain data struct T_VarUDRecord { uint32_t node; // Instruction that uses or sets it uint32_t fnIndex; // Function in which the use/define is located T_AutoArray< uint32_t , 16 > refs; // Corresponding uses/defines }; // Use/define chains for a variable struct T_VarUseDefine { T_VarId var; T_AutoArray< T_VarUDRecord , 16 > uses; T_AutoArray< T_VarUDRecord , 4> defines; explicit T_VarUseDefine( T_VarId const& id ) : var{ id } { } }; // Use/define chains T_ObjectTable< T_VarId , T_VarUseDefine > varUDChains{ []( auto const& var ) -> T_VarId { return var.var; } }; // Build the use/define chains. The control flow graph must be up to // date before calling this. void buildUseDefineChains( T_OpsParserOutput& program ) noexcept; }; uint32_t ComputeHash( T_OptData::T_VarId const& id ) noexcept; M_LSHIFT_OP( T_StringBuilder , T_OptData::T_CtrlFlowEdge const& ) noexcept; /*= INDIVIDUAL OPTIMISATIONS =================================================*/ // All functions below return true if transformations were made, false if not. // Attempts to fold constant expressions into single constants. // bool FoldConstants( T_OpsParserOutput& program , T_OptData& optData ) noexcept; // Attempts to propagate values from variables that contain constants to the // locations at which they are used. // bool PropagateConstants( T_OpsParserOutput& program , T_OptData& optData ) noexcept; // Attempt to remove blocks of code that will not be executed because of // constant conditions, dead stores, etc... // bool RemoveDeadCode( T_OpsParserOutput& program , T_OptData& optData ) noexcept; // Attempt to inline functions that are only used once and which have simple // argument values (constants or identifiers). // bool InlineFunctions( T_OpsParserOutput& program , T_OptData& optData ) noexcept; } // namespace opopt