/*-----------------------------------------------------------------*/ static S4O_RET termScanAtFunc (const lineNode *pl, int rIdx) { sym_link *ftype; bool banked_reg = (rIdx == R0_IDX) || (rIdx == R1_IDX) || (rIdx == R2_IDX); if (!isFunc (pl)) return S4O_CONTINUE; // let's assume calls to literally given locations use the default // most notably : (*(void (*)()) 0) (); see bug 1749275 if (IS_VALOP (IC_LEFT (pl->ic))) return (options.model == MODEL_HUGE) && banked_reg ? S4O_ABORT : options.all_callee_saves ? S4O_CONTINUE : S4O_TERM; ftype = OP_SYM_TYPE(IC_LEFT(pl->ic)); if (IS_FUNCPTR (ftype)) ftype = ftype->next; if (IFFUNC_ISBANKEDCALL(ftype) && banked_reg) return S4O_ABORT; if (FUNC_ARGS (ftype) && getSize (FUNC_ARGS (ftype)->type) > 4) return S4O_ABORT; if (FUNC_CALLEESAVES(ftype)) return S4O_CONTINUE; if (FUNC_ISNAKED(ftype)) return S4O_CONTINUE; return S4O_TERM; }
/*-----------------------------------------------------------------*/ static S4O_RET scan4op (lineNode **pl, const char *pReg, const char *untilOp, lineNode **plCond) { char *p; int len; bool isConditionalJump; int rIdx; S4O_RET ret; bool findPushPop; findPushPop = untilOp && (strcmp (untilOp, "push") == 0 || strcmp (untilOp, "pop") == 0); /* pReg points to e.g. "ar0"..."ar7" */ len = strlen (pReg); /* get index into pReg table */ for (rIdx = 0; rIdx < mcs51_nRegs; ++rIdx) if (strcmp (regs8051[rIdx].name, pReg + 1) == 0) break; /* sanity check */ if (rIdx >= mcs51_nRegs) { DEADMOVEERROR(); return S4O_ABORT; } for (; *pl; *pl = (*pl)->next) { if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment) continue; /* don't optimize across inline assembler, e.g. isLabel doesn't work there */ if ((*pl)->isInline) return S4O_ABORT; if ((*pl)->visited) return S4O_VISITED; (*pl)->visited = TRUE; /* found untilOp? */ if (untilOp && strncmp ((*pl)->line, untilOp, strlen (untilOp)) == 0) { p = (*pl)->line + strlen (untilOp); if (*p == '\t' && strncmp (p + 1, pReg, len) == 0) return S4O_FOUNDOPCODE; else { /* found untilOp but without our pReg */ return S4O_ABORT; } } /* found pReg? */ p = strchr ((*pl)->line, '\t'); if (p) { /* skip '\t' */ p++; /* when looking for push or pop and we find a direct access of sp: abort */ if (findPushPop && strstr (p, "sp")) return S4O_ABORT; /* course search */ if (strstr (p, pReg + 1)) { /* ok, let's have a closer look */ /* does opcode read from pReg? */ if (bitVectBitValue (port->peep.getRegsRead ((*pl)), rIdx)) return S4O_RD_OP; /* does opcode write to pReg? */ if (bitVectBitValue (port->peep.getRegsWritten ((*pl)), rIdx)) return S4O_WR_OP; /* we can get here, if the register name is part of a variable name: ignore it */ } } /* found label? */ if ((*pl)->isLabel) { const char *start; char label[SDCC_NAME_MAX + 1]; int len; if (!isLabelDefinition ((*pl)->line, &start, &len, FALSE)) return S4O_ABORT; memcpy (label, start, len); label[len] = '\0'; /* register passing this label */ if (!setLabelRefPassedLabel (label)) { DEADMOVEERROR(); return S4O_ABORT; } continue; } /* branch or terminate? */ isConditionalJump = FALSE; switch ((*pl)->line[0]) { case 'a': if (strncmp ("acall", (*pl)->line, 5) == 0) { /* for comments see 'lcall' */ ret = termScanAtFunc (*pl, rIdx); if (ret != S4O_CONTINUE) return ret; break; } if (strncmp ("ajmp", (*pl)->line, 4) == 0) { *pl = findLabel (*pl); if (!*pl) return S4O_ABORT; } break; case 'c': if (strncmp ("cjne", (*pl)->line, 4) == 0) { isConditionalJump = TRUE; break; } break; case 'd': if (strncmp ("djnz", (*pl)->line, 4) == 0) { isConditionalJump = TRUE; break; } break; case 'j': if (strncmp ("jmp", (*pl)->line, 3) == 0) /* "jmp @a+dptr": no chance to trace execution */ return S4O_ABORT; if (strncmp ("jc", (*pl)->line, 2) == 0 || strncmp ("jnc", (*pl)->line, 3) == 0 || strncmp ("jz", (*pl)->line, 2) == 0 || strncmp ("jnz", (*pl)->line, 3) == 0) { isConditionalJump = TRUE; break; } if (strncmp ("jbc", (*pl)->line, 3) == 0 || strncmp ("jb", (*pl)->line, 2) == 0 || strncmp ("jnb", (*pl)->line, 3) == 0) { isConditionalJump = TRUE; break; } break; case 'l': if (strncmp ("lcall", (*pl)->line, 5) == 0) { const char *p = (*pl)->line+5; while (*p == ' ' || *p == '\t') p++; while (isdigit (*p)) p++; if (isdigit(p[-1]) && *p == '$') /* at least one digit */ { /* this is a temp label for a pcall */ *pl = findLabel (*pl); if (!*pl) return S4O_ABORT; break; } ret = termScanAtFunc (*pl, rIdx); /* If it's a 'normal' 'caller save' function call, all registers have been saved until the 'lcall'. The 'life range' of all registers end at the lcall, and we can terminate our search. * If the function is 'banked', the registers r0, r1 and r2 are used to tell the trampoline the destination. After that their 'life range' ends just like the other registers. * If it's a 'callee save' function call, registers are saved by the callee. We've got no information, if the register might live beyond the lcall. Therefore we've to continue the search. */ if (ret != S4O_CONTINUE) return ret; break; } if (strncmp ("ljmp", (*pl)->line, 4) == 0) { *pl = findLabel (*pl); if (!*pl) return S4O_ABORT; } break; case 'p': if (strncmp ("pop", (*pl)->line, 3) == 0 || strncmp ("push", (*pl)->line, 4) == 0) return S4O_PUSHPOP; break; case 'r': if (strncmp ("reti", (*pl)->line, 4) == 0) return S4O_TERM; if (strncmp ("ret", (*pl)->line, 3) == 0) { /* pcall uses 'ret' */ if (isFunc (*pl)) { /* for comments see 'lcall' */ ret = termScanAtFunc (*pl, rIdx); if (ret != S4O_CONTINUE) return ret; break; } /* it's a normal function return */ if (!((*pl)->ic) || (IS_SYMOP (IC_LEFT ((*pl)->ic)) && IS_FUNC (OP_SYM_TYPE(IC_LEFT ((*pl)->ic))) && FUNC_CALLEESAVES (OP_SYM_TYPE(IC_LEFT ((*pl)->ic))))) return S4O_ABORT; else return S4O_TERM; } break; case 's': if (strncmp ("sjmp", (*pl)->line, 4) == 0) { *pl = findLabel (*pl); if (!*pl) return S4O_ABORT; } break; default: break; } /* switch ((*pl)->line[0]) */ if (isConditionalJump) { *plCond = findLabel (*pl); if (!*plCond) return S4O_ABORT; return S4O_CONDJMP; } } /* for (; *pl; *pl = (*pl)->next) */ return S4O_ABORT; }