Files
sslc/optimize.c
T

1309 lines
45 KiB
C
Raw Normal View History

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>
#define Protect(a) if (_stricmp(c, a) == 0) return 1;
2019-11-14 11:57:39 +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;
#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) {
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) {
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) {
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
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) {
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;
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) {
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++;
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:
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:
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:
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
}
}
} 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
}
if (node->token != T_ASSIGN) {
MarkVariableRead(usage, var, currstatement, whiledepth);
2019-11-14 11:57:39 +08:00
}
}
break;
case T_START_EXPRESSION:
expressiondepth = 1;
while (expressiondepth) {
2019-11-14 11:57:39 +08:00
i++;
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:
if ((var = LookupVariable(node)) != -1) {
MarkVariableRead(usage, var, currstatement, whiledepth);
2019-11-14 11:57:39 +08:00
}
}
}
break;
case T_CALL:
if ((node + 1)->token == T_SYMBOL && ((var = LookupVariable(node + 1)) != -1)) {
MarkVariableRead(usage, var, currstatement, whiledepth);
}
break;
2019-11-14 11:57:39 +08:00
}
}
assert(node->token == T_END);
2019-11-14 11:57:39 +08:00
}
static int isValidBinaryOp(int op) {
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));
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('/', /)
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:
out->type = V_INT;
out->intData = (fd1 != 0.0) && (fd2 != 0.0);
2019-11-14 11:57:39 +08:00
break;
case T_OR:
out->type = V_INT;
out->intData = (fd1 != 0.0) || (fd2 != 0.0);
2019-11-14 11:57:39 +08:00
break;
case T_BWAND:
out->type = V_INT;
2019-11-14 11:57:39 +08:00
parseWarningAtNode(node, "Optimizer encountered a bitwise operation on a float constant");
out->intData = in1->intData & in2->intData;
2019-11-14 11:57:39 +08:00
break;
case T_BWOR:
out->type = V_INT;
2019-11-14 11:57:39 +08:00
parseWarningAtNode(node, "Optimizer encountered a bitwise operation on a float constant");
out->intData = in1->intData | in2->intData;
2019-11-14 11:57:39 +08:00
break;
case T_BWXOR:
out->type = V_INT;
2019-11-14 11:57:39 +08:00
parseWarningAtNode(node, "Optimizer encountered a bitwise operation on a float constant");
out->intData = in1->intData ^ in2->intData;
2019-11-14 11:57:39 +08:00
break;
}
} else {
out->type = V_INT;
switch (op) {
2019-11-14 11:57:39 +08:00
I_OP('+', +)
I_OP('-', -)
I_OP('*', *)
I_OP('/', /)
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;
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");
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;
RemoveNodes(_nodes, i, 2);
2019-11-14 11:57:39 +08:00
}/* AND/OR were changed in the tree
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
}
if (found) {
2019-11-14 11:57:39 +08:00
parseMessageAtNode(&nodes[i], "Folding semi-constant binary expression");
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;
}
}
}
}*/
} 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");
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 {
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
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
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) {
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;
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);
matched = 1;
2019-11-14 11:57:39 +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) {
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) {
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 {
blockbegin = T_START_STATEMENT;
blockend = T_END_STATEMENT;
2019-11-14 11:57:39 +08:00
}
2019-11-14 11:57:39 +08:00
i++;
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
}
PerformConstOp(&values[var], &nodes[i + 2].value, &values[var], token, &nodes[i]);
2019-11-14 11:57:39 +08:00
}
} else {
values[var].type = -1;
2019-11-14 11:57:39 +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++;
matched |= ConstantPropagateExpression(nodes, vars, values, varCount, &i);
2019-11-14 11:57:39 +08:00
i++;
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);
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);
}
for (j = 0; j < varCount; j++) {
if (aif[j]) values[j].type = -1;
2019-11-14 11:57:39 +08:00
}
free(aif);
if (aelse) free(aelse);
2019-11-14 11:57:39 +08:00
free(backup);
} 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
Node* nodes2 = &nodes[i];
2019-11-14 11:57:39 +08:00
int* invalidate;
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++;
}
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;
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);
}
#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) {
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) {
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:
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;
}
}
assert(nodes[start].token == T_END_STATEMENT);
2019-11-14 11:57:39 +08:00
}
static void EatDeadStore(NodeList* _nodes, int start, int var) {
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 {
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) {
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);
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");
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);
matched1 = 1;
2019-11-14 11:57:39 +08:00
break;
} 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
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);
matched1 = 1;
2019-11-14 11:57:39 +08:00
break;
}
}
}
}
if (matched1) {
matched2 = 1;
2019-11-14 11:57:39 +08:00
continue;
}
break;
};
return matched2;
}
static int DeadCodeRemoval(NodeList* _nodes) {
int i, statementdepth, takebranch, matched = 0;
Node* nodes = _nodes->nodes;
2019-11-14 11:57:39 +08:00
//First look for constant if branches
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) {
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
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)");
assert(nodes[i].token == T_BEGIN);
2019-11-14 11:57:39 +08:00
SkipStatement;
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
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);
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;
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
i = tmp - 4;
2019-11-14 11:57:39 +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
i = back;
2019-11-14 11:57:39 +08:00
}
}
}
//Next strip out anything following returns, breaks and continue
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;
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
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--;
}
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);
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");
}
i = back;
2019-11-14 11:57:39 +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);
matched = 1;
2019-11-14 11:57:39 +08:00
}
}
return matched;
}
static int CanCombine(VarUsage *u1, VarUsage *u2) {
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);
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);
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;
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++) {
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
}
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);
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) {
int i, depth, j, k, matched = 0;
2019-11-14 11:57:39 +08:00
char op;
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
}
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--;
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
}
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");
matched = 1;
2019-11-14 11:57:39 +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++;
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
}
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;
int hasVars = proc->variables.variables != 0;
2019-11-14 11:57:39 +08:00
Value *values;
VarUsage *usage;
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 {
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
}
found |= DeadStoreRemoval(&proc->nodes, &proc->variables, usage);
2019-11-14 11:57:39 +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
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
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");
//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;
}*/
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--;
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--;
}
}
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) {
int i, j;
2019-11-14 11:57:39 +08:00
Node* node;
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
}
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) {
int i, matched = 1;
while (matched) {
matched = 0;
2019-11-14 11:57:39 +08:00
UpdateProcedureReferences(prog->procedures.procedures, prog->procedures.numProcedures);
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--);
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) {
int i, j, matched = 1;
2019-11-14 11:57:39 +08:00
Node* node;
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
}
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)) {
if (!procs[node->value.intData].uses) {
matched = 1;
procs[node->value.intData].uses = 1;
2019-11-14 11:57:39 +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)) {
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);
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) {
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
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
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
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);
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) {
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--;
}
}
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) {
int i, j, var;
2019-11-14 11:57:39 +08:00
Node* node;
//count variable references
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
}
}
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
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);
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
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);
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);
}
}
}
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
while (*(unsigned short*)list != 0xffff) {
2019-11-14 11:57:39 +08:00
entries++;
list += *(unsigned short*)list + 2;
2019-11-14 11:57:39 +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
list = namelist + 4;
for (i = 0; i < entries; i++) {
offsets[i] = 2 + (unsigned int)list - (unsigned int)namelist;
list += *(unsigned short*)list + 2;
2019-11-14 11:57:39 +08:00
}
memcpy(transforms, offsets, entries * 4);
*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) {
int j;
parseMessageAtNode(0, logFormat, namelist + offsets[i]);
char* namestart = namelist + offsets[i] - 2;
int len = *(unsigned short*)namestart + 2;
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;
}
static void CompressNamelist(Program *prog) {
char* endptr, *namestart;
int entries = 0, *refs, *offsets, *transforms, i, j;
Procedure* proc;
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
for (i = 0; i < prog->externals.numVariables; i++) {
for (j = 0; j < entries; j++) {
if (prog->externals.variables[i].name == offsets[j]) refs[j] |= 1;
2019-11-14 11:57:39 +08:00
}
}
for (i = 0; i < prog->variables.numVariables; i++) {
for (j = 0; j < entries; j++) {
if (prog->variables.variables[i].name == offsets[j]) refs[j] |= 2;
2019-11-14 11:57:39 +08:00
}
}
for (i = 0; i < prog->procedures.numProcedures; i++) {
2019-11-14 11:57:39 +08:00
proc = &prog->procedures.procedures[i];
for (j = 0; j < entries; j++) {
if (proc->name == offsets[j]) refs[j] |= 4;
2019-11-14 11:57:39 +08:00
}
}
//For each string that isn't referenced, remove it
for (i = entries - 1; i >= 0; i--) {
if (!refs[i]) {
RemoveFromNamelist(prog->namelist, i, entries, offsets, transforms, &endptr, "Removing unused string '%s' from program namespace");
} else if (refs[i] == 2 && optimize>=3) {
namestart = prog->namelist + offsets[i] - 2;
int len = *(unsigned short*)namestart + 2;
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]);
*(unsigned short*)(namestart) = 2;
*(char*)(prog->namelist + offsets[i] + 0) = 'a';
*(char*)(prog->namelist + offsets[i] + 1) = 0;
(*(unsigned int*)prog->namelist) -= len - 4;
memmove(prog->namelist + offsets[i] + 2, len + namestart, endptr - (len + namestart));
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
for (i = 0; i < prog->externals.numVariables; i++) {
for (j = 0; j < entries; j++) {
if (prog->externals.variables[i].name == offsets[j]) {
assert(transforms[j] != 0x7fffffff);
prog->externals.variables[i].name = transforms[j];
2019-11-14 11:57:39 +08:00
break;
}
}
}
for (i = 0; i < prog->variables.numVariables; i++) {
for (j = 0; j < entries; j++) {
if (prog->variables.variables[i].name == offsets[j]) {
assert(transforms[j] != 0x7fffffff);
prog->variables.variables[i].name = transforms[j];
2019-11-14 11:57:39 +08:00
break;
}
}
}
for (i = 0; i < prog->procedures.numProcedures; i++) {
2019-11-14 11:57:39 +08:00
proc = &prog->procedures.procedures[i];
for (j = 0; j < entries; j++) {
if (proc->name == offsets[j]) {
assert(transforms[j] != 0x7fffffff);
proc->name = transforms[j];
2019-11-14 11:57:39 +08:00
break;
}
}
}
FreeNamelistData(refs, offsets, transforms);
2019-11-14 11:57:39 +08:00
}
static int* GetStringReference(Node *node, Program *prog) {
if (node->token == T_CONSTANT && node->value.type == V_STRING) {
return &node->value.stringData;
}
else if (node->token == T_SYMBOL && node->value.type & P_STRINGIFY) {
return &prog->procedures.procedures[node->value.intData].stringifiedName;
}
return 0;
}
2023-06-09 01:52:06 +02:00
static void CompressStringspace(Program *prog) {
2023-06-09 10:55:54 +02:00
if (!prog->stringspace) return;
char* endptr;
int entries = 0, *refs, *offsets, *transforms, i, j, k, *stringData;
2023-06-09 01:52:06 +02:00
Procedure* proc;
Node* node;
GetNamelistData(prog->stringspace, &entries, &endptr, &refs, &offsets, &transforms);
2023-06-09 01:52:06 +02:00
//Then check all procedures nodes to see where in the stringspace they point
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];
if (stringData = GetStringReference(node, prog)) {
2023-06-09 01:52:06 +02:00
for (j = 0; j < entries; j++) {
if (*stringData == offsets[j]) refs[j] = 1;
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]) {
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];
if (stringData = GetStringReference(node, prog)) {
2023-06-09 01:52:06 +02:00
for (j = 0; j < entries; j++) {
if (*stringData == offsets[j]) {
2023-06-09 01:52:06 +02:00
assert(transforms[j] != 0x7fffffff);
*stringData = transforms[j];
2023-06-09 01:52:06 +02:00
break;
}
}
}
}
}
FreeNamelistData(refs, offsets, transforms);
2023-06-09 01:52:06 +02:00
}
2019-11-14 11:57:39 +08:00
void optimizeTree(Program *prog) {
int i, matched = 1;
currprogram = prog;
if (optimize == 1)
2019-11-14 11:57:39 +08:00
{
DecendUnusedProcedures(prog);
EliminateUnreferencedGlobals(prog);
}
else
{
while (matched) {
matched = 0;
2019-11-14 11:57:39 +08:00
DecendUnusedProcedures(prog);
for (i = 1; i <prog->procedures.numProcedures; i++) {
if (prog->procedures.procedures[i].type & P_IMPORT) continue;
2019-11-14 11:57:39 +08:00
OptimizeProcedure(&prog->procedures.procedures[i]);
}
DecendUnusedProcedures(prog);
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
}