2019-11-14 11:57:39 +08:00
#include "parse.h"
#include "parselib.h"
#include "extra.h"
#include "opcodes.h"
#include "oplib.h"
#include "opextra.h"
#include <stdlib.h>
#include <string.h>
2019-11-14 12:14:46 +08:00
#define Protect(a) if (_stricmp(c, a) == 0) return 1;
2019-11-14 11:57:39 +08:00
2019-11-14 12:14:46 +08:00
#define F_OP(token,op) case token: out->floatData = fd1 op fd2; break;
#define F_IOP(token,op) case token: out->type = V_INT; out->intData = fd1 op fd2; break;
#define I_OP(token,op) case token: out->intData = in1->intData op in2->intData; break;
2020-02-02 16:30:45 +08:00
#define I_OPU(token,op) case token: out->intData = (unsigned int)in1->intData op (unsigned int)in2->intData; break;
2019-11-14 11:57:39 +08:00
#define VU_FIRST_ASSIGN_IS_PURE 0x01
#define VU_FIRST_ASSIGN_IN_WHILE 0x02
#define VU_LAST_ASSIGN_IN_WHILE 0x04
#define VU_FIRST_ASSIGN_IS_DEAD 0x08
#define VU_LAST_ASSIGN_IS_PURE 0x10
#define VU_FIRST_READ_IN_WHILE 0x20
#define VU_LAST_READ_IN_WHILE 0x40
#define VU_FIRST_ASSIGN_TRANSFERABLE 0x80
extern int optimize ;
static Program * currprogram ;
//Sanity checks. Should never be hit
static void assert ( int i ) {
2019-11-14 12:14:46 +08:00
if ( ! i )
2019-11-14 11:57:39 +08:00
parseError ( "ICE in optimizer." );
}
//Strip out some nods from a nodelist
static void RemoveNodes ( NodeList * nodes , int start , int count ) {
2019-11-14 12:14:46 +08:00
memmove ( & nodes -> nodes [ start ], & nodes -> nodes [ start + count ], ( nodes -> numNodes - ( start + count )) * sizeof ( Node ));
nodes -> numNodes -= count ;
2019-11-14 11:57:39 +08:00
//not much point reallocing
}
//Check if a node is a local variable, and return its id if so or -1 otherwise
static int LookupVariable ( const Node * node ) {
2019-11-14 12:14:46 +08:00
assert ( node -> token == T_SYMBOL );
if ( node -> value . type != P_LOCAL ) return - 1 ;
2019-11-14 11:57:39 +08:00
return node -> value . intData ;
}
//Returns true if a token is known to have no side effects
static int IsTokenPure ( int token ) {
//TODO: Go through the whole list
2019-11-14 12:14:46 +08:00
switch ( token ) {
2019-11-14 11:57:39 +08:00
case T_TS_LEN_ARRAY :
case T_TS_ATOI :
case T_TS_ATOF :
case T_TS_TILE_PID :
case T_TS_MODIFIED_INI :
return 1 ;
}
return 0 ;
}
//Returns true if we can prove an expression has no side effects
static int IsExpressionPure ( const Node * node ) {
2019-11-14 12:14:46 +08:00
assert ( node -> token == T_START_EXPRESSION );
while (( ++ node ) -> token != T_END_EXPRESSION ) {
//if (node->token == T_START_EXPRESSION) return 0;
if ( node -> token >= T_END_CORE && ! IsTokenPure ( node -> token )) return 0 ;
if ( node -> token == T_SYMBOL && node -> value . type & P_PROCEDURE ) {
if ( node -> value . type != ( P_LOCAL | P_PROCEDURE ) || ! ( currprogram -> procedures . procedures [ node -> value . intData ]. type & P_PURE )) return 0 ;
2019-11-14 11:57:39 +08:00
}
}
return 1 ;
}
typedef struct {
int firstassign ;
int lastassign ;
int firstuse ;
int lastuse ;
int flags ;
} VarUsage ;
2023-06-09 00:54:45 +02:00
static void MarkVariableRead ( VarUsage * usage , int var , int currstatement , int whiledepth ) {
usage [ var ]. lastuse = currstatement ;
if ( whiledepth ) usage [ var ]. flags |= VU_LAST_READ_IN_WHILE ;
else usage [ var ]. flags &= ~ VU_LAST_READ_IN_WHILE ;
if ( ! usage [ var ]. firstuse ) {
usage [ var ]. firstuse = currstatement ;
if ( whiledepth ) usage [ var ]. flags |= VU_FIRST_READ_IN_WHILE ;
}
}
2019-11-14 11:57:39 +08:00
static void FindVarUsage ( const Node * node , VarUsage * usage , int varCount ) {
2019-11-14 12:14:46 +08:00
int depth = 1 , currstatement = 0 , i = 0 , whiledepth = 0 , ifdepth = 0 , var , expressiondepth ;
memset ( usage , 0 , varCount * sizeof ( VarUsage ));
assert ( node -> token == T_BEGIN );
while ( depth ) {
2019-11-14 11:57:39 +08:00
i ++ ;
2019-11-14 12:14:46 +08:00
switch (( ++ node ) -> token ) {
2019-11-14 11:57:39 +08:00
case T_BEGIN : depth ++ ; break ;
case T_END : depth -- ; break ;
case T_START_STATEMENT :
2019-11-14 12:14:46 +08:00
if ( ifdepth ) ifdepth ++ ;
if ( ! whiledepth ) {
currstatement = i ;
if ( node [ 1 ]. token == T_WHILE ) {
whiledepth = 1 ;
} else if ( node [ 1 ]. token == T_IF && ! ifdepth ) {
ifdepth = 1 ;
2019-11-14 11:57:39 +08:00
}
} else whiledepth ++ ;
break ;
case T_END_STATEMENT :
2019-11-14 12:14:46 +08:00
if ( ifdepth ) ifdepth -- ;
if ( whiledepth ) whiledepth -- ;
2019-11-14 11:57:39 +08:00
break ;
case T_ASSIGN : case T_ASSIGN_ADD : case T_ASSIGN_SUB : case T_ASSIGN_MUL : case T_ASSIGN_DIV :
2019-11-14 12:14:46 +08:00
if (( var = LookupVariable ( & node [ - 1 ])) != - 1 ) {
int pure = IsExpressionPure ( & node [ 1 ]);
usage [ var ]. lastassign = currstatement ;
if ( whiledepth ) usage [ var ]. flags |= VU_LAST_ASSIGN_IN_WHILE ;
else usage [ var ]. flags &= ~ VU_LAST_ASSIGN_IN_WHILE ;
if ( pure ) usage [ var ]. flags |= VU_LAST_ASSIGN_IS_PURE ;
if ( ! usage [ var ]. firstuse ) {
if ( ! usage [ var ]. firstassign ) {
usage [ var ]. firstassign = currstatement ;
if ( whiledepth ) usage [ var ]. flags |= VU_FIRST_ASSIGN_IN_WHILE ;
if ( pure ) {
usage [ var ]. flags |= VU_FIRST_ASSIGN_IS_PURE ;
if ( ! whiledepth && ! ifdepth && node [ 2 ]. token == T_CONSTANT && node [ 3 ]. token == T_END_EXPRESSION && node [ 2 ]. value . type != V_STRING ) {
usage [ var ]. flags |= VU_FIRST_ASSIGN_TRANSFERABLE | VU_FIRST_ASSIGN_IS_DEAD ;
2019-11-14 11:57:39 +08:00
}
}
2019-11-14 12:14:46 +08:00
} else if ( ! whiledepth && ! ifdepth ) {
if ( usage [ var ]. flags & VU_FIRST_ASSIGN_IS_PURE && node -> token == T_ASSIGN ) usage [ var ]. flags |= VU_FIRST_ASSIGN_IS_DEAD ;
} else if ( ! pure && currstatement == usage [ var ]. firstassign ) usage [ var ]. flags &= ~ VU_FIRST_ASSIGN_IS_PURE ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
if ( node -> token != T_ASSIGN ) {
2023-06-09 00:54:45 +02:00
MarkVariableRead ( usage , var , currstatement , whiledepth );
2019-11-14 11:57:39 +08:00
}
}
break ;
case T_START_EXPRESSION :
2019-11-14 12:14:46 +08:00
expressiondepth = 1 ;
while ( expressiondepth ) {
2019-11-14 11:57:39 +08:00
i ++ ;
2019-11-14 12:14:46 +08:00
switch (( ++ node ) -> token ) {
2019-11-14 11:57:39 +08:00
case T_START_EXPRESSION : expressiondepth ++ ; break ;
case T_END_EXPRESSION : expressiondepth -- ; break ;
case T_SYMBOL :
2019-11-14 12:14:46 +08:00
if (( var = LookupVariable ( node )) != - 1 ) {
2023-06-09 00:54:45 +02:00
MarkVariableRead ( usage , var , currstatement , whiledepth );
2019-11-14 11:57:39 +08:00
}
}
}
break ;
2023-06-09 00:54:45 +02:00
case T_CALL :
2023-06-09 11:15:11 +02:00
if (( node + 1 ) -> token == T_SYMBOL && (( var = LookupVariable ( node + 1 )) != - 1 )) {
2023-06-09 00:54:45 +02:00
MarkVariableRead ( usage , var , currstatement , whiledepth );
}
break ;
2019-11-14 11:57:39 +08:00
}
}
2019-11-14 12:14:46 +08:00
assert ( node -> token == T_END );
2019-11-14 11:57:39 +08:00
}
static int isValidBinaryOp ( int op ) {
2020-02-02 16:30:45 +08:00
return op == '+' || op == '-' || op == '*' || op == '/' || op == T_DIV2 ||
op == T_AND || op == T_OR || op == T_BWAND || op == T_BWOR || op == T_BWXOR ||
op == '>' || op == '<' || op == T_EQUAL || op == T_NOT_EQUAL || op == T_LESS_EQUAL || op == T_GREATER_EQUAL ;
2019-11-14 11:57:39 +08:00
}
//Calculate the result of a constant operation (doesn't handle strings, which would require namespace modifications)
static void PerformConstOp ( const Value * in1 , const Value * in2 , Value * out , int op , const Node * node ) {
int isfloat ;
assert ( isValidBinaryOp ( op ));
2019-11-14 12:14:46 +08:00
assert ( in1 -> type == V_INT || in1 -> type == V_FLOAT );
assert ( in2 -> type == V_INT || in2 -> type == V_FLOAT );
isfloat = in1 -> type == V_FLOAT || in2 -> type == V_FLOAT ;
if ( isfloat ) {
float fd1 = in1 -> type == V_FLOAT ? in1 -> floatData : ( float ) in1 -> intData ;
float fd2 = in2 -> type == V_FLOAT ? in2 -> floatData : ( float ) in2 -> intData ;
out -> type = V_FLOAT ;
switch ( op ) {
2019-11-14 11:57:39 +08:00
F_OP ( '+' , + )
F_OP ( '-' , - )
F_OP ( '*' , * )
F_OP ( '/' , / )
2020-02-02 16:30:45 +08:00
F_OP ( T_DIV2 , / )
2019-11-14 11:57:39 +08:00
F_IOP ( T_EQUAL , == )
F_IOP ( T_NOT_EQUAL , != )
F_IOP ( '>' , > )
F_IOP ( '<' , < )
F_IOP ( T_LESS_EQUAL , <= )
F_IOP ( T_GREATER_EQUAL , >= )
case T_AND :
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
out -> intData = ( fd1 != 0.0 ) && ( fd2 != 0.0 );
2019-11-14 11:57:39 +08:00
break ;
case T_OR :
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
out -> intData = ( fd1 != 0.0 ) || ( fd2 != 0.0 );
2019-11-14 11:57:39 +08:00
break ;
case T_BWAND :
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
2019-11-14 11:57:39 +08:00
parseWarningAtNode ( node , "Optimizer encountered a bitwise operation on a float constant" );
2019-11-14 12:14:46 +08:00
out -> intData = in1 -> intData & in2 -> intData ;
2019-11-14 11:57:39 +08:00
break ;
case T_BWOR :
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
2019-11-14 11:57:39 +08:00
parseWarningAtNode ( node , "Optimizer encountered a bitwise operation on a float constant" );
2019-11-14 12:14:46 +08:00
out -> intData = in1 -> intData | in2 -> intData ;
2019-11-14 11:57:39 +08:00
break ;
case T_BWXOR :
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
2019-11-14 11:57:39 +08:00
parseWarningAtNode ( node , "Optimizer encountered a bitwise operation on a float constant" );
2019-11-14 12:14:46 +08:00
out -> intData = in1 -> intData ^ in2 -> intData ;
2019-11-14 11:57:39 +08:00
break ;
}
} else {
2019-11-14 12:14:46 +08:00
out -> type = V_INT ;
switch ( op ) {
2019-11-14 11:57:39 +08:00
I_OP ( '+' , + )
I_OP ( '-' , - )
I_OP ( '*' , * )
I_OP ( '/' , / )
2020-02-02 16:30:45 +08:00
I_OPU ( T_DIV2 , / )
2019-11-14 11:57:39 +08:00
I_OP ( T_AND , && )
I_OP ( T_OR , || )
I_OP ( T_BWAND , & )
I_OP ( T_BWOR , | )
I_OP ( T_BWXOR , ^ )
I_OP ( T_EQUAL , == )
I_OP ( T_NOT_EQUAL , != )
I_OP ( '>' , > )
I_OP ( '<' , < )
I_OP ( T_LESS_EQUAL , <= )
I_OP ( T_GREATER_EQUAL , >= )
}
}
}
static int ConstantFolding ( NodeList * _nodes ) {
int i , token , matched = 0 ;
2019-11-14 12:14:46 +08:00
Node * nodes = _nodes -> nodes ;
for ( i = 2 ; i < _nodes -> numNodes ; i ++ ) {
token = nodes [ i ]. token ;
if ( isValidBinaryOp ( token )) {
if ( nodes [ i - 1 ]. token == T_CONSTANT && nodes [ i - 2 ]. token == T_CONSTANT && nodes [ i - 1 ]. value . type != V_STRING && nodes [ i - 2 ]. value . type != V_STRING ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Folding constant binary expression" );
2019-11-14 12:14:46 +08:00
matched = 1 ;
PerformConstOp ( & nodes [ i - 2 ]. value , & nodes [ i - 1 ]. value , & nodes [ i ]. value , token , & nodes [ i ]);
nodes [ i ]. token = T_CONSTANT ;
2019-11-14 11:57:39 +08:00
i -= 2 ;
2019-11-14 12:14:46 +08:00
RemoveNodes ( _nodes , i , 2 );
2019-11-14 11:57:39 +08:00
} /* AND/OR were changed in the tree
2019-11-14 12:14:46 +08:00
else if (token == T_AND || token == T_OR) {
if ((nodes[i - 1].token == T_CONSTANT && nodes[i - 1].value.type != V_STRING) || (nodes[i - 2].token == T_CONSTANT && nodes[i - 2].value.type != V_STRING)) {
if ((nodes[i - 1].token == T_SYMBOL && !(nodes[i - 1].value.type & P_PROCEDURE)) || (nodes[i - 2].token == T_SYMBOL && !(nodes[i - 1].value.type & P_PROCEDURE))) {
int value, found = 0;
if (nodes[i - 1].token == T_CONSTANT && nodes[i - 1].value.type != V_STRING) value = nodes[i - 1].value.intData;
else value = nodes[i - 2].value.intData;
if (!value && token == T_AND) {
found = 1;
} else if (value && token == T_OR) {
found = 2;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
if (found) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode(&nodes[i], "Folding semi-constant binary expression");
2019-11-14 12:14:46 +08:00
nodes[i].token = T_CONSTANT;
nodes[i].value.type = V_INT;
nodes[i].value.intData = found - 1;
RemoveNodes(_nodes, i - 2, 2);
2019-11-14 11:57:39 +08:00
i -= 2;
}
}
}
}*/
2019-11-14 12:14:46 +08:00
} else if ( token == T_NOT || token == T_BWNOT || token == T_NEGATE ) {
if ( nodes [ i - 1 ]. token == T_CONSTANT && nodes [ i - 1 ]. value . type != V_STRING ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Folding constant unary expression" );
2019-11-14 12:14:46 +08:00
matched = 1 ;
if ( token == T_NOT ) {
nodes [ i - 1 ]. value . type = V_INT ;
nodes [ i - 1 ]. value . intData = ! nodes [ i - 1 ]. value . intData ;
} else if ( token == T_BWNOT ) {
if ( nodes [ i - 1 ]. value . type != V_INT ) parseWarningAtNode ( & nodes [ i - 1 ], "Optimizer encountered a bitwise operation on a float constant" );
nodes [ i - 1 ]. value . type = V_INT ;
nodes [ i - 1 ]. value . intData = ~ nodes [ i - 1 ]. value . intData ;
2019-11-14 11:57:39 +08:00
} else {
2019-11-14 12:14:46 +08:00
if ( nodes [ i - 1 ]. value . type == V_INT )
nodes [ i - 1 ]. value . intData = - nodes [ i - 1 ]. value . intData ;
2019-11-14 11:57:39 +08:00
else
2019-11-14 12:14:46 +08:00
nodes [ i - 1 ]. value . floatData = - nodes [ i - 1 ]. value . floatData ;
2019-11-14 11:57:39 +08:00
}
RemoveNodes ( _nodes , i , 1 );
i -= 1 ;
}
}
}
return matched ;
}
2019-11-14 12:59:22 +08:00
static int isValidMathOp ( int op ) {
// 43 45 42 47
2020-02-02 16:30:45 +08:00
return op == '+' || op == '-' || op == '*' || op == '/' ; // | op == T_DIV2
2019-11-14 12:59:22 +08:00
}
// optimizes remaining not optimized mathematical operations (except for logical operations) | added: Fakels
static int ConstantFoldingPassTwo ( NodeList * _nodes ) {
int i , token , _token , isStartExp = 0 ;
int nResult = 0 , tokenOp = 0 , isNotEquals = 0 , matched = 0 , nonConstant = 0 ;
Node * nodes = _nodes -> nodes ;
for ( i = 2 ; i < _nodes -> numNodes ; i ++ )
{
token = nodes [ i ]. token ;
if ( ! isStartExp ){
if ( token == T_START_EXPRESSION ) isStartExp = 1 ;
continue ;
} else if ( token == T_END_EXPRESSION ) {
isStartExp = 0 ;
nonConstant = 0 ;
continue ;
}
if ( token == T_SYMBOL ) {
tokenOp = 0 ;
// remember position for last non-const symbol
if ( nodes [ i + 2 ]. token != '+' ) nonConstant = i + 1 ; // skip for 'T_SYMBOL + a'
continue ;
}
if ( token == T_CONSTANT && nodes [ i ]. value . type != V_STRING && ( nonConstant == 0 || nonConstant < i )) {
_token = nodes [ i + 1 ]. token ;
if ( ! isValidMathOp ( _token )) {
tokenOp = 0 ;
continue ;
}
if ( ! tokenOp ) {
tokenOp = _token ;
nResult = i ;
continue ;
}
if ( tokenOp != _token ) {
isNotEquals = 1 ;
if ( tokenOp == '+' && _token == '-' ) isNotEquals = 0 ; // exclusion for 'a + b - c'
}
if ( ! isNotEquals ) {
parseMessageAtNode ( & nodes [ i + 1 ], "Pass two: Folding constant mathematics expression" );
PerformConstOp ( & nodes [ nResult ]. value , & nodes [ i ]. value , & nodes [ nResult ]. value , _token , & nodes [ i + 1 ]);
RemoveNodes ( _nodes , i , 2 );
i -= 2 ;
matched = 1 ;
} else {
isNotEquals = 0 ;
}
tokenOp = 0 ;
i -- ;
}
}
return matched ;
}
2019-11-14 11:57:39 +08:00
static int ConstantPropagateExpression ( Node * nodes , Variable * vars , Value * values , int varCount , int * _i ) {
2019-11-14 12:14:46 +08:00
int expressiondepth = 1 , matched = 0 , token , var , i = * _i ;
assert ( nodes [ i ]. token == T_START_EXPRESSION );
while ( expressiondepth ) {
2019-11-14 11:57:39 +08:00
token = nodes [ ++ i ]. token ;
2019-11-14 12:14:46 +08:00
if ( token == T_START_EXPRESSION ) expressiondepth ++ ;
else if ( token == T_END_EXPRESSION ) expressiondepth -- ;
else if ( token == T_SYMBOL ) {
if (( var = LookupVariable ( & nodes [ i ])) != - 1 ) {
if ( values [ var ]. type != - 1 ) {
nodes [ i ]. token = T_CONSTANT ;
nodes [ i ]. value = values [ var ];
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "propagating constant %d" , values [ var ]. intData );
2019-11-14 12:14:46 +08:00
matched = 1 ;
2019-11-14 11:57:39 +08:00
}
}
}
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == T_END_EXPRESSION );
* _i = i ;
2019-11-14 11:57:39 +08:00
return matched ;
}
static int * FindAssignmentsInBlock ( const Node * nodes , const Variable * vars , int varCount ) {
2019-11-14 12:14:46 +08:00
int * results = ( int * ) calloc ( 1 , varCount * 4 );
int statementdepth = 1 , var ;
assert ( nodes -> token == T_START_STATEMENT );
while ( statementdepth ) {
switch ( nodes -> token ) {
case T_START_STATEMENT :
statementdepth ++ ;
break ;
case T_END_STATEMENT :
statementdepth -- ;
break ;
case T_ASSIGN :
case T_ASSIGN_ADD :
case T_ASSIGN_SUB :
case T_ASSIGN_MUL :
case T_ASSIGN_DIV :
if ( nodes [ - 1 ]. token == T_SYMBOL && ( var = LookupVariable ( & nodes [ - 1 ])) != - 1 ) {
results [ var ] = 1 ;
2019-11-14 11:57:39 +08:00
}
break ;
}
nodes ++ ;
}
return results ;
}
static int ConstantPropagateBlock ( Node * nodes , int * _i , Variable * vars , Value * values , int varCount ) {
2019-11-14 12:14:46 +08:00
int i = _i ? * _i : 0 ,
matched = 0 ,
blockdepth = 1 ,
token , var , blockbegin , blockend , j ;
assert ( nodes -> token == T_BEGIN || nodes -> token == T_START_STATEMENT );
if ( nodes [ i ]. token == T_BEGIN ) {
blockbegin = T_BEGIN ;
blockend = T_END ;
2019-11-14 11:57:39 +08:00
} else {
2019-11-14 12:14:46 +08:00
blockbegin = T_START_STATEMENT ;
blockend = T_END_STATEMENT ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
2019-11-14 11:57:39 +08:00
i ++ ;
2019-11-14 12:14:46 +08:00
while ( 1 ) {
token = nodes [ i ]. token ;
if ( token == blockbegin ) blockdepth ++ ;
else if ( token == blockend ) {
if ( !-- blockdepth ) break ;
} else if ( token == T_ASSIGN || token == T_ASSIGN_ADD || token == T_ASSIGN_SUB || token == T_ASSIGN_MUL || token == T_ASSIGN_DIV ) {
if (( var = LookupVariable ( & nodes [ i - 1 ])) != - 1 ) {
if ( nodes [ i + 1 ]. token == T_START_EXPRESSION && nodes [ i + 2 ]. token == T_CONSTANT && nodes [ i + 3 ]. token == T_END_EXPRESSION ) {
if ( token == T_ASSIGN )
values [ var ] = nodes [ i + 2 ]. value ;
else if ( values [ var ]. type != - 1 ) {
switch ( token ) {
case T_ASSIGN_ADD : token = '+' ; break ;
case T_ASSIGN_SUB : token = '-' ; break ;
case T_ASSIGN_MUL : token = '*' ; break ;
case T_ASSIGN_DIV : token = '/' ; break ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
PerformConstOp ( & values [ var ], & nodes [ i + 2 ]. value , & values [ var ], token , & nodes [ i ]);
2019-11-14 11:57:39 +08:00
}
} else {
2019-11-14 12:14:46 +08:00
values [ var ]. type = - 1 ;
2019-11-14 11:57:39 +08:00
}
}
2019-11-14 12:14:46 +08:00
} else if ( token == T_IF ) {
int * aif = 0 , * aelse = 0 ;
Value * backup = ( Value * ) malloc ( varCount * sizeof ( Value ));
memcpy ( backup , values , varCount * sizeof ( Value ));
2019-11-14 11:57:39 +08:00
i ++ ;
2019-11-14 12:14:46 +08:00
matched |= ConstantPropagateExpression ( nodes , vars , values , varCount , & i );
2019-11-14 11:57:39 +08:00
i ++ ;
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ++ ]. token == T_THEN );
aif = FindAssignmentsInBlock ( & nodes [ i ], vars , varCount );
2019-11-14 11:57:39 +08:00
ConstantPropagateBlock ( nodes , & i , vars , values , varCount );
2019-11-14 12:14:46 +08:00
if ( nodes [ i + 1 ]. token == T_ELSE ) {
i += 2 ;
memcpy ( values , backup , varCount * sizeof ( Value ));
aelse = FindAssignmentsInBlock ( & nodes [ i ], vars , varCount );
for ( j = 0 ; j < varCount ; j ++ ) if ( aelse [ j ]) aif [ j ] = 1 ;
2019-11-14 11:57:39 +08:00
ConstantPropagateBlock ( nodes , & i , vars , values , varCount );
}
2019-11-14 12:14:46 +08:00
for ( j = 0 ; j < varCount ; j ++ ) {
if ( aif [ j ]) values [ j ]. type = - 1 ;
2019-11-14 11:57:39 +08:00
}
free ( aif );
2019-11-14 12:14:46 +08:00
if ( aelse ) free ( aelse );
2019-11-14 11:57:39 +08:00
free ( backup );
2019-11-14 12:14:46 +08:00
} else if ( token == T_WHILE ) {
2019-11-14 11:57:39 +08:00
//TODO: Check here if the while loop expression is a single symbol; if it's 0 at this point we can eat the loop, even if the loop modifies it
2019-11-14 12:14:46 +08:00
Node * nodes2 = & nodes [ i ];
2019-11-14 11:57:39 +08:00
int * invalidate ;
2019-11-14 12:14:46 +08:00
while ( nodes2 -> token != T_DO ) nodes2 ++ ;
invalidate = FindAssignmentsInBlock ( nodes2 + 1 , vars , varCount );
for ( j = 0 ; j < varCount ; j ++ ) if ( invalidate [ j ]) values [ j ]. type = - 1 ;
} else if ( token == T_START_EXPRESSION ) {
matched |= ConstantPropagateExpression ( nodes , vars , values , varCount , & i );
2019-11-14 11:57:39 +08:00
}
i ++ ;
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == blockend );
if ( _i ) * _i = i ;
2019-11-14 11:57:39 +08:00
return matched ;
}
static int ConstantPropagation ( NodeList * _nodes , Variable * vars , Value * values , int varCount , int numArgs ) {
int i ;
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < numArgs ; i ++ ) values [ i ]. type = - 1 ;
for ( i = numArgs ; i < varCount ; i ++ ) values [ i ] = vars [ i ]. value ;
2019-11-14 11:57:39 +08:00
return ConstantPropagateBlock ( _nodes -> nodes , 0 , vars , values , varCount );
}
2019-11-14 12:14:46 +08:00
#define SkipStatement { statementdepth = 1; \
assert(nodes[i - 1].token == T_START_STATEMENT); \
while (statementdepth) { if (nodes[i].token == T_START_STATEMENT) statementdepth++; \
else if (nodes[i].token == T_END_STATEMENT) statementdepth--; \
i++; \
2019-11-14 11:57:39 +08:00
}}
static void EatStatement ( NodeList * _nodes , int start ) {
2019-11-14 12:14:46 +08:00
int statementdepth = 1 , i = start + 1 ;
Node * nodes = _nodes -> nodes ;
assert ( nodes [ start ]. token == T_START_STATEMENT );
2019-11-14 11:57:39 +08:00
SkipStatement ;
RemoveNodes ( _nodes , start , i - start );
}
static void EatAssignsInWhile ( NodeList * _nodes , int start , int varid ) {
2019-11-14 12:14:46 +08:00
int statementdepth = 1 ;
Node * nodes = _nodes -> nodes ;
assert ( nodes [ start ++ ]. token == T_START_STATEMENT );
assert ( nodes [ start ]. token == T_WHILE );
while ( statementdepth ) {
switch ( nodes [ ++ start ]. token ) {
2019-11-14 11:57:39 +08:00
case T_START_STATEMENT :
2019-11-14 12:14:46 +08:00
if ( nodes [ start + 1 ]. token == T_SYMBOL && nodes [ start + 2 ]. token >= T_ASSIGN && nodes [ start + 2 ]. token <= T_ASSIGN_DIV && LookupVariable ( & nodes [ start + 1 ]) == varid ) {
2019-11-14 11:57:39 +08:00
EatStatement ( _nodes , start -- );
} else statementdepth ++ ;
break ;
case T_END_STATEMENT : statementdepth -- ; break ;
}
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ start ]. token == T_END_STATEMENT );
2019-11-14 11:57:39 +08:00
}
static void EatDeadStore ( NodeList * _nodes , int start , int var ) {
2019-11-14 12:14:46 +08:00
Node * nodes = _nodes -> nodes ;
assert ( nodes [ start ]. token == T_START_STATEMENT );
if ( nodes [ start + 1 ]. token == T_WHILE ) {
parseMessageAtNode ( & nodes [ start + 1 ], "Eating dead stores to variable %d in while loop" , var );
2019-11-14 11:57:39 +08:00
EatAssignsInWhile ( _nodes , start , var );
} else {
2019-11-14 12:14:46 +08:00
parseMessageAtNode ( & nodes [ start + 1 ], "Eating dead store to variable %d" , var );
2019-11-14 11:57:39 +08:00
EatStatement ( _nodes , start );
}
}
static int DeadStoreRemoval ( NodeList * _nodes , VariableList * vars , VarUsage * usage ) {
2019-11-14 12:14:46 +08:00
int i , matched1 , matched2 = 0 ;
Node * nodes = _nodes -> nodes ;
while ( 1 ) {
matched1 = 0 ;
2019-11-14 11:57:39 +08:00
FindVarUsage ( nodes , usage , vars -> numVariables );
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < vars -> numVariables ; i ++ ) {
if ( usage [ i ]. flags & VU_FIRST_ASSIGN_IS_DEAD ) {
if ( usage [ i ]. flags & VU_FIRST_ASSIGN_TRANSFERABLE ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ usage [ i ]. firstassign ], "Using constant variable store for initializer" );
2019-11-14 12:14:46 +08:00
assert ( nodes [ usage [ i ]. firstassign + 4 ]. token == T_CONSTANT );
vars -> variables [ i ]. value = nodes [ usage [ i ]. firstassign + 4 ]. value ;
2019-11-14 11:57:39 +08:00
}
EatDeadStore ( _nodes , usage [ i ]. firstassign , i );
2019-11-14 12:14:46 +08:00
matched1 = 1 ;
2019-11-14 11:57:39 +08:00
break ;
2019-11-14 12:14:46 +08:00
} else if ( usage [ i ]. lastassign >= usage [ i ]. lastuse && usage [ i ]. flags & VU_LAST_ASSIGN_IS_PURE ) {
if ( nodes [ usage [ i ]. lastassign + 1 ]. token != T_WHILE || usage [ i ]. lastassign > usage [ i ]. lastuse ) {
2019-11-14 11:57:39 +08:00
//Pretty sure I don't need the do check here
2019-11-14 12:14:46 +08:00
if ( nodes [ usage [ i ]. lastassign - 1 ]. token != T_THEN && nodes [ usage [ i ]. lastassign - 1 ]. token != T_ELSE && nodes [ usage [ i ]. lastassign - 1 ]. token != T_DO ) {
2019-11-14 11:57:39 +08:00
EatDeadStore ( _nodes , usage [ i ]. lastassign , i );
2019-11-14 12:14:46 +08:00
matched1 = 1 ;
2019-11-14 11:57:39 +08:00
break ;
}
}
}
}
2019-11-14 12:14:46 +08:00
if ( matched1 ) {
matched2 = 1 ;
2019-11-14 11:57:39 +08:00
continue ;
}
break ;
};
return matched2 ;
}
static int DeadCodeRemoval ( NodeList * _nodes ) {
2019-11-14 12:14:46 +08:00
int i , statementdepth , takebranch , matched = 0 ;
Node * nodes = _nodes -> nodes ;
2019-11-14 11:57:39 +08:00
//First look for constant if branches
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < _nodes -> numNodes ; i ++ ) {
2019-11-14 11:57:39 +08:00
if ( nodes [ i ]. token == T_IF && nodes [ i - 1 ]. token == T_START_STATEMENT ) {
2019-11-14 12:14:46 +08:00
if ( nodes [ i + 1 ]. token == T_START_EXPRESSION && nodes [ i + 2 ]. token == T_CONSTANT && nodes [ i + 3 ]. token == T_END_EXPRESSION ) {
int back = i - 1 ;
matched = 1 ;
//Need to check if fallout considers 'if ("bingle")' and 'if ("")' to be 1 or 0
takebranch = nodes [ i + 2 ]. value . intData ;
2019-11-14 11:57:39 +08:00
RemoveNodes ( _nodes , -- i , 6 ); //start statement, if, start expression, const, end expression, then
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == T_START_STATEMENT );
if ( takebranch ) {
int back2 = i ++ , tmp ;
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Eliminating constant if expression (true)" );
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == T_BEGIN );
2019-11-14 11:57:39 +08:00
SkipStatement ;
2019-11-14 12:14:46 +08:00
assert ( nodes [ i - 2 ]. token == T_END );
RemoveNodes ( _nodes , i - 2 , 2 ); //end end statement
tmp = i ;
i = back2 ;
assert ( nodes [ i ]. token == T_START_STATEMENT );
assert ( nodes [ i + 1 ]. token == T_BEGIN );
2019-11-14 11:57:39 +08:00
RemoveNodes ( _nodes , i , 2 ); //begin begin statement
2019-11-14 12:14:46 +08:00
i = tmp - 4 ;
if ( nodes [ i ]. token == T_ELSE ) {
2019-11-14 11:57:39 +08:00
RemoveNodes ( _nodes , i , 1 );
EatStatement ( _nodes , i );
}
} else {
parseMessageAtNode ( & nodes [ i ], "Eliminating constant if expression (false)" );
EatStatement ( _nodes , i );
2019-11-14 12:14:46 +08:00
if ( nodes [ i ]. token == T_ELSE ) {
int back2 = i ++ , tmp ;
RemoveNodes ( _nodes , i - 1 , 1 ); //else
assert ( nodes [ i ]. token == T_BEGIN );
2019-11-14 11:57:39 +08:00
SkipStatement ;
2019-11-14 12:14:46 +08:00
assert ( nodes [ i - 2 ]. token == T_END );
RemoveNodes ( _nodes , i - 2 , 2 ); //end end statement
tmp = i ;
i = back2 ;
assert ( nodes [ i ]. token == T_START_STATEMENT );
assert ( nodes [ i + 1 ]. token == T_BEGIN );
2019-11-14 11:57:39 +08:00
RemoveNodes ( _nodes , i , 2 ); //else begin statement begin
2019-11-14 12:14:46 +08:00
i = tmp - 4 ;
2019-11-14 11:57:39 +08:00
}
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == T_END_STATEMENT );
2019-11-14 11:57:39 +08:00
RemoveNodes ( _nodes , i , 1 ); //The final end statement
2019-11-14 12:14:46 +08:00
i = back ;
2019-11-14 11:57:39 +08:00
}
}
}
//Next strip out anything following returns, breaks and continue
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < _nodes -> numNodes ; i ++ ) {
if ( nodes [ i ]. token == T_RETURN || nodes [ i ]. token == T_BREAK || nodes [ i ]. token == T_CONTINUE ) {
//if (nodes[i].token == T_RETURN)
2019-11-14 11:57:39 +08:00
SkipStatement ;
2019-11-14 12:14:46 +08:00
while ( nodes [ i ]. token == T_START_STATEMENT ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Eating statement hidden by return" );
EatStatement ( _nodes , i );
matched = 1 ;
}
}
}
//Finally look for empty if blocks and remove them if the expression is pure
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < _nodes -> numNodes - 5 ; i ++ ) {
if ( nodes [ i ]. token == T_THEN && nodes [ i + 3 ]. token == T_END && nodes [ i + 4 ]. token == T_END_STATEMENT && nodes [ i + 5 ]. token != T_ELSE ) {
int back = i , expressiondepth = 1 ;
assert ( nodes [ i + 1 ]. token == T_START_STATEMENT );
assert ( nodes [ i + 2 ]. token == T_BEGIN );
assert ( nodes [ i - 1 ]. token == T_END_EXPRESSION );
i -= 2 ;
while ( expressiondepth ) {
if ( nodes [ i ]. token == T_START_EXPRESSION ) expressiondepth -- ;
else if ( nodes [ i ]. token == T_END_EXPRESSION ) expressiondepth ++ ;
2019-11-14 11:57:39 +08:00
i -- ;
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ i ]. token == T_IF );
assert ( nodes [ -- i ]. token == T_START_STATEMENT );
if ( IsExpressionPure ( & nodes [ i + 2 ])) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Eating empty if block" );
EatStatement ( _nodes , i );
2019-11-14 12:14:46 +08:00
matched = 1 ;
2019-11-14 11:57:39 +08:00
} else {
parseWarningAtNode ( & nodes [ i ], "Optimizer found an empty if block, but could not remove it because the expression could not be confirmed as pure" );
}
2019-11-14 12:14:46 +08:00
i = back ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
if ( nodes [ i ]. token == T_ELSE && nodes [ i + 3 ]. token == T_END && nodes [ i + 4 ]. token == T_END_STATEMENT ) {
int back = i , expressiondepth = 1 ;
assert ( nodes [ i + 1 ]. token == T_START_STATEMENT );
assert ( nodes [ i + 2 ]. token == T_BEGIN );
assert ( nodes [ i - 1 ]. token == T_END_STATEMENT );
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Eating empty else block" );
RemoveNodes ( _nodes , i , 1 );
EatStatement ( _nodes , i );
2019-11-14 12:14:46 +08:00
matched = 1 ;
2019-11-14 11:57:39 +08:00
}
}
return matched ;
}
static int CanCombine ( VarUsage * u1 , VarUsage * u2 ) {
2019-11-14 12:14:46 +08:00
int u1min = u1 -> firstassign < u1 -> firstuse ? u1 -> firstassign : u1 -> firstuse ;
int u2max = u2 -> lastassign > u2 -> lastuse ? u2 -> lastassign : u2 -> lastuse ;
if ( u1min < u2max ) return 0 ;
if ( u1min > u2max ) return 1 ;
2019-11-14 11:57:39 +08:00
//TODO: If they're equal, if we're in a while loop return 0, otherwise if one is an assign and the other is part of the expression we can probably combine
return 0 ;
}
static void VariableReuse ( NodeList * _nodes , VariableList * vars , int numArgs , VarUsage * usage ) {
int i , j , k ;
Node * nodes = _nodes -> nodes ;
FindVarUsage ( nodes , usage , vars -> numVariables );
2019-11-14 12:14:46 +08:00
for ( i = vars -> numVariables - 1 ; i >= numArgs ; i -- ) {
for ( j = 0 ; j < i ; j ++ ) {
if ( CanCombine ( & usage [ i ], & usage [ j ]) || CanCombine ( & usage [ j ], & usage [ i ])) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ 0 ], "Merging variables %d and %d" , i , j );
2019-11-14 12:14:46 +08:00
for ( k = 0 ; k < _nodes -> numNodes ; k ++ ) {
if ( nodes [ k ]. token == T_SYMBOL && LookupVariable ( & nodes [ k ]) == i ) nodes [ k ]. value . intData = j ;
2019-11-14 11:57:39 +08:00
}
FindVarUsage ( nodes , usage , vars -> numVariables );
break ;
}
}
}
}
static void DeadVariableRemoval ( NodeList * _nodes , VariableList * vars , int numArgs ) {
int i , var , j ;
2019-11-14 12:14:46 +08:00
Node * nodes = _nodes -> nodes ;
int * uses = ( int * ) calloc ( 1 , vars -> numVariables * 4 );
for ( i = 0 ; i < vars -> numVariables ; i ++ ) uses [ i ] = 0 ;
for ( i = 0 ; i < _nodes -> numNodes ; i ++ ) {
2023-05-25 01:47:52 +02:00
if ( nodes [ i ]. token == T_SYMBOL && ( var = LookupVariable ( & nodes [ i ])) != - 1 ) {
uses [ var ] ++ ;
// If any proc argument is used, mark all previous as used too, to prevent argument values being swapped.
if ( var < numArgs ) {
for ( j = 0 ; j < var ; j ++ ) {
if ( uses [ j ] == 0 ) uses [ j ] ++ ;
}
}
}
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
for ( i = vars -> numVariables - 1 ; i >= 0 ; i -- ) {
if ( ! uses [ i ]) {
memmove ( & vars -> variables [ i ], & vars -> variables [ i + 1 ], ( vars -> numVariables - ( i + 1 )) * sizeof ( Variable ));
2019-11-14 11:57:39 +08:00
vars -> numVariables -- ;
parseMessageAtNode ( & nodes [ 0 ], "Eliminating variable id %d" , i );
2019-11-14 12:14:46 +08:00
for ( j = 0 ; j < _nodes -> numNodes ; j ++ ) {
if ( nodes [ j ]. token == T_SYMBOL && LookupVariable ( & nodes [ j ]) > i ) nodes [ j ]. value . intData -- ;
2019-11-14 11:57:39 +08:00
}
}
}
free ( uses );
}
static int Combine ( NodeList * _nodes ) {
2019-11-14 12:14:46 +08:00
int i , depth , j , k , matched = 0 ;
2019-11-14 11:57:39 +08:00
char op ;
2019-11-14 12:14:46 +08:00
Node * nodes = _nodes -> nodes ;
for ( i = 5 ; i < _nodes -> numNodes - 4 ; i ++ ) {
if ( nodes [ i ]. token < T_ASSIGN_ADD || nodes [ i ]. token > T_ASSIGN_DIV ) continue ;
switch ( nodes [ i ]. token ) {
case T_ASSIGN_ADD : op = '+' ; break ;
case T_ASSIGN_SUB : op = '-' ; break ;
case T_ASSIGN_MUL : op = '*' ; break ;
case T_ASSIGN_DIV : op = '/' ; break ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ i + 1 ]. token == T_START_EXPRESSION );
assert ( nodes [ i - 2 ]. token == T_START_STATEMENT );
assert ( nodes [ i - 1 ]. token == T_SYMBOL );
if ( nodes [ i - 3 ]. token != T_END_STATEMENT ) continue ;
depth = 1 ;
j = i - 3 ;
while ( depth ) {
2019-11-14 11:57:39 +08:00
j -- ;
2019-11-14 12:14:46 +08:00
if ( nodes [ j ]. token == T_START_STATEMENT ) depth -- ;
else if ( nodes [ j ]. token == T_END_STATEMENT ) depth ++ ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ j ]. token == T_START_STATEMENT );
if ( nodes [ j + 1 ]. token != T_SYMBOL || nodes [ j + 2 ]. token != T_ASSIGN || nodes [ j + 1 ]. value . intData != nodes [ i - 1 ]. value . intData || nodes [ j + 1 ]. value . type != nodes [ i - 1 ]. value . type ) continue ;
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( & nodes [ i ], "Combining assignments" );
2019-11-14 12:14:46 +08:00
matched = 1 ;
2019-11-14 11:57:39 +08:00
2019-11-14 12:14:46 +08:00
assert ( nodes [ i - 4 ]. token == T_END_EXPRESSION );
depth = 1 ;
k = i + 1 ;
while ( depth ) {
2019-11-14 11:57:39 +08:00
k ++ ;
2019-11-14 12:14:46 +08:00
if ( nodes [ k ]. token == T_START_EXPRESSION ) depth ++ ;
else if ( nodes [ k ]. token == T_END_EXPRESSION ) depth -- ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
assert ( nodes [ k ]. token == T_END_EXPRESSION );
assert ( nodes [ k + 1 ]. token == T_END_STATEMENT );
memmove ( & nodes [ i - 4 ], & nodes [ i + 2 ], ( k - i - 1 ) * sizeof ( Node ));
assert ( nodes [ k - 6 ]. token == T_END_EXPRESSION );
nodes [ k - 6 ]. token = op ;
nodes [ k - 5 ]. token = T_END_EXPRESSION ;
nodes [ k - 4 ]. token = T_END_STATEMENT ;
memmove ( & nodes [ k - 3 ], & nodes [ k + 2 ], ( _nodes -> numNodes - k - 2 ) * sizeof ( Node ));
_nodes -> numNodes -= 6 ;
2019-11-14 11:57:39 +08:00
}
return matched ;
}
static void OptimizeProcedure ( Procedure * proc ) {
int found ;
2019-11-14 12:14:46 +08:00
int hasVars = proc -> variables . variables != 0 ;
2019-11-14 11:57:39 +08:00
Value * values ;
VarUsage * usage ;
2019-11-14 12:14:46 +08:00
if ( hasVars ) {
values = ( Value * ) malloc ( sizeof ( Value ) * proc -> variables . numVariables );
usage = ( VarUsage * ) malloc ( proc -> variables . numVariables * sizeof ( VarUsage ));
2019-11-14 11:57:39 +08:00
}
do {
2019-11-14 12:14:46 +08:00
found = 0 ;
found = ConstantFolding ( & proc -> nodes );
if ( hasVars ) {
if ( optimize >= 3 ) { // constant propagation is known to break code
found |= ConstantPropagation ( & proc -> nodes , proc -> variables . variables , values , proc -> variables . numVariables , proc -> numArgs );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
found |= DeadStoreRemoval ( & proc -> nodes , & proc -> variables , usage );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
found |= DeadCodeRemoval ( & proc -> nodes );
found |= Combine ( & proc -> nodes );
} while ( found );
2019-11-14 12:59:22 +08:00
#ifdef _DEBUG
while ( ConstantFoldingPassTwo ( & proc -> nodes )); // Additional optimization passes
#endif
2019-11-14 12:14:46 +08:00
if ( hasVars ) {
2019-11-14 11:57:39 +08:00
DeadVariableRemoval ( & proc -> nodes , & proc -> variables , proc -> numArgs ); //use this twice so that VariableReuse has no completely dead variables to worry about
2019-11-14 12:14:46 +08:00
if ( optimize >= 3 ) { // variable reuse is known to break code
2019-11-14 11:57:39 +08:00
VariableReuse ( & proc -> nodes , & proc -> variables , proc -> numArgs , usage );
DeadVariableRemoval ( & proc -> nodes , & proc -> variables , proc -> numArgs );
}
free ( values );
free ( usage );
}
}
int IsProtectedProc ( const char * c ) {
// this should be the full list of procedures (copy-pasted directly from EXE)
Protect ( "no_p_proc" );
Protect ( "start" );
Protect ( "spatial_p_proc" );
Protect ( "description_p_proc" );
Protect ( "desc_p_proc" ); // Fallout 1
Protect ( "pickup_p_proc" );
Protect ( "drop_p_proc" );
Protect ( "use_p_proc" );
Protect ( "use_obj_on_p_proc" );
Protect ( "use_skill_on_p_proc" );
Protect ( "talk_p_proc" );
Protect ( "critter_p_proc" );
Protect ( "combat_p_proc" );
Protect ( "damage_p_proc" );
Protect ( "map_enter_p_proc" );
Protect ( "map_exit_p_proc" );
Protect ( "create_p_proc" );
Protect ( "destroy_p_proc" );
Protect ( "look_at_p_proc" );
Protect ( "timed_event_p_proc" );
Protect ( "map_update_p_proc" );
Protect ( "push_p_proc" );
Protect ( "is_dropping_p_proc" );
Protect ( "combat_is_starting_p_proc" );
Protect ( "combat_is_over_p_proc" );
2019-11-14 12:14:46 +08:00
//Protect("node998");
//Protect("node999");
2019-11-14 11:57:39 +08:00
return 0 ;
}
static int __once = 0 ;
static void EliminateProcedure ( Program * prog , int id ) {
int i , j ;
parseMessageAtNode ( & prog -> procedures . procedures [ id ]. nodes . nodes [ 0 ], "Eliminating procedure %s" , prog -> namelist + prog -> procedures . procedures [ id ]. name );
/*, remLen, srcOfs;
long *namesLen;
char *src, *dst;
namesLen = (long*)prog->namelist;
dst = prog->namelist + prog->procedures.procedures[id].name - 2;
src = dst + 2 + *(short*)dst;
srcOfs = src - prog->namelist + 2;
remLen = src - dst;
//parseMessageAtNode(&prog->procedures.procedures[id].nodes.nodes[0], "Eliminating procedure %s total=%d srcOfs=%d dstOfs=%d rem=%d move=%d", prog->namelist + prog->procedures.procedures[id].name, namesLen, srcOfs-prog->namelist, dstOfs-prog->namelist, remLen, prog->namelist+*namesLen-srcOfs);
// remove from name list
if (!__once) {
memmove(dst, src, *namesLen - (src - prog->namelist) + 4);
for (i = id; i < prog->procedures.numProcedures; i++) {
if (prog->procedures.procedures[i].name >= srcOfs)
prog->procedures.procedures[i].name -= remLen;
}
for (i = 0; i < prog->variables.numVariables; i++) {
if (prog->variables.variables[i].name >= srcOfs)
prog->variables.variables[i].name -= remLen;
}
for (i = 0; i < prog->externals.numVariables; i++) {
if (prog->externals.variables[i].name >= srcOfs)
prog->externals.variables[i].name -= remLen;
}
*namesLen -= remLen;
__once = 1;
}*/
2019-11-14 12:14:46 +08:00
memmove ( & prog -> procedures . procedures [ id ], & prog -> procedures . procedures [ id + 1 ], ( prog -> procedures . numProcedures - ( id + 1 )) * sizeof ( Procedure ));
2019-11-14 11:57:39 +08:00
prog -> procedures . numProcedures -- ;
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
NodeList * list = & prog -> procedures . procedures [ i ]. nodes ;
for ( j = 0 ; j < list -> numNodes ; j ++ ) {
if ( list -> nodes [ j ]. token == T_SYMBOL && ( list -> nodes [ j ]. value . type & P_PROCEDURE ) && list -> nodes [ j ]. value . intData >= id ) {
//assert(list->nodes[j].value.intData != id); //The procedure elimination can now cope with circular references, which would trigger this assert
2019-11-14 11:57:39 +08:00
list -> nodes [ j ]. value . intData -- ;
}
}
2019-11-14 12:14:46 +08:00
if ( prog -> procedures . procedures [ i ]. type & P_CONDITIONAL ) {
list = & prog -> procedures . procedures [ i ]. condition ;
for ( j = 0 ; j < list -> numNodes ; j ++ ) {
if ( list -> nodes [ j ]. token == T_SYMBOL && ( list -> nodes [ j ]. value . type & P_PROCEDURE ) && list -> nodes [ j ]. value . intData >= id ) {
//assert(list->nodes[j].value.intData != id);
2019-11-14 11:57:39 +08:00
list -> nodes [ j ]. value . intData -- ;
}
}
}
}
}
//This is the old method of procedure elimination; it wouldn't correctly remove circular references
/*static void UpdateProcedureReferences(Procedure* procs, int count) {
2019-11-14 12:14:46 +08:00
int i, j;
2019-11-14 11:57:39 +08:00
Node* node;
2019-11-14 12:14:46 +08:00
for (i = 1; i < count; i++) procs[i].uses = 0;
for (i = 1; i < count; i++) {
for (j = 0; j < procs[i].nodes.numNodes; j++) {
node = &procs[i].nodes.nodes[j];
if (node->token == T_SYMBOL && node->value.type == (P_PROCEDURE | P_LOCAL)) procs[node->value.intData].uses++;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
if (procs[i].type & P_CONDITIONAL) {
for (j = 0; j < procs[i].condition.numNodes; j++) {
node = &procs[i].condition.nodes[j];
if (node->token == T_SYMBOL && node->value.type == (P_PROCEDURE | P_LOCAL)) procs[node->value.intData].uses++;
2019-11-14 11:57:39 +08:00
}
}
}
}
static void DecendUnusedProcedures(Program *prog) {
2019-11-14 12:14:46 +08:00
int i, matched = 1;
while (matched) {
matched = 0;
2019-11-14 11:57:39 +08:00
UpdateProcedureReferences(prog->procedures.procedures, prog->procedures.numProcedures);
2019-11-14 12:14:46 +08:00
for (i = 1; i < prog->procedures.numProcedures; i++) {
if (prog->procedures.procedures[i].type & (P_TIMED | P_IMPORT | P_EXPORT | P_CONDITIONAL | P_CRITICAL)) continue;
if (!prog->procedures.procedures[i].uses && !IsProtectedProc(prog->namelist + prog->procedures.procedures[i].name)) {
2019-11-14 11:57:39 +08:00
EliminateProcedure(prog, i--);
2019-11-14 12:14:46 +08:00
matched = 1;
2019-11-14 11:57:39 +08:00
}
}
}
}*/
2023-06-09 15:37:27 +02:00
static int IsProcedureReference ( Node * node ) {
return node -> token == T_SYMBOL && (( node -> value . type & ( P_PROCEDURE | P_LOCAL )) == ( P_PROCEDURE | P_LOCAL ));
}
2019-11-14 11:57:39 +08:00
static void UpdateProcedureReferences ( Procedure * procs , int count ) {
2019-11-14 12:14:46 +08:00
int i , j , matched = 1 ;
2019-11-14 11:57:39 +08:00
Node * node ;
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < count ; i ++ ) {
if ( IsProtectedProc ( currprogram -> namelist + procs [ i ]. name ) || procs [ i ]. type & ( P_TIMED | P_CONDITIONAL | P_EXPORT /* | P_CRITICAL*/ )) procs [ i ]. uses = 1 ;
//else if (procs[i].type & P_IMPORT) procs[i].uses = 2;
else procs [ i ]. uses = 0 ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
while ( matched ) {
matched = 0 ;
for ( i = 1 ; i < count ; i ++ ) {
if ( procs [ i ]. uses != 1 || procs [ i ]. type & P_IMPORT ) continue ;
procs [ i ]. uses = 2 ;
for ( j = 0 ; j < procs [ i ]. nodes . numNodes ; j ++ ) {
node = & procs [ i ]. nodes . nodes [ j ];
2023-06-09 15:37:27 +02:00
if ( IsProcedureReference ( node )) {
2019-11-14 12:14:46 +08:00
if ( ! procs [ node -> value . intData ]. uses ) {
matched = 1 ;
procs [ node -> value . intData ]. uses = 1 ;
2019-11-14 11:57:39 +08:00
}
}
}
2019-11-14 12:14:46 +08:00
if ( procs [ i ]. type & P_CONDITIONAL ) {
for ( j = 0 ; j < procs [ i ]. condition . numNodes ; j ++ ) {
node = & procs [ i ]. condition . nodes [ j ];
2023-06-09 15:37:27 +02:00
if ( IsProcedureReference ( node )) {
2019-11-14 12:14:46 +08:00
if ( ! procs [ node -> value . intData ]. uses ) {
matched = 1 ;
procs [ node -> value . intData ]. uses = 1 ;
2019-11-14 11:57:39 +08:00
}
}
}
}
}
}
}
static void DecendUnusedProcedures ( Program * prog ) {
int i ;
UpdateProcedureReferences ( prog -> procedures . procedures , prog -> procedures . numProcedures );
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
if ( ! prog -> procedures . procedures [ i ]. uses ) EliminateProcedure ( prog , i -- );
2019-11-14 11:57:39 +08:00
}
}
static int ConstantPropagateGlobals ( Program * prog ) {
2019-11-14 12:14:46 +08:00
Value * values = ( Value * ) malloc ( prog -> variables . numVariables * sizeof ( Variable ));
int i , j , matched = 0 ;
2019-11-14 11:57:39 +08:00
//Make initial list of variables
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) {
2019-11-14 11:57:39 +08:00
values [ i ] = prog -> variables . variables [ i ]. value ;
}
//Check for any that are assigned
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
for ( j = 1 ; j < prog -> procedures . procedures [ i ]. nodes . numNodes ; j ++ ) {
if ( prog -> procedures . procedures [ i ]. nodes . nodes [ j ]. token >= T_ASSIGN && prog -> procedures . procedures [ i ]. nodes . nodes [ j ]. token <= T_ASSIGN_DIV ) {
Node * node = & prog -> procedures . procedures [ i ]. nodes . nodes [ j - 1 ];
assert ( node -> token == T_SYMBOL );
if ( node -> value . type == P_GLOBAL ) values [ node -> value . intData ]. type = - 1 ;
2019-11-14 11:57:39 +08:00
}
}
}
//Constant propagate any that are not
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
for ( j = 1 ; j < prog -> procedures . procedures [ i ]. nodes . numNodes ; j ++ ) {
Node * node = & prog -> procedures . procedures [ i ]. nodes . nodes [ j ];
if ( node -> token == T_SYMBOL && node -> value . type == P_GLOBAL && values [ node -> value . intData ]. type != - 1 ) {
if ( node [ 1 ]. token == T_START_EVENT ) {
2019-11-14 11:57:39 +08:00
parseWarningAtNode ( node , "Global variable '%s' is called as a procedure butnever recieves a value" , prog -> namelist + prog -> variables . variables [ node -> value . intData ]. name );
} else {
parseMessageAtNode ( node , "Constant propagating global variable %s" , prog -> namelist + prog -> variables . variables [ node -> value . intData ]. name );
2019-11-14 12:14:46 +08:00
node -> token = T_CONSTANT ;
node -> value = values [ node -> value . intData ];
matched = 1 ;
2019-11-14 11:57:39 +08:00
}
}
}
}
free ( values );
return matched ;
}
static void ShiftSymbolIntValues ( Program * prog , int var , int matchType ) {
2019-11-14 12:14:46 +08:00
int i , j ;
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
NodeList * list = & prog -> procedures . procedures [ i ]. nodes ;
for ( j = 0 ; j < list -> numNodes ; j ++ ) {
if ( list -> nodes [ j ]. token == T_SYMBOL && list -> nodes [ j ]. value . type == matchType && list -> nodes [ j ]. value . intData >= var ) {
assert ( list -> nodes [ j ]. value . intData != var );
2019-11-14 11:57:39 +08:00
list -> nodes [ j ]. value . intData -- ;
}
}
2019-11-14 12:14:46 +08:00
if ( prog -> procedures . procedures [ i ]. type & P_CONDITIONAL ) {
list = & prog -> procedures . procedures [ i ]. condition ;
for ( j = 0 ; j < list -> numNodes ; j ++ ) {
if ( list -> nodes [ j ]. token == T_SYMBOL && list -> nodes [ j ]. value . type == matchType && list -> nodes [ j ]. value . intData >= var ) {
assert ( list -> nodes [ j ]. value . intData != var );
2019-11-14 11:57:39 +08:00
list -> nodes [ j ]. value . intData -- ;
}
}
}
}
}
static void EliminateUnreferencedGlobals ( Program * prog ) {
2019-11-14 12:14:46 +08:00
int i , j , var ;
2019-11-14 11:57:39 +08:00
Node * node ;
//count variable references
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) prog -> variables . variables [ i ]. uses = 0 ;
for ( i = 0 ; i < prog -> externals . numVariables ; i ++ )
prog -> externals . variables [ i ]. uses = ( prog -> externals . variables [ i ]. type == V_EXPORT ); // assume export variable is always used
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
for ( j = 0 ; j < prog -> procedures . procedures [ i ]. nodes . numNodes ; j ++ ) {
node = & prog -> procedures . procedures [ i ]. nodes . nodes [ j ];
if ( node -> token == T_SYMBOL ) {
if ( node -> value . type == P_GLOBAL ) prog -> variables . variables [ node -> value . intData ]. uses ++ ;
if ( node -> value . type == P_EXTERN ) prog -> externals . variables [ node -> value . intData ]. uses ++ ;
2019-11-14 11:57:39 +08:00
}
}
2019-11-14 12:14:46 +08:00
if ( prog -> procedures . procedures [ i ]. type & P_CONDITIONAL ) {
for ( j = 0 ; j < prog -> procedures . procedures [ i ]. condition . numNodes ; j ++ ) {
node = & prog -> procedures . procedures [ i ]. condition . nodes [ j ];
if ( node -> token == T_SYMBOL ) {
if ( node -> value . type == P_GLOBAL ) prog -> variables . variables [ node -> value . intData ]. uses ++ ;
if ( node -> value . type == P_EXTERN ) prog -> externals . variables [ node -> value . intData ]. uses ++ ;
2019-11-14 11:57:39 +08:00
}
}
}
}
//remove any variables that aren't referenced or exported
2019-11-14 12:14:46 +08:00
for ( var = prog -> variables . numVariables - 1 ; var >= 0 ; var -- ) {
if ( ! prog -> variables . variables [ var ]. uses ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( 0 , "Eliminating unused global variable %s" , prog -> namelist + prog -> variables . variables [ var ]. name );
2019-11-14 12:14:46 +08:00
memmove ( & prog -> variables . variables [ var ], & prog -> variables . variables [ var + 1 ], ( prog -> variables . numVariables - ( var + 1 )) * sizeof ( Variable ));
2019-11-14 11:57:39 +08:00
prog -> variables . numVariables -- ;
ShiftSymbolIntValues ( prog , var , P_GLOBAL );
}
}
// remove unreferenced import variables
2019-11-14 12:14:46 +08:00
for ( var = prog -> externals . numVariables - 1 ; var >= 0 ; var -- ) {
if ( ! prog -> externals . variables [ var ]. uses ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( 0 , "Eliminating unused import variable %s" , prog -> namelist + prog -> externals . variables [ var ]. name );
2019-11-14 12:14:46 +08:00
memmove ( & prog -> externals . variables [ var ], & prog -> externals . variables [ var + 1 ], ( prog -> externals . numVariables - ( var + 1 )) * sizeof ( Variable ));
2019-11-14 11:57:39 +08:00
prog -> externals . numVariables -- ;
ShiftSymbolIntValues ( prog , var , P_EXTERN );
}
}
}
2023-06-09 02:36:11 +02:00
static void GetNamelistData ( char * namelist , int * outEntries , char ** outEndptr , int ** outRefs , int ** outOffsets , int ** outTransforms ) {
char * list = namelist + 4 , * endptr ;
int entries = 0 , * refs , * offsets , * transforms , i ;
2019-11-14 11:57:39 +08:00
2019-11-14 12:14:46 +08:00
while ( * ( unsigned short * ) list != 0xffff ) {
2019-11-14 11:57:39 +08:00
entries ++ ;
2019-11-14 12:14:46 +08:00
list += * ( unsigned short * ) list + 2 ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
endptr = list + 2 ;
refs = ( int * ) calloc ( 1 , entries * 4 );
offsets = ( int * ) malloc ( entries * 4 );
transforms = ( int * ) malloc ( entries * 4 );
2019-11-14 11:57:39 +08:00
//first find the offsets
2023-06-09 02:36:11 +02:00
list = namelist + 4 ;
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < entries ; i ++ ) {
2023-06-09 02:36:11 +02:00
offsets [ i ] = 2 + ( unsigned int ) list - ( unsigned int ) namelist ;
2019-11-14 12:14:46 +08:00
list += * ( unsigned short * ) list + 2 ;
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
memcpy ( transforms , offsets , entries * 4 );
2023-06-09 02:36:11 +02:00
* outEntries = entries ;
* outEndptr = endptr ;
* outRefs = refs ;
* outOffsets = offsets ;
* outTransforms = transforms ;
}
static void FreeNamelistData ( int * refs , int * offsets , int * transforms ) {
free ( refs );
free ( offsets );
free ( transforms );
}
static void RemoveFromNamelist ( char * namelist , int i , int entries , int * offsets , int * transforms , char ** endptr , char * logFormat ) {
2023-06-10 22:02:08 +08:00
char * namestart ;
int j , len ;
2023-06-09 02:36:11 +02:00
parseMessageAtNode ( 0 , logFormat , namelist + offsets [ i ]);
2023-06-10 22:02:08 +08:00
namestart = namelist + offsets [ i ] - 2 ;
len = * ( unsigned short * ) namestart + 2 ;
2023-06-09 02:36:11 +02:00
memmove ( namestart , len + namestart , * endptr - ( len + namestart ));
( * ( unsigned int * ) namelist ) -= len ;
* endptr -= len ;
transforms [ i ] = 0x7fffffff ;
for ( j = i + 1 ; j < entries ; j ++ ) transforms [ j ] -= len ;
}
2023-06-09 16:56:51 +02:00
static void SetNameReferenced ( int name , int entries , int * refs , int * offsets , int refType ) {
int j ;
for ( j = 0 ; j < entries ; j ++ ) {
if ( name == offsets [ j ]) {
refs [ j ] |= refType ;
break ;
}
}
}
static void ShiftName ( int * name , int entries , int * transforms , int * offsets ) {
int j ;
for ( j = 0 ; j < entries ; j ++ ) {
if ( * name == offsets [ j ]) {
assert ( transforms [ j ] != 0x7fffffff );
* name = transforms [ j ];
break ;
}
}
}
2023-06-09 02:36:11 +02:00
static void CompressNamelist ( Program * prog ) {
char * endptr , * namestart ;
2023-06-10 22:02:08 +08:00
int entries = 0 , * refs , * offsets , * transforms , i , j , len ;
2023-06-09 02:36:11 +02:00
GetNamelistData ( prog -> namelist , & entries , & endptr , & refs , & offsets , & transforms );
2019-11-14 11:57:39 +08:00
//Then check all variables, imports and procedures to see where in the namelist they point
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> externals . numVariables ; i ++ ) {
2023-06-09 16:56:51 +02:00
SetNameReferenced ( prog -> externals . variables [ i ]. name , entries , refs , offsets , 1 );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) {
2023-06-09 17:55:26 +02:00
SetNameReferenced ( prog -> variables . variables [ i ]. name , entries , refs , offsets , 2 );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
2023-06-09 16:56:51 +02:00
SetNameReferenced ( prog -> procedures . procedures [ i ]. name , entries , refs , offsets , 4 );
2019-11-14 11:57:39 +08:00
}
//For each string that isn't referenced, remove it
2019-11-14 12:14:46 +08:00
for ( i = entries - 1 ; i >= 0 ; i -- ) {
if ( ! refs [ i ]) {
2023-06-09 02:36:11 +02:00
RemoveFromNamelist ( prog -> namelist , i , entries , offsets , transforms , & endptr , "Removing unused string '%s' from program namespace" );
2023-06-10 22:02:08 +08:00
} else if ( refs [ i ] == 2 && optimize >= 3 ) {
2023-06-09 02:36:11 +02:00
namestart = prog -> namelist + offsets [ i ] - 2 ;
2023-06-10 22:02:08 +08:00
len = * ( unsigned short * ) namestart + 2 ;
2019-11-14 12:14:46 +08:00
if ( len > 4 ) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode ( 0 , "Shortening non-visible string '%s' in program namespace" , prog -> namelist + offsets [ i ]);
2023-06-09 02:36:11 +02:00
* ( unsigned short * )( namestart ) = 2 ;
2019-11-14 12:14:46 +08:00
* ( char * )( prog -> namelist + offsets [ i ] + 0 ) = 'a' ;
* ( char * )( prog -> namelist + offsets [ i ] + 1 ) = 0 ;
( * ( unsigned int * ) prog -> namelist ) -= len - 4 ;
2023-06-09 02:36:11 +02:00
memmove ( prog -> namelist + offsets [ i ] + 2 , len + namestart , endptr - ( len + namestart ));
2019-11-14 12:14:46 +08:00
for ( j = i + 1 ; j < entries ; j ++ ) transforms [ j ] -= len - 4 ;
2019-11-14 11:57:39 +08:00
}
}
}
//And finally, update the name pointers of everything else
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> externals . numVariables ; i ++ ) {
2023-06-09 16:56:51 +02:00
ShiftName ( & prog -> externals . variables [ i ]. name , entries , transforms , offsets );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) {
2023-06-09 16:56:51 +02:00
ShiftName ( & prog -> variables . variables [ i ]. name , entries , transforms , offsets );
2019-11-14 11:57:39 +08:00
}
2019-11-14 12:14:46 +08:00
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
2023-06-09 16:56:51 +02:00
ShiftName ( & prog -> procedures . procedures [ i ]. name , entries , transforms , offsets );
2019-11-14 11:57:39 +08:00
}
2023-06-09 02:36:11 +02:00
FreeNamelistData ( refs , offsets , transforms );
2019-11-14 11:57:39 +08:00
}
2024-06-09 12:01:31 +02:00
static int * GetStringOffsetRef ( Node * node , Program * prog ) {
2023-06-09 15:13:52 +02:00
if ( node -> token == T_CONSTANT && node -> value . type == V_STRING ) {
return & node -> value . stringData ;
}
return 0 ;
}
2023-06-09 16:56:51 +02:00
static void SetVariableDefaultValueReferenced ( Variable * var , int entries , int * refs , int * offsets , int refType ) {
if ( var -> initialized && var -> value . type == V_STRING ) {
SetNameReferenced ( var -> value . stringData , entries , refs , offsets , refType );
}
}
static void ShiftVariableDefaultValue ( Variable * var , int entries , int * transforms , int * offsets ) {
if ( var -> initialized && var -> value . type == V_STRING ) {
ShiftName ( & var -> value . stringData , entries , transforms , offsets );
}
}
2023-06-09 01:52:06 +02:00
static void CompressStringspace ( Program * prog ) {
2023-06-09 02:36:11 +02:00
char * endptr ;
2023-06-09 16:56:51 +02:00
int entries = 0 , * refs , * offsets , * transforms , i , k , * stringData ;
2023-06-09 01:52:06 +02:00
Procedure * proc ;
Node * node ;
2023-06-10 22:02:08 +08:00
if ( ! prog -> stringspace ) return ;
2023-06-09 02:36:11 +02:00
GetNamelistData ( prog -> stringspace , & entries , & endptr , & refs , & offsets , & transforms );
2023-06-09 16:56:51 +02:00
//Then check all procedures nodes and variable default values to see where in the stringspace they point
2023-06-09 01:52:06 +02:00
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
proc = & prog -> procedures . procedures [ i ];
for ( k = 0 ; k < proc -> nodes . numNodes ; k ++ ) {
node = & proc -> nodes . nodes [ k ];
2024-06-09 12:01:31 +02:00
if ( stringData = GetStringOffsetRef ( node , prog )) {
2023-06-09 16:56:51 +02:00
SetNameReferenced ( * stringData , entries , refs , offsets , 1 );
2023-06-09 01:52:06 +02:00
}
}
2023-06-09 16:56:51 +02:00
for ( k = 0 ; k < proc -> variables . numVariables ; k ++ ) {
SetVariableDefaultValueReferenced ( & proc -> variables . variables [ k ], entries , refs , offsets , 2 );
}
}
for ( i = 0 ; i < prog -> externals . numVariables ; i ++ ) {
SetVariableDefaultValueReferenced ( & prog -> externals . variables [ i ], entries , refs , offsets , 4 );
}
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) {
2023-06-09 17:55:26 +02:00
SetVariableDefaultValueReferenced ( & prog -> variables . variables [ i ], entries , refs , offsets , 8 );
2023-06-09 01:52:06 +02:00
}
2024-06-09 12:01:31 +02:00
//Handle stringified names
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
SetNameReferenced ( prog -> procedures . procedures [ i ]. stringifiedName , entries , refs , offsets , 16 );
}
2023-06-09 01:52:06 +02:00
//For each string that isn't referenced, remove it
for ( i = entries - 1 ; i >= 0 ; i -- ) {
if ( ! refs [ i ]) {
2023-06-09 02:36:11 +02:00
RemoveFromNamelist ( prog -> stringspace , i , entries , offsets , transforms , & endptr , "Removing unused string '%s' from program stringspace" );
2023-06-09 01:52:06 +02:00
}
}
//And finally, update the string pointers of everything else
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
proc = & prog -> procedures . procedures [ i ];
for ( k = 0 ; k < proc -> nodes . numNodes ; k ++ ) {
node = & proc -> nodes . nodes [ k ];
2024-06-09 12:01:31 +02:00
if ( stringData = GetStringOffsetRef ( node , prog )) {
2023-06-09 16:56:51 +02:00
ShiftName ( stringData , entries , transforms , offsets );
2023-06-09 01:52:06 +02:00
}
}
2023-06-09 16:56:51 +02:00
for ( k = 0 ; k < proc -> variables . numVariables ; k ++ ) {
ShiftVariableDefaultValue ( & proc -> variables . variables [ k ], entries , transforms , offsets );
}
}
for ( i = 0 ; i < prog -> externals . numVariables ; i ++ ) {
ShiftVariableDefaultValue ( & prog -> externals . variables [ i ], entries , transforms , offsets );
}
for ( i = 0 ; i < prog -> variables . numVariables ; i ++ ) {
ShiftVariableDefaultValue ( & prog -> variables . variables [ i ], entries , transforms , offsets );
2023-06-09 01:52:06 +02:00
}
2024-06-09 12:01:31 +02:00
//Handle stringified names
for ( i = 0 ; i < prog -> procedures . numProcedures ; i ++ ) {
ShiftName ( & prog -> procedures . procedures [ i ]. stringifiedName , entries , transforms , offsets );
}
2023-06-09 01:52:06 +02:00
2023-06-09 02:36:11 +02:00
FreeNamelistData ( refs , offsets , transforms );
2023-06-09 01:52:06 +02:00
}
2019-11-14 11:57:39 +08:00
void optimizeTree ( Program * prog ) {
2019-11-14 12:14:46 +08:00
int i , matched = 1 ;
currprogram = prog ;
if ( optimize == 1 )
2019-11-14 11:57:39 +08:00
{
DecendUnusedProcedures ( prog );
EliminateUnreferencedGlobals ( prog );
}
else
{
2019-11-14 12:14:46 +08:00
while ( matched ) {
matched = 0 ;
2019-11-14 11:57:39 +08:00
DecendUnusedProcedures ( prog );
2019-11-14 12:14:46 +08:00
for ( i = 1 ; i < prog -> procedures . numProcedures ; i ++ ) {
2023-07-11 22:25:00 +02:00
Procedure * proc = & prog -> procedures . procedures [ i ];
if (( proc -> type & P_IMPORT ) || proc -> deftype != 2 ) continue ;
OptimizeProcedure ( proc );
2019-11-14 11:57:39 +08:00
}
DecendUnusedProcedures ( prog );
2019-11-14 12:14:46 +08:00
matched |= ConstantPropagateGlobals ( prog );
2019-11-14 11:57:39 +08:00
EliminateUnreferencedGlobals ( prog );
}
}
CompressNamelist ( prog );
2023-06-09 01:52:06 +02:00
CompressStringspace ( prog );
2019-11-14 11:57:39 +08:00
}