MWCC/compiler_and_linker/unsorted/COptimizer.c

1832 lines
51 KiB
C

#include "compiler/COptimizer.h"
#include "compiler/CompilerTools.h"
#include "compiler/CClass.h"
#include "compiler/CDecl.h"
#include "compiler/CError.h"
#include "compiler/CExpr.h"
#include "compiler/CFunc.h"
#include "compiler/CInt64.h"
#include "compiler/CMachine.h"
#include "compiler/CParser.h"
#include "compiler/InlineAsm.h"
#include "compiler/enode.h"
#include "compiler/objects.h"
#include "compiler/CodeGen.h"
#include "compiler/Switch.h"
#include "compiler/Exceptions.h"
#include "compiler/IrOptimizer.h"
#include "cos.h"
COptBlock *basicblocks;
Boolean copt_isleaffunction;
static Boolean stmtchanged;
static COptBlock *currentblock;
static ENode *mexpr;
static short setbytes;
static COptCSE *csenodes[MAXEXPR];
static COptCSEList *cselist;
static short extravars;
static Boolean cse_found;
static Boolean cse_invals;
static Boolean static_for_inlines;
static short replaces;
static ENode *objrefnode;
static int objrefnodes;
static short bitmasks[] = {
1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000
};
// forward decls
static COptCSE *cse_expression(ENode *expr);
static COptCSE *cse_new(ENode *expr) {
COptCSE *cse = oalloc(sizeof(COptCSE));
cse->expr = expr;
cse->replaced = NULL;
cse->block = currentblock;
cse->mexpr = mexpr;
cse->left = NULL;
cse->right = NULL;
cse->x1C = 1;
return cse;
}
static COptCSE *cse_append(COptCSE *cse, ENode *expr) {
COptCSEList *list = oalloc(sizeof(COptCSEList));
list->next = cselist;
cselist = list;
list->cse = cse;
list->expr = expr;
return cse;
}
static void cse_cleanup(void) {
COptCSEList *scanlist;
COptCSEList *prevlist;
COptCSE *scan;
COptCSE *prev;
short op;
scanlist = cselist;
while (scanlist && !scanlist->cse)
scanlist = scanlist->next;
cselist = scanlist;
if (scanlist) {
do {
prevlist = scanlist;
do {
scanlist = scanlist->next;
} while (scanlist && !scanlist->cse);
prevlist->next = scanlist;
} while (scanlist);
}
for (op = 0; op < MAXEXPR; op++) {
scan = csenodes[op];
while (scan && scan->x1C < 0)
scan = scan->next;
csenodes[op] = scan;
if (scan) {
do {
prev = scan;
do {
scan = scan->next;
} while (scan && scan->x1C < 0);
prev->next = scan;
} while (scan);
}
}
}
static void cse_inval(COptCSE *cse) {
COptCSEList *scanlist;
COptCSE *scan;
short op;
if (cse) {
for (scanlist = cselist; scanlist; scanlist = scanlist->next) {
if (scanlist->cse == cse) {
scanlist->cse = NULL;
scanlist->expr = NULL;
}
}
cse->x1C = -1;
cse->left = NULL;
cse->right = NULL;
for (op = 0; op < MAXEXPR; op++) {
for (scan = csenodes[op]; scan; scan = scan->next) {
if (scan->left == cse || scan->right == cse)
cse_inval(scan);
}
}
}
}
static void cse_update_usages(COptCSE *cse, short amount) {
cse->x1C /= amount;
if (cse->left)
cse_update_usages(cse->left, amount);
if (cse->right)
cse_update_usages(cse->right, amount);
}
static void cse_replace(ENode *expr, COptCSE *cse) {
COptCSEList *list;
for (list = cselist; list; list = list->next) {
if (list->cse == cse) {
*list->expr = *expr;
replaces++;
list->cse = NULL;
list->expr = NULL;
}
}
}
static short cse_objectcost(Object *obj) {
if (obj->datatype == DLOCAL && !obj->u.var.info->noregister)
return 0;
return 1;
}
static void cse_treereplacemexpr(COptCSE *cse, ENode *from, ENode *to) {
if (cse->mexpr == from)
cse->mexpr = to;
if (cse->left)
cse_treereplacemexpr(cse->left, from, to);
if (cse->right)
cse_treereplacemexpr(cse->right, from, to);
}
static Boolean cse_issubcse(COptCSE *a, COptCSE *b) {
if (a == b)
return 1;
if (b->left && cse_issubcse(a, b->left))
return 1;
if (b->right && cse_issubcse(a, b->right))
return 1;
return 0;
}
static short cse_cost(COptCSE *cse) {
short cost;
if (cse) {
while (ENODE_IS(cse->expr, ETYPCON) && cse->expr->rtype->type == cse->expr->data.monadic->rtype->type && cse->expr->rtype->size == cse->expr->data.monadic->rtype->size)
cse = cse->left;
if (ENODE_IS_INDIRECT_TO(cse->expr, EOBJREF))
return cse_objectcost(cse->expr->data.monadic->data.objref);
cost = 1;
if (!copts.optimize_for_size) {
if (ENODE_IS3(cse->expr, EMUL, EDIV, EMODULO))
cost = 2;
}
return cse_cost(cse->left) + cse_cost(cse->right) + cost;
}
return 0;
}
static void cse_remove(void) {
short op; // r27
COptCSE *cse; // r25
COptCSE *best_cse; // r28
int best_cost; // r30
Object *obj; // r31
Boolean did_replacement; // r24
VarInfo *vi; // r27
ObjectList *objlist;
ENode *expr1;
ENode *expr2;
ENode *expr3;
ENode *expr4;
ENode *mexprsave;
COptCSEList *list;
short cost;
while (1) {
op = 0;
best_cse = NULL;
best_cost = 0;
did_replacement = 0;
for (; op < MAXEXPR; op++) {
switch (op) {
case EINTCONST:
case EFLOATCONST:
case EOBJREF:
case EVECTOR128CONST:
break;
default:
for (cse = csenodes[op]; cse; cse = cse->next) {
if (cse->x1C > 1 && (cost = cse_cost(cse)) > 4) {
if (cse->replaced) {
replaces = 0;
cse_replace(cse->replaced, cse);
CError_ASSERT(348, replaces >= 1);
cse_found = 1;
did_replacement = 1;
cse_update_usages(cse, cse->x1C);
} else {
if ((cse->x1C * cost) > best_cost) {
best_cse = cse;
best_cost = cse->x1C * cost;
}
}
}
}
break;
}
}
if (did_replacement)
continue;
if (!best_cse || extravars >= 256)
return;
obj = lalloc(sizeof(Object));
memclrw(obj, sizeof(Object));
obj->name = CParser_GetUniqueName();
obj->type = cse->expr->rtype;
obj->datatype = DLOCAL;
vi = CodeGen_GetNewVarInfo();
obj->u.var.info = vi;
objlist = lalloc(sizeof(ObjectList));
objlist->object = obj;
objlist->next = locals;
locals = objlist;
vi->used = 1;
vi->usage = cse->x1C + 1;
expr1 = lalloc(sizeof(ENode));
expr1->type = EOBJREF;
expr1->cost = 0;
expr1->flags = 0;
expr1->data.objref = obj;
expr1->rtype = CDecl_NewPointerType(obj->type);
expr2 = lalloc(sizeof(ENode));
expr2->type = EINDIRECT;
expr2->cost = 1;
expr2->flags = 0;
expr2->data.monadic = expr1;
expr2->rtype = obj->type;
expr3 = lalloc(sizeof(ENode));
expr3->type = EASS;
expr3->cost = -1;
expr3->flags = 0;
expr3->rtype = obj->type;
expr3->data.diadic.left = expr2;
expr3->data.diadic.right = lalloc(sizeof(ENode));
*expr3->data.diadic.right = *cse->expr;
cse->expr = expr3->data.diadic.right;
replaces = 0;
cse_replace(expr2, cse);
cse->replaced = expr2;
if (replaces < 2)
CError_FATAL(390);
else
cse_found = 1;
expr4 = lalloc(sizeof(ENode));
*expr4 = *cse->mexpr;
cse->mexpr->type = ECOMMA;
cse->mexpr->data.diadic.left = expr3;
cse->mexpr->data.diadic.right = expr4;
cse_update_usages(cse, cse->x1C);
extravars++;
mexprsave = cse->mexpr;
if (mexpr == mexprsave)
mexpr = expr4;
for (list = cselist; list; list = list->next) {
if (list->cse && !cse_issubcse(list->cse, cse)) {
if (list->cse->mexpr == mexprsave)
list->cse->mexpr = expr4;
if (list->cse->expr == mexprsave)
list->cse->expr = expr4;
if (list->expr == mexprsave)
list->expr = expr4;
}
}
}
}
static COptCSE *cse_intconst(ENode *expr) {
COptCSE *cse = csenodes[EINTCONST];
for (; cse; cse = cse->next) {
if (expr->rtype == cse->expr->rtype)
if (CInt64_Equal(cse->expr->data.intval, expr->data.intval))
return cse;
}
cse = cse_new(expr);
cse->next = csenodes[EINTCONST];
csenodes[EINTCONST] = cse;
return cse;
}
static COptCSE *cse_floatconst(ENode *expr) {
COptCSE *cse = csenodes[EFLOATCONST];
Float val = expr->data.floatval;
for (; cse; cse = cse->next) {
if (CMach_CalcFloatDiadicBool(cse->expr->rtype, cse->expr->data.floatval, TK_LOGICAL_EQ, val))
if (expr->rtype == cse->expr->rtype)
return cse;
}
cse = cse_new(expr);
cse->next = csenodes[EFLOATCONST];
csenodes[EFLOATCONST] = cse;
return cse;
}
static COptCSE *cse_vectorconst(ENode *expr) {
COptCSE *cse = csenodes[EVECTOR128CONST];
MWVector128 val = expr->data.vector128val;
for (; cse; cse = cse->next) {
if (CMach_CalcVectorDiadicBool(cse->expr->rtype, &cse->expr->data.vector128val, TK_LOGICAL_EQ, &val))
if (expr->rtype == cse->expr->rtype)
return cse;
}
cse = cse_new(expr);
cse->next = csenodes[EVECTOR128CONST];
csenodes[EVECTOR128CONST] = cse;
return cse;
}
static COptCSE *cse_objref(ENode *expr) {
COptCSE *cse = csenodes[EOBJREF];
Object *obj = expr->data.objref;
for (; cse; cse = cse->next) {
if (cse->expr->data.objref == obj)
return cse;
}
cse = cse_new(expr);
cse->next = csenodes[EOBJREF];
csenodes[EOBJREF] = cse;
return cse;
}
static COptCSE *cse_add_monadic_node(ENode *outer, COptCSE *innercse) {
COptCSE *cse;
if (!innercse)
return NULL;
for (cse = csenodes[outer->type]; cse; cse = cse->next) {
if (cse->left == innercse && cse->expr->rtype == outer->rtype) {
CError_ASSERT(524, cse->expr != outer);
cse->x1C++;
return cse;
}
}
cse = cse_new(outer);
cse->next = csenodes[outer->type];
csenodes[outer->type] = cse;
cse->left = innercse;
return cse;
}
static COptCSE *cse_add_typecon_node(ENode *outer, COptCSE *innercse) {
COptCSE *cse;
if (!innercse)
return NULL;
for (cse = csenodes[outer->type]; cse; cse = cse->next) {
if (cse->left == innercse && cse->expr->rtype == outer->rtype) {
CError_ASSERT(552, cse->expr != outer);
cse->x1C++;
return cse;
}
}
cse = cse_new(outer);
cse->next = csenodes[outer->type];
csenodes[outer->type] = cse;
cse->left = innercse;
return cse;
}
static COptCSE *cse_add_diadic_node(ENode *outer, COptCSE *leftcse, COptCSE *rightcse) {
COptCSE *cse;
if (!leftcse || !rightcse)
return NULL;
for (cse = csenodes[outer->type]; cse; cse = cse->next) {
if (cse->left == leftcse && cse->right == rightcse) {
CError_ASSERT(581, cse->expr != outer);
cse->x1C++;
return cse;
}
}
cse = cse_new(outer);
cse->next = csenodes[outer->type];
csenodes[outer->type] = cse;
cse->left = leftcse;
cse->right = rightcse;
return cse;
}
static COptCSE *cse_add_commdiadic_node(ENode *outer, COptCSE *leftcse, COptCSE *rightcse) {
COptCSE *cse;
if (!leftcse || !rightcse)
return NULL;
for (cse = csenodes[outer->type]; cse; cse = cse->next) {
if ((cse->left == leftcse && cse->right == rightcse) || (cse->left == rightcse && cse->right == leftcse)) {
CError_ASSERT(612, cse->expr != outer);
cse->x1C++;
return cse;
}
}
cse = cse_new(outer);
cse->next = csenodes[outer->type];
csenodes[outer->type] = cse;
cse->left = leftcse;
cse->right = rightcse;
return cse;
}
static void sfind_objref(ENode *expr) {
while (1) {
switch (expr->type) {
case ETYPCON:
expr = expr->data.monadic;
break;
case EOBJREF:
objrefnode = expr;
objrefnodes++;
return;
case EADD:
case ESUB:
sfind_objref(expr->data.diadic.left);
expr = expr->data.diadic.right;
break;
default:
return;
}
}
}
static ENode *find_objref_node(ENode *expr) {
objrefnode = NULL;
objrefnodes = 0;
sfind_objref(expr);
if (objrefnodes == 1)
return objrefnode;
else
return NULL;
}
static void cse_mem_modify(void) {
COptCSE *cse;
for (cse = csenodes[EINDIRECT]; cse; cse = cse->next) {
if (ENODE_IS(cse->expr->data.monadic, EOBJREF)) {
Object *obj = cse->expr->data.monadic->data.objref;
CError_ASSERT(672, obj->datatype != DALIAS);
if (obj->datatype == DLOCAL && !obj->u.var.info->noregister)
continue;
}
cse_inval(cse);
}
}
static void cse_modify_expression(ENode *expr) {
if (!expr) {
short op;
cse_remove();
for (op = 0; op < MAXEXPR; op++)
csenodes[op] = NULL;
cselist = NULL;
freeoheap();
} else {
ENode *objnode;
Object *obj;
COptCSE *cse;
cse_invals = 1;
if (ENODE_IS(expr, EINDIRECT) && (objnode = find_objref_node(expr->data.monadic))) {
do {
obj = objnode->data.objref;
cse_remove();
for (cse = csenodes[EINDIRECT]; cse; cse = cse->next) {
if ((objnode = find_objref_node(cse->expr->data.monadic)) && obj == objnode->data.objref)
cse_inval(cse);
}
objnode = find_objref_node(expr->data.monadic);
if (!objnode) {
cse_mem_modify();
break;
}
} while (obj != objnode->data.objref);
} else {
cse_remove();
cse_mem_modify();
}
}
}
static void cse_pascalcall(ENode *expr) {
ENodeList *list;
for (list = expr->data.funccall.args; list; list = list->next)
cse_expression(list->node);
if (ENODE_IS(expr, EFUNCCALLP))
cse_expression(expr->data.funccall.funcref);
cse_remove();
cse_mem_modify();
}
static Boolean cse_pusharg(ENodeList *exprs, FuncArg *args) {
Boolean result;
if (!exprs)
return 1;
if (args && args != &elipsis && args != &oldstyle)
args = args->next;
if (exprs->next)
result = cse_pusharg(exprs->next, args);
cse_expression(exprs->node);
return result;
}
static void cse_ccall(ENode *expr) {
if (cse_pusharg(expr->data.funccall.args, expr->data.funccall.functype->args)) {
if (ENODE_IS(expr, EFUNCCALL))
cse_expression(expr->data.funccall.funcref);
cse_remove();
cse_mem_modify();
} else {
cse_modify_expression(NULL);
}
}
static COptCSE *cse_expression(ENode *expr) {
ENode *save;
COptCSE *tmp;
ENodeType nt = expr->type;
switch (nt) {
case EFUNCCALL:
cse_ccall(expr);
return NULL;
case EFUNCCALLP:
cse_pascalcall(expr);
return NULL;
case EPOSTINC:
case EPOSTDEC:
case EPREINC:
case EPREDEC:
save = mexpr;
mexpr = expr;
CError_ASSERT(816, ENODE_IS(expr->data.monadic, EINDIRECT));
cse_expression(expr->data.monadic->data.monadic);
cse_modify_expression(expr->data.monadic);
mexpr = save;
return NULL;
case EINDIRECT:
case EMONMIN:
case EBINNOT:
case ELOGNOT:
case EFORCELOAD:
return cse_append(cse_add_monadic_node(expr, cse_expression(expr->data.monadic)), expr);
case ETYPCON:
return cse_append(cse_add_typecon_node(expr, cse_expression(expr->data.monadic)), expr);
case EMUL:
case EMULV:
case EADDV:
case EADD:
case EEQU:
case ENOTEQU:
case EAND:
case EXOR:
case EOR:
tmp = cse_expression(expr->data.diadic.left);
if (expr->type == nt)
return cse_append(cse_add_commdiadic_node(expr, tmp, cse_expression(expr->data.diadic.right)), expr);
else
return NULL;
case EDIV:
case EMODULO:
case ESUBV:
case ESUB:
case ESHL:
case ESHR:
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EPMODULO:
case EROTL:
case EROTR:
case EBTST:
tmp = cse_expression(expr->data.diadic.left);
if (expr->type == nt)
return cse_append(cse_add_diadic_node(expr, tmp, cse_expression(expr->data.diadic.right)), expr);
else
return NULL;
case EASS:
case EMULASS:
case EDIVASS:
case EMODASS:
case EADDASS:
case ESUBASS:
case ESHLASS:
case ESHRASS:
case EANDASS:
case EXORASS:
case EORASS:
CError_ASSERT(887, ENODE_IS(expr->data.diadic.left, EINDIRECT));
save = mexpr;
mexpr = expr;
cse_expression(expr->data.diadic.right);
if (expr->type == nt) {
cse_expression(expr->data.diadic.left->data.monadic);
cse_modify_expression(expr->data.diadic.left);
mexpr = save;
return NULL;
} else {
cse_modify_expression(NULL);
return NULL;
}
case ECOMMA:
cse_expression(expr->data.diadic.left);
save = mexpr;
mexpr = expr->data.diadic.right;
tmp = cse_expression(expr->data.diadic.right);
mexpr = save;
return tmp;
case EINTCONST:
return cse_intconst(expr);
case EFLOATCONST:
return cse_floatconst(expr);
case EVECTOR128CONST:
return cse_vectorconst(expr);
case EOBJREF:
return cse_objref(expr);
case EBITFIELD:
cse_modify_expression(NULL);
return NULL;
case ECOND:
cse_expression(expr->data.cond.cond);
cse_modify_expression(NULL);
return NULL;
case ENULLCHECK:
cse_expression(expr->data.nullcheck.nullcheckexpr);
cse_modify_expression(NULL);
return NULL;
case ELAND:
case ELOR:
cse_expression(expr->data.diadic.left);
cse_modify_expression(NULL);
return NULL;
case ESTRINGCONST:
case ELABEL:
return NULL;
case EPRECOMP:
CError_FATAL(948);
default:
CError_FATAL(951);
return NULL;
}
}
static void cse_block(COptBlock *block) {
Statement *stmt;
COptBlock *check;
COptBlockList *scan;
COptBlock *best;
short i;
SInt32 counter;
UInt32 starttime;
starttime = COS_GetTicks();
counter = 0;
while (1) {
cse_invals = 0;
currentblock = block;
block->x1E = 1;
for (stmt = block->stmt, i = block->x1C; i > 0; stmt = stmt->next, i--) {
switch (stmt->type) {
case ST_EXPRESSION:
case ST_SWITCH:
case ST_IFGOTO:
case ST_IFNGOTO:
mexpr = stmt->expr;
cse_expression(mexpr);
break;
case ST_RETURN:
mexpr = stmt->expr;
if (mexpr)
cse_expression(mexpr);
break;
}
}
i = 0;
for (scan = block->blocks2; scan; scan = scan->next) {
check = scan->block;
if (!check->x1E && check->blocks && !check->blocks->next && check->blocks->block == block) {
best = check;
i++;
}
}
if (i != 1)
break;
block = best;
if (COS_GetTicks() > (starttime + 60)) {
if (counter++ & 1) {
if (CWDisplayLines(cparamblkptr->context, lines))
CError_UserBreak();
} else {
if (CWDisplayLines(cparamblkptr->context, lines + 1))
CError_UserBreak();
}
starttime = COS_GetTicks();
}
if (cse_invals)
cse_cleanup();
}
cse_remove();
}
static void CSE(void) {
COptBlock *block;
for (block = basicblocks->next; block; block = block->next)
block->x1E = 0;
for (block = basicblocks->next; block; block = block->next) {
if (!block->x1E) {
short op;
for (op = 0; op < MAXEXPR; op++)
csenodes[op] = NULL;
cselist = NULL;
cse_found = 0;
cse_block(block);
freeoheap();
}
}
}
static short TestSetBit(short *set, short bit) {
return set[bit >> 4] & bitmasks[bit & 15];
}
static void SetSetBit(short *set, short bit) {
set[bit >> 4] |= bitmasks[bit & 15];
}
UInt32 RegAllocMask(short var) {
COptBlock *block;
UInt32 mask = 0;
for (block = basicblocks; block; block = block->next) {
if (TestSetBit(block->set1, var) || TestSetBit(block->set2, var))
mask |= block->allocmask;
}
return mask;
}
void MarkRegAllocMask(short var, short bit, Boolean flag) {
COptBlock *block;
UInt32 mask = 1 << bit;
if (flag) {
for (block = basicblocks; block; block = block->next) {
if (TestSetBit(block->set1, var) || TestSetBit(block->set2, var))
block->allocmask |= mask;
}
} else {
for (block = basicblocks; block; block = block->next) {
block->allocmask |= mask;
}
}
}
static COptBlock *newblock(void) {
COptBlock *block;
short i;
short max;
block = lalloc(sizeof(COptBlock) + (setbytes * 2));
block->x1E = 0;
block->next = NULL;
block->blocks = NULL;
block->blocks2 = NULL;
block->allocmask = 0;
block->set1 = (short *) (((long) block) + sizeof(COptBlock));
block->set2 = (short *) (((long) block) + setbytes + sizeof(COptBlock));
for (i = 0, max = setbytes / 2; i < max; i++) {
block->set1[i] = 0;
block->set2[i] = 0;
}
return block;
}
static void MarkFollow(COptBlock *block) {
COptBlockList *list;
restart:
block->x1E = 1;
if ((list = block->blocks2)) {
while (1) {
if (!list->block->x1E) {
if (!list->next) {
block = list->block;
goto restart;
}
MarkFollow(list->block);
list = list->next;
} else {
list = list->next;
if (!list)
break;
}
}
}
}
static Boolean CheckInit(short var) {
Boolean result;
COptBlock *block;
result = 1;
for (block = basicblocks; block; block = block->next) {
if (!block->x1E && TestSetBit(block->set2, var))
MarkFollow(block);
}
for (block = basicblocks; block; block = block->next) {
if (!block->x1E) {
if (TestSetBit(block->set1, var))
result = 0;
} else {
block->x1E = 0;
}
}
return result;
}
static void CheckVarInit(void) {
COptBlock *block;
VarInfo *vi;
ObjectList *list;
for (block = basicblocks; block; block = block->next)
block->x1E = 0;
for (list = locals; list; list = list->next) {
if (list->object->datatype == DLOCAL && !IsTempName(list->object->name) && !is_volatile_object(list->object)) {
vi = list->object->u.var.info;
if (vi->used && !CheckInit(vi->varnumber)) {
if (!IS_TYPE_CLASS(list->object->type) || !CClass_IsEmpty(TYPE_CLASS(list->object->type))) {
CError_SetErrorToken(&vi->deftoken);
CError_Warning(CErrorStr185, list->object->name->name);
}
}
}
}
}
static void AddVar(Object *obj, Boolean whichSet) {
VarInfo *vi;
if (obj->datatype == DLOCAL) {
vi = obj->u.var.info;
if (!whichSet) {
SetSetBit(currentblock->set1, vi->varnumber);
} else {
if (!TestSetBit(currentblock->set1, vi->varnumber))
SetSetBit(currentblock->set2, vi->varnumber);
}
}
}
static void CheckExpr(ENode *expr) {
ENodeList *list;
while (1) {
switch (expr->type) {
case EINDIRECT:
if (ENODE_IS(expr->data.monadic, EOBJREF)) {
AddVar(expr->data.monadic->data.objref, 0);
return;
}
expr = expr->data.monadic;
break;
case EOBJREF:
AddVar(expr->data.objref, 1);
return;
case EFUNCCALL:
case EFUNCCALLP:
CheckExpr(expr->data.funccall.funcref);
for (list = expr->data.funccall.args; list; list = list->next)
CheckExpr(list->node);
return;
case ECOND:
CheckExpr(expr->data.cond.cond);
CheckExpr(expr->data.cond.expr1);
CheckExpr(expr->data.cond.expr2);
return;
case EASS:
if (ENODE_IS_INDIRECT_TO(expr->data.diadic.left, EOBJREF)) {
CheckExpr(expr->data.diadic.right);
AddVar(expr->data.diadic.left->data.monadic->data.objref, 1);
return;
}
case EMUL:
case EMULV:
case EDIV:
case EMODULO:
case EADDV:
case ESUBV:
case EADD:
case ESUB:
case ESHL:
case ESHR:
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
case EAND:
case EXOR:
case EOR:
case ELAND:
case ELOR:
case EMULASS:
case EDIVASS:
case EMODASS:
case EADDASS:
case ESUBASS:
case ESHLASS:
case ESHRASS:
case EANDASS:
case EXORASS:
case EORASS:
case ECOMMA:
case EPMODULO:
case EROTL:
case EROTR:
case EBCLR:
case EBTST:
case EBSET:
CheckExpr(expr->data.diadic.left);
expr = expr->data.diadic.right;
break;
case EPOSTINC:
case EPOSTDEC:
case EPREINC:
case EPREDEC:
case EMONMIN:
case EBINNOT:
case ELOGNOT:
case EFORCELOAD:
case ETYPCON:
case EBITFIELD:
expr = expr->data.monadic;
break;
case ENULLCHECK:
CheckExpr(expr->data.nullcheck.nullcheckexpr);
expr = expr->data.nullcheck.condexpr;
break;
case EINTCONST:
case EFLOATCONST:
case ESTRINGCONST:
case EPRECOMP:
case ELABEL:
case EVECTOR128CONST:
return;
default:
CError_FATAL(1332);
}
}
}
static short CheckPath(COptBlock *block, short var) {
COptBlockList *list;
if (block->x1E)
return TestSetBit(block->set1, var);
block->x1E = 1;
if (TestSetBit(block->set1, var))
return -1;
if (TestSetBit(block->set2, var))
return 0;
for (list = block->blocks2; list; list = list->next) {
if (CheckPath(list->block, var)) {
SetSetBit(block->set1, var);
return -1;
}
}
return 0;
}
static void MarkPre(COptBlock *block, short var) {
COptBlockList *list;
restart:
block->x1E = 1;
for (list = block->blocks; list; list = list->next) {
if (!list->block->x1E) {
if (!TestSetBit(list->block->set2, var)) {
SetSetBit(list->block->set1, var);
if (!list->next) {
block = list->block;
goto restart;
}
MarkPre(list->block, var);
} else {
list->block->x1E = 1;
}
}
}
}
static void CheckVarUsage(void) {
short local;
COptBlock *block;
short counter;
for (currentblock = basicblocks; currentblock; currentblock = currentblock->next) {
Statement *stmt = currentblock->stmt;
if (currentblock->x1C > 0 && stmt->type == ST_LABEL && (stmt->flags & StmtFlag_1)) {
short i;
for (i = 0; i < localcount; i++)
SetSetBit(currentblock->set2, i);
}
for (counter = currentblock->x1C; counter > 0; counter--) {
if (stmt->type >= ST_EXPRESSION && stmt->type <= ST_GOTOEXPR && stmt->expr)
CheckExpr(stmt->expr);
stmt = stmt->next;
}
}
for (local = 0; local < localcount; local++) {
for (block = basicblocks; block; block = block->next) {
if (!block->x1E && TestSetBit(block->set1, local))
MarkPre(block, local);
}
for (block = basicblocks; block; block = block->next) {
block->x1E = 0;
}
}
}
static void SplitCommaExpressions(void) {
COptBlock *block;
Statement *stmt;
ENode *expr;
Statement *stmtcopy;
short counter;
Boolean flag;
block = basicblocks->next;
while (block) {
stmt = block->stmt;
counter = block->x1C;
flag = 1;
while (counter > 0) {
if (stmt->type >= ST_EXPRESSION && stmt->type <= ST_GOTOEXPR) {
if ((expr = stmt->expr) && ENODE_IS(expr, ECOMMA)) {
stmtcopy = lalloc(sizeof(Statement));
*stmtcopy = *stmt;
stmt->next = stmtcopy;
stmt->type = ST_EXPRESSION;
stmt->expr = expr->data.diadic.left;
stmtcopy->expr = expr->data.diadic.right;
block->x1C++;
flag = 0;
break;
}
}
stmt = stmt->next;
counter--;
}
if (flag)
block = block->next;
}
}
static void BasicBlockAnalyze(Statement *stmt) {
COptBlock *block;
ObjectList *objlist;
COptBlockList *scan;
COptBlockList *list;
CLabel *label;
COptBlock *iblock;
COptBlock *oblock;
SwitchCase *swcase;
setbytes = 2 * ((localcount - 1) / 16) + 2;
if (copts.global_optimizer) {
setbytes += 32;
extravars = 0;
}
block = newblock();
basicblocks = block;
block->stmt = NULL;
block->x1C = 0;
if (stmt) {
list = lalloc(sizeof(COptBlockList));
list->next = NULL;
block->blocks2 = list;
list->block = (COptBlock *) stmt;
}
for (objlist = arguments; objlist; objlist = objlist->next) {
SetSetBit(block->set2, objlist->object->u.var.info->varnumber);
}
while (stmt) {
int counter;
block->next = newblock();
block = block->next;
block->stmt = stmt;
counter = 1;
innerLoop:
switch (stmt->type) {
case ST_ASM:
label = InlineAsm_GetReferencedLabel(stmt);
if (!label)
goto jumpahead;
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) label->stmt;
if (stmt->next) {
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) stmt->next;
}
stmt = stmt->next;
block->x1C = counter;
continue;
case ST_NOP:
case ST_GOTO:
case ST_ENTRY:
if (stmt->next && stmt->next->type != ST_LABEL) {
stmt = stmt->next;
counter++;
goto innerLoop;
}
break;
case ST_EXPRESSION:
case ST_BEGINCATCH:
case ST_ENDCATCH:
case ST_ENDCATCHDTOR:
jumpahead:
if (stmt->next && stmt->next->type == ST_GOTO) {
stmt = stmt->next;
counter++;
goto innerLoop;
}
break;
}
switch (stmt->type) {
case ST_SWITCH:
label = ((SwitchInfo *) stmt->label)->defaultlabel;
if (label != cleanreturnlabel) {
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) label->stmt;
}
for (swcase = ((SwitchInfo *) stmt->label)->cases; swcase; swcase = swcase->next) {
label = swcase->label;
if (label != cleanreturnlabel) {
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) label->stmt;
}
}
break;
case ST_RETURN:
case ST_EXIT:
case ST_GOTOEXPR:
break;
case ST_GOTO:
case ST_IFGOTO:
case ST_IFNGOTO:
case ST_OVF:
label = stmt->label;
if (label != cleanreturnlabel) {
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) label->stmt;
}
if (stmt->type == ST_GOTO)
break;
default:
if (stmt->next) {
list = lalloc(sizeof(COptBlockList));
list->next = block->blocks2;
block->blocks2 = list;
list->block = (COptBlock *) stmt->next;
}
break;
}
stmt = stmt->next;
block->x1C = counter;
}
for (oblock = basicblocks; oblock; oblock = oblock->next) {
for (scan = oblock->blocks2; scan; scan = scan->next) {
stmt = (Statement *) scan->block;
for (iblock = basicblocks->next; iblock; iblock = iblock->next) {
if (iblock->stmt == stmt) {
scan->block = iblock;
list = lalloc(sizeof(COptBlockList));
list->next = iblock->blocks;
iblock->blocks = list;
list->block = oblock;
break;
}
}
CError_ASSERT(1602, iblock);
}
}
SplitCommaExpressions();
CheckVarUsage();
}
static CLabel *finallabel(CLabel *label, Statement *stmt) {
Statement *scan;
for (scan = label->stmt; scan; scan = scan->next) {
if (scan->type > ST_LABEL) {
if (scan == stmt)
return label;
if (scan->type == ST_GOTO) {
if (scan->label != label)
stmtchanged = 1;
return scan->label;
}
return label;
}
}
return label;
}
static void optimizegoto(Statement *stmt) {
Statement *scan;
for (scan = stmt->next; scan; scan = scan->next) {
if (stmt->label->stmt == scan) {
stmt->type = ST_NOP;
stmtchanged = 1;
return;
}
if (scan->type > ST_LABEL)
break;
}
stmt->label = finallabel(stmt->label, stmt);
}
static void removeif(Statement *stmt, Boolean flag) {
if (((stmt->type == ST_IFGOTO) && flag) || ((stmt->type == ST_IFNGOTO) && !flag)) {
Statement *copy = lalloc(sizeof(Statement));
*copy = *stmt;
copy->type = ST_GOTO;
stmt->next = copy;
}
stmt->type = ST_EXPRESSION;
stmtchanged = 1;
}
static void optimizeif(Statement *stmt) {
Statement *scan;
Statement *scan2;
Boolean flag;
if (iszero(stmt->expr)) {
removeif(stmt, 0);
return;
}
if (isnotzero(stmt->expr)) {
removeif(stmt, 1);
return;
}
for (scan = stmt->next, flag = 0; scan; scan = scan->next) {
if (scan->type > ST_LABEL) {
if (scan->type == ST_GOTO) {
if (scan->label == stmt->label) {
stmt->type = ST_EXPRESSION;
stmtchanged = 1;
return;
}
if (!flag) {
for (scan2 = scan->next; scan2; scan2 = scan2->next) {
if (scan2->type > ST_LABEL)
break;
if (stmt->label->stmt == scan2) {
stmt->label = scan->label;
scan->type = ST_NOP;
if (stmt->type == ST_IFGOTO)
stmt->type = ST_IFNGOTO;
else
stmt->type = ST_IFGOTO;
stmtchanged = 1;
stmt->label = finallabel(stmt->label, stmt);
return;
}
}
}
} else if (scan->type == ST_RETURN && !scan->expr && !static_for_inlines && !flag) {
for (scan2 = scan->next; scan2; scan2 = scan2->next) {
if (scan2->type > ST_LABEL)
break;
if (stmt->label->stmt == scan2) {
stmt->label = cleanreturnlabel;
needs_cleanup = 1;
scan->type = ST_NOP;
if (stmt->type == ST_IFGOTO)
stmt->type = ST_IFNGOTO;
else
stmt->type = ST_IFGOTO;
stmtchanged = 1;
return;
}
}
}
break;
}
if (scan->type == ST_LABEL)
flag = 1;
if (stmt->label->stmt == scan) {
stmt->type = ST_EXPRESSION;
stmtchanged = 1;
return;
}
}
stmt->label = finallabel(stmt->label, stmt);
}
static void optimizeswitch(Statement *stmt) {
SwitchInfo *info;
SwitchCase *swcase;
info = (SwitchInfo *) stmt->label;
CError_ASSERT(1746, info && info->cases && info->defaultlabel);
info->defaultlabel = finallabel(info->defaultlabel, stmt);
for (swcase = info->cases; swcase; swcase = swcase->next)
swcase->label = finallabel(swcase->label, stmt);
if (ENODE_IS(stmt->expr, EINTCONST)) {
for (swcase = info->cases; swcase; swcase = swcase->next) {
if (CInt64_GreaterEqual(swcase->min, stmt->expr->data.intval) && CInt64_LessEqual(swcase->max, stmt->expr->data.intval))
break;
}
stmt->type = ST_GOTO;
stmt->label = swcase ? swcase->label : info->defaultlabel;
}
}
static void removeunusedlabels(Statement *stmt) {
Statement *scan;
CLabel *label;
SwitchCase *swcase;
ExceptionAction *action;
for (scan = stmt; scan; scan = scan->next)
scan->marked = 0;
for (scan = stmt; scan; scan = scan->next) {
switch (scan->type) {
case ST_GOTO:
case ST_IFGOTO:
case ST_IFNGOTO:
case ST_OVF:
if (scan->label->stmt)
scan->label->stmt->marked = 1;
break;
case ST_SWITCH:
((SwitchInfo *) scan->label)->defaultlabel->stmt->marked = 1;
for (swcase = ((SwitchInfo *) scan->label)->cases; swcase; swcase = swcase->next)
swcase->label->stmt->marked = 1;
break;
case ST_ASM:
if ((label = InlineAsm_GetReferencedLabel(scan)))
label->stmt->marked = 1;
if ((label = InlineAsm_GetReferencedLabel2(scan)))
label->stmt->marked = 1;
break;
case ST_BEGINLOOP:
((LoopInfo *) scan->expr)->stmt->marked = 1;
break;
case ST_ENDLOOP:
if (scan->next->type == ST_GOTO && scan->next->next->type == ST_LABEL)
scan->next->next->marked = 1;
break;
default:
for (action = scan->dobjstack; action; action = action->prev) {
if (action->type == EAT_CATCHBLOCK) {
action->data.catch_block.catch_label->stmt->marked = 1;
action->data.catch_block.catch_label->stmt->flags |= StmtFlag_1;
} else if (action->type == EAT_SPECIFICATION) {
action->data.specification.unexp_label->stmt->marked = 1;
action->data.specification.unexp_label->stmt->flags |= StmtFlag_1;
}
}
}
}
for (scan = stmt; scan; scan = scan->next) {
if (scan->type == ST_LABEL && !scan->marked && !(scan->flags & StmtFlag_1)) {
scan->type = ST_NOP;
stmtchanged = 1;
}
}
}
static void optimizebranches(Statement *stmt) {
removeunusedlabels(stmt);
while (stmt) {
switch (stmt->type) {
case ST_GOTO:
optimizegoto(stmt);
break;
case ST_IFGOTO:
case ST_IFNGOTO:
optimizeif(stmt);
break;
case ST_SWITCH:
optimizeswitch(stmt);
break;
}
stmt = stmt->next;
}
}
void SetVarUsage(Object *obj, Boolean noregister) {
VarInfo *vi;
CError_ASSERT(1875, obj->datatype != DALIAS);
if (obj->datatype == DLOCAL || obj->datatype == DNONLAZYPTR) {
vi = obj->u.var.info;
vi->used = 1;
if (!copts.global_optimizer) {
if (copts.optimize_for_size)
vi->usage++;
else
vi->usage += curstmtvalue;
}
if (noregister)
vi->noregister = 1;
}
}
static void checkexpression(ENode *expr) {
ENodeList *list;
while (1) {
switch (expr->type) {
case EOBJREF:
SetVarUsage(expr->data.objref, 1);
return;
case EINDIRECT:
if (ENODE_IS(expr->data.monadic, EOBJREF)) {
SetVarUsage(expr->data.monadic->data.objref, 0);
return;
}
expr = expr->data.monadic;
break;
case EFUNCCALL:
case EFUNCCALLP:
copt_isleaffunction = 0;
checkexpression(expr->data.funccall.funcref);
for (list = expr->data.funccall.args; list; list = list->next)
checkexpression(list->node);
return;
case ECOND:
case ECONDASS:
checkexpression(expr->data.cond.cond);
checkexpression(expr->data.cond.expr1);
checkexpression(expr->data.cond.expr2);
return;
case EMUL:
case EMULV:
case EDIV:
case EMODULO:
case EADDV:
case ESUBV:
case EADD:
case ESUB:
case ESHL:
case ESHR:
case ELESS:
case EGREATER:
case ELESSEQU:
case EGREATEREQU:
case EEQU:
case ENOTEQU:
case EAND:
case EXOR:
case EOR:
case ELAND:
case ELOR:
case EASS:
case EMULASS:
case EDIVASS:
case EMODASS:
case EADDASS:
case ESUBASS:
case ESHLASS:
case ESHRASS:
case EANDASS:
case EXORASS:
case EORASS:
case ECOMMA:
case EPMODULO:
case EROTL:
case EROTR:
case EBCLR:
case EBTST:
case EBSET:
checkexpression(expr->data.diadic.left);
expr = expr->data.diadic.right;
break;
case EPOSTINC:
case EPOSTDEC:
case EPREINC:
case EPREDEC:
case EMONMIN:
case EBINNOT:
case ELOGNOT:
case EFORCELOAD:
case ETYPCON:
case EBITFIELD:
expr = expr->data.monadic;
break;
case ENULLCHECK:
checkexpression(expr->data.nullcheck.nullcheckexpr);
expr = expr->data.nullcheck.condexpr;
break;
case EINTCONST:
case EFLOATCONST:
case EPRECOMP:
case ELABEL:
case EVECTOR128CONST:
return;
case ESTRINGCONST:
return;
default:
CError_FATAL(1998);
}
}
}
static void checklocalusage(Statement *stmt) {
Statement *scan;
for (scan = stmt; scan; scan = scan->next) {
if (scan->type >= ST_EXPRESSION && scan->type <= ST_GOTOEXPR && scan->expr) {
curstmtvalue = scan->value;
checkexpression(scan->expr);
} else if (scan->type == ST_ASM) {
curstmtvalue = scan->value;
InlineAsm_CheckLocalUsage(scan);
}
}
}
static void colorcode(Statement *stmt) {
CLabel *label;
SwitchCase *swcase;
while (stmt && !stmt->marked) {
stmt->marked = 1;
switch (stmt->type) {
case ST_GOTOEXPR:
return;
case ST_IFGOTO:
case ST_IFNGOTO:
case ST_OVF:
colorcode(stmt->label->stmt);
break;
case ST_GOTO:
case ST_EXIT:
stmt = stmt->label->stmt;
continue;
case ST_RETURN:
return;
case ST_SWITCH:
for (swcase = ((SwitchInfo *) stmt->label)->cases; swcase; swcase = swcase->next)
colorcode(swcase->label->stmt);
stmt = ((SwitchInfo *) stmt->label)->defaultlabel->stmt;
continue;
case ST_ASM:
if ((label = InlineAsm_GetReferencedLabel(stmt)))
colorcode(label->stmt);
if ((label = InlineAsm_GetReferencedLabel2(stmt)))
colorcode(label->stmt);
break;
case ST_ENDLOOP:
if (stmt->next && stmt->next->type == ST_GOTO) {
stmt = stmt->next;
stmt->marked = 1;
}
break;
case ST_NOP:
case ST_LABEL:
case ST_EXPRESSION:
case ST_ENTRY:
case ST_BEGINCATCH:
case ST_ENDCATCH:
case ST_ENDCATCHDTOR:
case ST_BEGINLOOP:
break;
default:
CError_FATAL(2096);
}
stmt = stmt->next;
}
}
static void removeunusedcode(Statement *stmt) {
Statement *scan;
for (scan = stmt; scan; scan = scan->next)
scan->marked = 0;
colorcode(stmt);
for (scan = stmt; scan; scan = scan->next) {
if (!scan->marked && (scan->flags & StmtFlag_1))
colorcode(scan);
}
for (scan = stmt; scan; scan = scan->next) {
if (!scan->marked && scan->type != ST_NOP) {
scan->type = ST_NOP;
stmtchanged = 1;
}
}
}
static void COpt_ReturnCheck(Object *obj, Statement *stmt) {
if ((copts.pedantic || copts.cplusplus) && obj && TYPE_FUNC(obj->type)->functype != &stvoid) {
while (stmt) {
if (!stmt->next && stmt->type != ST_GOTO && stmt->type != ST_RETURN) {
CError_Warning(CErrorStr184);
break;
}
if (stmt->type == ST_RETURN && !stmt->expr && !(stmt->flags & StmtFlag_8)) {
CError_Warning(CErrorStr184);
break;
}
stmt = stmt->next;
}
}
}
static void COpt_Optimize(Object *obj, Statement *stmt) {
Statement **ptr;
do {
stmtchanged = 0;
optimizebranches(stmt->next);
removeunusedcode(stmt->next);
} while (stmtchanged);
ptr = &stmt->next;
while (*ptr) {
if ((*ptr)->type == ST_NOP)
*ptr = (*ptr)->next;
else
ptr = &(*ptr)->next;
}
}
static void COpt_ELABELCallBack(ENode *expr) {
CError_ASSERT(2195, expr->data.label->stmt && expr->data.label->stmt->type == ST_LABEL);
expr->data.label->stmt->flags |= StmtFlag_1;
}
void COpt_SimpleOptimizer(Object *obj, Statement *stmt) {
Statement *scan;
for (scan = stmt; scan; scan = scan->next) {
if ((scan->type >= ST_EXPRESSION && scan->type <= ST_GOTOEXPR) && scan->expr)
CExpr_SearchExprTree(scan->expr, COpt_ELABELCallBack, 1, ELABEL);
}
static_for_inlines = 1;
cleanreturnlabel = NULL;
COpt_Optimize(obj, stmt);
COpt_ReturnCheck(obj, stmt);
}
Statement *COpt_Optimizer(Object *obj, Statement *stmt) {
copt_isleaffunction = 1;
if (copts.global_optimizer)
stmt = IRO_Optimizer(obj, stmt);
static_for_inlines = 0;
COpt_Optimize(obj, stmt);
if (obj && !(obj->qual & Q_INLINE))
COpt_ReturnCheck(obj, stmt);
checklocalusage(stmt->next);
return stmt;
}