#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; }