234 lines
5.9 KiB
C++
234 lines
5.9 KiB
C++
#pragma once
|
|
#include "c-opast.hh"
|
|
#include "c-opcomp.hh"
|
|
|
|
#include <ebcl/Algorithms.hh>
|
|
|
|
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
|
|
// <width,height> 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
|