#include "compiler/IroRangePropagation.h" #include "compiler/IroDump.h" #include "compiler/IroFlowgraph.h" #include "compiler/IroLinearForm.h" #include "compiler/IroMalloc.h" #include "compiler/IroPointerAnalysis.h" #include "compiler/IroUtil.h" #include "compiler/IroVars.h" #include "compiler/CInt64.h" #include "compiler/objects.h" #include "compiler/types.h" #ifdef __MWERKS__ #pragma options align=mac68k #endif typedef enum ERangeType { ERangeType0, ERangeType1, ERangeType2, ERangeType3 } ERangeType; typedef struct ERange { ERangeType type; CInt64 upper; CInt64 lower; } ERange; typedef struct ERecord { Object *object; ERange *range; struct ERecord *next; } ERecord; #ifdef __MWERKS__ #pragma options align=reset #endif static ERecord *ERangeFirst; static ERecord *ERangeLast; static ERange *ERnewERange(ERangeType type) { ERange *range; range = oalloc(sizeof(ERange)); range->type = type; return range; } static ERecord *ERnewRecord(Object *object, ERange *range) { ERecord *record; record = oalloc(sizeof(ERecord)); record->object = object; record->range = range; record->next = ERangeFirst; ERangeFirst = record; if (!ERangeLast) ERangeLast = record; return record; } static ERecord *ERecordFound(Object *obj) { ERecord *scan; scan = ERangeFirst; while (scan && obj != scan->object) scan = scan->next; return scan; } static Boolean EREandHasNoUse(ERange *range, CInt64 val) { UInt16 i; CInt64 v11; CInt64 work; i = 0; work = range->upper; while (CInt64_NotEqual(work = CInt64_ShrU(work, cint64_one), cint64_zero)) i++; if (CInt64_NotEqual(range->upper, cint64_zero)) i++; CInt64_SetULong(&work, i); v11 = CInt64_Sub(CInt64_Shl(cint64_one, work), cint64_one); if (CInt64_NotEqual(cint64_zero, CInt64_And(CInt64_Inv(val), v11))) return 0; else return 1; } static void ERcheckOverflow(ERange *range, Type *type) { CInt64 typeSize; CInt64 work; CInt64 work2; CInt64 value3; if (!range) return; if (!IS_TYPE_INT(type)) { range->type = ERangeType3; return; } CInt64_SetLong(&typeSize, type->size); CInt64_SetLong(&value3, 3); if (IRO_IsUnsignedType(type)) { if (type->size < 8) { work = CInt64_Sub(CInt64_Shl(cint64_one, CInt64_Shl(typeSize, value3)), cint64_one); if (CInt64_GreaterU(range->upper, work)) range->type = ERangeType3; } else { range->type = ERangeType3; } } else { if (type->size < 8) { work = CInt64_Sub(CInt64_Shl(cint64_one, CInt64_Sub(CInt64_Shl(typeSize, value3), cint64_one)), cint64_one); work2 = CInt64_Shl(cint64_negone, CInt64_Sub(CInt64_Shl(typeSize, value3), cint64_one)); if (CInt64_Greater(range->upper, work) || CInt64_Less(range->lower, work2)) range->type = ERangeType3; } else { range->type = ERangeType3; } } } static void ERinvalidAll(void) { ERecord *record; for (record = ERangeFirst; record; record = record->next) record->range->type = ERangeType3; } static void SetRangesForKillsByIndirectAssignment(IROLinear *nd) { IROListNode *list; IROListNode *scan; IROLinear *inner; Boolean failed; IROListNode *resultList; IROLinear *scannd; Boolean foundObjRef; ERecord *record; ERange *range; IROListNode *next; Object *obj; IROLinear *analysend; Object *proc; failed = 0; if (nd->type == IROLinearOp2Arg) inner = nd->u.diadic.left; else inner = nd->u.monadic; if ( inner && inner->type == IROLinearOp1Arg && inner->nodetype == EINDIRECT && (analysend = inner->u.monadic) && copts.opt_pointer_analysis && analysend->pointsToFunction && (proc = FunctionName) ) { resultList = NULL; PointerAnalysis_LookupLinearNodePointerExpr(proc, analysend, &resultList); if ((list = resultList)) { for (scan = list; scan; scan = scan->nextList) { if (!scan->list.head || !scan->list.tail) { failed = 1; break; } foundObjRef = 0; for (scannd = scan->list.head; scannd != scan->list.tail->next; scannd = scannd->next) { if (scannd->type == IROLinearOperand && scannd->u.node->type == EOBJREF) { foundObjRef = 1; break; } } if (!foundObjRef) { failed = 1; break; } } if (!failed) { for (; list; list = list->nextList) { for (scannd = list->list.head; scannd != list->list.tail->next; scannd = scannd->next) { if (scannd->type == IROLinearOperand && scannd->u.node->type == EOBJREF) { obj = scannd->u.node->data.objref; CError_ASSERT(302, obj != NULL); range = nd->x16; if (nd->nodetype == EPOSTINC || nd->nodetype == EPOSTDEC) range = inner->x16; record = ERecordFound(obj); if (!record) ERnewRecord(obj, range); else record->range = range; } } } } while (resultList) { next = resultList->nextList; IRO_free(resultList); resultList = next; } } else { failed = 1; } } else { failed = 1; } if (failed) { ERinvalidAll(); nd->x16 = ERnewERange(ERangeType3); } } static void InvalidateRangesForKillsByFunctionCall(IROLinear *nd) { IROListNode *scan; IROLinear *scannd; Boolean failed; Boolean foundObjRef; IROListNode *list; IROListNode *resultList; ERecord *record; IROListNode *next; Object *obj; IROLinear *analysend; Object *proc; ObjectList *olist; ObjectList *killList; failed = 0; if ( (analysend = nd->u.funccall.linear8) && copts.opt_pointer_analysis && analysend->pointsToFunction && (proc = FunctionName) ) { resultList = NULL; PointerAnalysis_LookupLinearNodePointerExpr(proc, analysend, &resultList); if (resultList) { for (scan = resultList; scan; scan = scan->nextList) { if (!scan->list.head || !scan->list.tail) { failed = 1; break; } foundObjRef = 0; for (scannd = scan->list.head; scannd != scan->list.tail->next; scannd = scannd->next) { if (scannd->type == IROLinearOperand && scannd->u.node->type == EOBJREF) { foundObjRef = 1; obj = scannd->u.node->data.objref; CError_ASSERT(385, obj != NULL); killList = NULL; PointerAnalysis_GetFunctionKills(obj, nd, &killList); for (olist = killList; olist; olist = olist->next) { if (!olist->object) { failed = 1; break; } } while (killList) { olist = killList->next; IRO_free(killList); killList = olist; } if (failed) break; } } if (!foundObjRef) failed = 1; if (failed) break; } if (!failed) { for (list = resultList; list; list = list->nextList) { for (scannd = list->list.head; scannd != list->list.tail->next; scannd = scannd->next) { if (scannd->type == IROLinearOperand && scannd->u.node->type == EOBJREF) { obj = scannd->u.node->data.objref; killList = NULL; PointerAnalysis_GetFunctionKills(obj, nd, &killList); for (olist = killList; olist; olist = olist->next) { if ((record = ERecordFound(olist->object))) record->range->type = ERangeType3; } while (killList) { olist = killList->next; IRO_free(killList); killList = olist; } } } } } while (resultList) { next = resultList->nextList; IRO_free(resultList); resultList = next; } } else { failed = 1; } } else { failed = 1; } if (failed) ERinvalidAll(); } static void ERfoldOperand(IROLinear *nd) { switch (nd->u.node->type) { case EOBJREF: nd->x16 = NULL; break; case EINTCONST: nd->x16 = ERnewERange(ERangeType0); nd->x16->upper = nd->x16->lower = nd->u.node->data.intval; break; case EFLOATCONST: case ESTRINGCONST: nd->x16 = ERnewERange(ERangeType0); break; } } static Boolean ERfoldExpr(IROLinear *nd) { ERecord *record; ERange *range; IROLinear *tmp; IROLinear *inner; Object *obj; switch (nd->nodetype) { case EINDIRECT: inner = nd->u.monadic; if (IS_TYPE_INT(nd->rtype)) { if (inner->type == IROLinearOperand && inner->u.node->type == EOBJREF) { if (!inner->x16 && (obj = inner->u.node->data.objref)) { if ((record = ERecordFound(obj))) { inner->x16 = ERnewERange(ERangeType3); *inner->x16 = *record->range; } else { inner->x16 = ERnewERange(ERangeType3); inner->x16->upper = cint64_max; inner->x16->lower = cint64_min; ERnewRecord(obj, inner->x16); } } nd->x16 = inner->x16; } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } else { if (inner->type == IROLinearOperand && inner->u.node->type == EOBJREF && !inner->x16) inner->x16 = ERnewERange(ERangeType3); nd->x16 = ERnewERange(ERangeType3); } break; case EAND: case EANDASS: if (IRO_IsIntConstant(nd->u.diadic.right)) { CInt64 val = nd->u.diadic.right->u.node->data.intval; nd->x16 = ERnewERange(ERangeType1); nd->x16->upper = val; nd->x16->lower = cint64_zero; if ( (range = nd->u.diadic.left->x16) && range->type != ERangeType3 && CInt64_LessEqualU(range->upper, val) && CInt64_LessEqualU(range->lower, val) && EREandHasNoUse(range, val) && !IRO_HasSideEffect(nd->u.diadic.left) ) { IRO_Dump("eliminating redundant EAND %ld; upperBound==0x%x, lowerBound==0x%x, Constant==0x%x\n", nd->index, CInt64_GetULong(&range->upper), CInt64_GetULong(&range->upper), CInt64_GetULong(&val) ); IRO_NopOut(nd->u.diadic.right); nd->type = IROLinearNop; nd->expr = NULL; tmp = nd->u.diadic.left; nd->u.diadic.left = nd->u.diadic.right; if (!IRO_LocateFather_Cut_And_Paste(nd, tmp)) { tmp->flags &= ~IROLF_Reffed; if (IRO_IsVariable(tmp)) IRO_NopOut(tmp); } } } else { if (nd->u.diadic.right->x16) { nd->x16 = ERnewERange(ERangeType3); *nd->x16 = *nd->u.diadic.right->x16; } } ERcheckOverflow(nd->x16, nd->rtype); break; case ELOGNOT: case ELESS: case EGREATER: case ELESSEQU: case EGREATEREQU: case EEQU: case ENOTEQU: case ELAND: case ELOR: nd->x16 = ERnewERange(ERangeType1); nd->x16->upper = cint64_one; nd->x16->lower = cint64_zero; ERcheckOverflow(nd->x16, nd->rtype); break; case EBINNOT: case EFORCELOAD: case EXOR: case EOR: case EXORASS: case EORASS: case ECOMMA: case ETYPCON: case EBITFIELD: case ECOND: case ENULLCHECK: nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; break; case EASS: if (IS_TYPE_INT(nd->rtype)) nd->x16 = nd->u.diadic.right->x16; break; case EMUL: case EMULV: case EMULASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); if (IRO_IsUnsignedType(nd->rtype)) { nd->x16->upper = CInt64_MulU(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->upper); nd->x16->lower = CInt64_MulU(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->lower); } else { nd->x16->upper = CInt64_Mul(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->upper); nd->x16->lower = CInt64_Mul(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->lower); } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EDIV: case EDIVASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); if (!CInt64_IsZero(&nd->u.diadic.right->x16->lower) && !CInt64_IsZero(&nd->u.diadic.right->x16->upper)) { if (IRO_IsUnsignedType(nd->rtype)) { nd->x16->upper = CInt64_DivU(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_DivU(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } else { nd->x16->upper = CInt64_Div(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_Div(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EMODULO: case EMODASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); if (!CInt64_IsZero(&nd->u.diadic.right->x16->lower) && !CInt64_IsZero(&nd->u.diadic.right->x16->upper)) { if (IRO_IsUnsignedType(nd->rtype)) { nd->x16->upper = CInt64_ModU(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_ModU(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } else { nd->x16->upper = CInt64_Mod(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_Mod(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EADDV: case EADD: case EADDASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); nd->x16->upper = CInt64_Add(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->upper); nd->x16->lower = CInt64_Add(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->lower); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case ESUBV: case ESUB: case ESUBASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); nd->x16->upper = CInt64_Sub(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_Sub(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case ESHL: case ESHLASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); nd->x16->upper = CInt64_Shl(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->upper); nd->x16->lower = CInt64_Shl(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->lower); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case ESHR: case ESHRASS: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.diadic.left->x16 && nd->u.diadic.left->x16->type != ERangeType3 && nd->u.diadic.right->x16 && nd->u.diadic.right->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); if (IRO_IsUnsignedType(nd->rtype)) { nd->x16->upper = CInt64_ShrU(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_ShrU(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } else { nd->x16->upper = CInt64_Shr(nd->u.diadic.left->x16->upper, nd->u.diadic.right->x16->lower); nd->x16->lower = CInt64_Shr(nd->u.diadic.left->x16->lower, nd->u.diadic.right->x16->upper); } } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EPOSTINC: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.monadic->x16 && nd->u.monadic->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); range = nd->u.monadic->x16; *nd->x16 = *range; range->upper = CInt64_Add(range->upper, cint64_one); range->lower = CInt64_Add(range->lower, cint64_one); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EPOSTDEC: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.monadic->x16 && nd->u.monadic->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); range = nd->u.monadic->x16; *nd->x16 = *range; range->upper = CInt64_Sub(range->upper, cint64_one); range->lower = CInt64_Sub(range->lower, cint64_one); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EPREINC: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.monadic->x16 && nd->u.monadic->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); range = nd->u.monadic->x16; nd->x16->upper = CInt64_Add(range->upper, cint64_one); nd->x16->lower = CInt64_Add(range->lower, cint64_one); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EPREDEC: if (IS_TYPE_INT(nd->rtype)) { if (nd->u.monadic->x16 && nd->u.monadic->x16->type != ERangeType3) { nd->x16 = ERnewERange(ERangeType2); range = nd->u.monadic->x16; nd->x16->upper = CInt64_Sub(range->upper, cint64_one); nd->x16->lower = CInt64_Sub(range->lower, cint64_one); } else { nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; } } ERcheckOverflow(nd->x16, nd->rtype); break; case EMONMIN: nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; break; case EPMODULO: case EROTL: case EROTR: case EBCLR: case EBTST: case EBSET: nd->x16 = ERnewERange(ERangeType3); nd->x16->upper = cint64_max; nd->x16->lower = cint64_min; break; default: ERcheckOverflow(nd->x16, nd->rtype); break; } if ( (nd->type == IROLinearOp1Arg || nd->type == IROLinearOp2Arg) && IRO_IsAssignOp[nd->nodetype] && nd->x16 && IS_TYPE_INT(nd->rtype) ) { IROLinear *x = NULL; if (nd->type == IROLinearOp2Arg) x = nd->u.diadic.left; else if (nd->type == IROLinearOp1Arg) x = nd->u.monadic; if (x->type == IROLinearOp1Arg && x->nodetype == EINDIRECT && (x->u.monadic->nodetype == EINDIRECT || x->u.monadic->nodetype == EADD)) { SetRangesForKillsByIndirectAssignment(nd); } else { obj = NULL; if (x) obj = IRO_IsVariable(x); if (!obj) return 0; range = nd->x16; if (nd->nodetype == EPOSTINC || nd->nodetype == EPOSTDEC) range = x->x16; record = ERecordFound(obj); if (!record) ERnewRecord(obj, range); else record->range = range; } } return nd->x16 != NULL; } static Boolean ERfoldLinear(IROLinear *nd) { nd->x16 = 0; switch (nd->type) { case IROLinearNop: case IROLinearEnd: break; case IROLinearOperand: ERfoldOperand(nd); break; case IROLinearOp1Arg: case IROLinearOp2Arg: ERfoldExpr(nd); break; case IROLinearFunccall: InvalidateRangesForKillsByFunctionCall(nd); break; case IROLinearAsm: ERinvalidAll(); break; } return 0; } Boolean IRO_RangePropagateInFNode(void) { IRONode *fnode; IROLinear *nd; Boolean result; result = 0; for (fnode = IRO_FirstNode; fnode; fnode = fnode->nextnode) { ERangeFirst = ERangeLast = NULL; for (nd = fnode->first; nd != fnode->last; nd = nd->next) ERfoldLinear(nd); if (ERfoldLinear(nd)) result = 1; } if (result) { IRO_ComputeSuccPred(); IRO_ComputeDom(); } IRO_CheckForUserBreak(); return result; }