#include "ralloc.h"
#define D(x) x
-#define DEADMOVEERROR "Internal error: deadmove\n"
+#define DEADMOVEERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in deadmove");} while(0)
typedef enum
{
S4O_RD_OP,
S4O_TERM,
S4O_VISITED,
- S4O_ABORT
+ S4O_ABORT,
+ S4O_CONTINUE
} S4O_RET;
static struct
/* sanity check */
if (p == pl->line)
{
- D(fprintf (stderr, DEADMOVEERROR);)
+ DEADMOVEERROR();
return NULL;
}
return NULL;
}
+/*-----------------------------------------------------------------*/
+/* isFunc - returns TRUE if it's a CALL or PCALL (not _gptrget()) */
+/*-----------------------------------------------------------------*/
+static bool
+isFunc (const lineNode *pl)
+{
+ if (pl && pl->ic)
+ {
+ if ( pl->ic->op == CALL
+ || pl->ic->op == PCALL)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* termScanAtFunc - returns S4O_TERM if it's a 'normal' function */
+/* call and it's a 'caller save'. returns S4O_CONTINUE if it's */
+/* 'callee save' or 'naked'. returns S4O_ABORT if it's 'banked' */
+/* uses the register for the destination. */
+/*-----------------------------------------------------------------*/
+static S4O_RET
+termScanAtFunc (const lineNode *pl, int rIdx)
+{
+ sym_link *ftype;
+
+ 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.all_callee_saves;
+
+ ftype = OP_SYM_TYPE(IC_LEFT(pl->ic));
+ if (IS_FUNCPTR (ftype))
+ ftype = ftype->next;
+ if (FUNC_CALLEESAVES(ftype))
+ return S4O_CONTINUE;
+ if (FUNC_ISNAKED(ftype))
+ return S4O_CONTINUE;
+ if (FUNC_BANKED(ftype) &&
+ ((rIdx == R0_IDX) || (rIdx == R1_IDX) || (rIdx == R2_IDX)))
+ return S4O_ABORT;
+ return S4O_TERM;
+}
+
/*-----------------------------------------------------------------*/
/* scan4op - "executes" and examines the assembler opcodes, */
/* follows conditional and un-conditional jumps. */
char *p;
int len;
bool isConditionalJump;
+ int rIdx;
+ S4O_RET ret;
/* 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)
{
/* ok, let's have a closer look */
- /* get index into pReg table */
- int rIdx;
-
- for (rIdx = 0; rIdx < mcs51_nRegs; ++rIdx)
- if (strcmp (regs8051[rIdx].name, pReg + 1) == 0)
- break;
-
- /* sanity check */
- if (rIdx >= mcs51_nRegs)
- {
- D(fprintf (stderr, DEADMOVEERROR);)
- return S4O_ABORT;
- }
-
/* does opcode read from pReg? */
if (bitVectBitValue (port->peep.getRegsRead ((*pl)), rIdx))
return S4O_RD_OP;
if (bitVectBitValue (port->peep.getRegsWritten ((*pl)), rIdx))
return S4O_WR_OP;
- /* should never reach here */
- D(fprintf (stderr, DEADMOVEERROR);)
- return S4O_ABORT;
+ /* we can get here, if the register name is
+ part of a variable name: ignore it */
}
}
/* register passing this label */
if (!setLabelRefPassedLabel (label))
{
- D(fprintf (stderr, DEADMOVEERROR);)
+ DEADMOVEERROR();
return S4O_ABORT;
}
continue;
{
case 'a':
if (strncmp ("acall", (*pl)->line, 5) == 0)
- return S4O_TERM;
+ {
+ /* 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);
break;
case 'l':
if (strncmp ("lcall", (*pl)->line, 5) == 0)
- return S4O_TERM;
+ {
+ 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);
return S4O_PUSHPOP;
break;
case 'r':
- /* pcall uses ret */
- if (strncmp ("ret", (*pl)->line, 3) == 0) /* catches "reti" too */
+ 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 */
+ return S4O_TERM;
+ }
break;
case 's':
if (strncmp ("sjmp", (*pl)->line, 4) == 0)
case S4O_VISITED:
if (!pushPl)
{
- D(fprintf (stderr, DEADMOVEERROR);)
+ DEADMOVEERROR();
return FALSE;
}
*pl = pushPl;
}
/*-----------------------------------------------------------------*/
-/* - */
+/* removeDeadPopPush - remove pop/push pair if possible */
/*-----------------------------------------------------------------*/
-bool
-mcs51DeadMove (const char *op1, lineNode *currPl, lineNode *head)
+static bool
+removeDeadPopPush (const char *pReg, lineNode *currPl, lineNode *head)
{
- char pReg[5] = "ar";
lineNode *pushPl, *pl;
/* A pop/push pair can be removed, if these criteria are met
; - inline assembly
; - a jump in or out of area 2 (see checkLabelRef())
- ; An "acall", "lcall", "ret", "reti" or write access of ar0 terminate
- ; the search, and the pop/push pair can safely be removed.
+ ; An "acall", "lcall" (not callee save), "ret" (not PCALL with
+ ; callee save), "reti" or write access of r0 terminate
+ ; the search, and the "mov r0,a" can safely be removed.
*/
- _G.head = head;
- strcat (pReg, op1);
-
- unvisitLines (_G.head);
- cleanLabelRef();
-
/* area 1 */
pushPl = currPl->next;
if (!doPushScan (&pushPl, pReg))
/* 'pop ar0' will be removed by peephole framework after returning TRUE */
return TRUE;
}
+
+/*-----------------------------------------------------------------*/
+/* removeDeadMove - remove superflous 'mov r%1,%2' */
+/*-----------------------------------------------------------------*/
+static bool
+removeDeadMove (const char *pReg, lineNode *currPl, lineNode *head)
+{
+ lineNode *pl;
+
+ /* "mov r0,a" can be removed, if these criteria are met
+ (r0 is just an example here, r0...r7 are possible):
+
+ ; There must not be:
+ ; - read access of r0
+ ; - "jmp @a+dptr" opcode
+ ; - inline assembly
+ ; - a jump in or out of this area (see checkLabelRef())
+
+ ; An "acall", "lcall" (not callee save), "ret" (not PCALL with
+ ; callee save), "reti" or write access of r0 terminate
+ ; the search, and the "mov r0,a" can safely be removed.
+ */
+ pl = currPl->next;
+ if (!doTermScan (&pl, pReg))
+ return FALSE;
+
+ if (!checkLabelRef())
+ return FALSE;
+
+ return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* mcs51DeadMove - dispatch condition deadmove between */
+/* - remove pop/push */
+/* - remove mov r%1,%2 */
+/*-----------------------------------------------------------------*/
+bool
+mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head)
+{
+ char pReg[5] = "ar";
+
+ _G.head = head;
+ strcat (pReg, reg);
+
+ unvisitLines (_G.head);
+ cleanLabelRef();
+
+ if (strncmp (currPl->line, "pop", 3) == 0)
+ return removeDeadPopPush (pReg, currPl, head);
+ else if ( strncmp (currPl->line, "mov", 3) == 0
+ && (currPl->line[3] == ' ' || currPl->line[3] == '\t'))
+ return removeDeadMove (pReg, currPl, head);
+ else
+ {
+ fprintf (stderr, "Error: "
+ "peephole rule with condition deadMove "
+ "used with unknown opocde:\n"
+ "\t%s\n", currPl->line);
+ return FALSE;
+ }
+}