demotool/c-opopt.hh
Emmanuel BENOîT 027d6a2f8d Optimizer - Removed unnecessary computations
The following computations will not be done if their results are cached:
* instruction numbering,
* control flow graph construction,
* use-define chains
2017-12-15 07:16:51 +01:00

228 lines
5.7 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_Optional< 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
ebcl::T_Set< uint32_t > refs{ // Corresponding uses/defines
ebcl::UseTag< ebcl::ArrayBacked< 16 > >( ) };
};
// 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.
//
bool RemoveDeadCode(
T_OpsParserOutput& program ,
T_OptData& optData ) noexcept;
} // namespace opopt