diff options
| author | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
|---|---|---|
| committer | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
| commit | 268a53de823a6750d6256ee1fb1e7707b4b45740 (patch) | |
| tree | 42c1799a9a82b2f7d9790ee9fe181d72a7274751 /src/pdk/peep.c | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'src/pdk/peep.c')
| -rw-r--r-- | src/pdk/peep.c | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/src/pdk/peep.c b/src/pdk/peep.c new file mode 100644 index 0000000..153b090 --- /dev/null +++ b/src/pdk/peep.c @@ -0,0 +1,521 @@ +#include "common.h" +#include "SDCCgen.h" + +#include "peep.h" + +#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0) + +// #define D(_s) { printf _s; fflush(stdout); } +#define D(_s) + +#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) + +typedef enum +{ + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +static bool +isReturned(const char *what) +{ + symbol *sym; + sym_link *sym_lnk; + int size; + lineNode *l; + + l = _G.head; + do + { + l = l->next; + } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION); + + sym = OP_SYMBOL(IC_LEFT(l->ic)); + + if(sym && IS_DECL(sym->type)) + { + // Find size of return value. + specifier *spec; + if(sym->type->select.d.dcl_type != FUNCTION) + NOTUSEDERROR(); + spec = &(sym->etype->select.s); + if(spec->noun == V_VOID) + size = 0; + else if(spec->noun == V_CHAR || spec->noun == V_BOOL) + size = 1; + else if(spec->noun == V_INT && !(spec->b_long)) + size = 2; + else + size = 4; + + // Check for returned pointer. + sym_lnk = sym->type; + while (sym_lnk && !IS_PTR (sym_lnk)) + sym_lnk = sym_lnk->next; + if(IS_PTR(sym_lnk)) + size = 2; + } + else + { + NOTUSEDERROR(); + return TRUE; + } + + switch(*what) + { + case 'a': + return(size == 1 || size == 2); + case 'p': + return(size == 2); + default: + return false; + } +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each jump the label is at the end */ + p = strlen (pl->line) - 1 + pl->line; + + /* Skip trailing whitespace */ + while(isspace(*p)) + p--; + + /* scan backward until space or ',' */ + for (; p > pl->line; p--) + if (isspace(*p) || *p == ',') + break; + + /* sanity check */ + if (p == pl->line) + { + NOTUSEDERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if ( cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/* Check if reading arg implies reading what. */ +static bool argIs(const char *arg, const char *what) +{ + if (arg == NULL || strlen (arg) == 0) + return false; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + if (arg[0] == ',') + arg++; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + return !strncmp(arg, what, strlen(what)) && + (!arg[strlen(what)] || isspace((unsigned char)(arg[strlen(what)])) || arg[strlen(what)] == ','); +} + +static bool +stm8MightReadFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return true; + + if (ISINST (pl->line, "t0sn") || ISINST (pl->line, "t1sn")) + return argIs(strchr (pl->line, ','), what); + + if(ISINST (pl->line, "addc") || + ISINST (pl->line, "subc")) + return !strcmp(what, "c"); + + return false; +} + +static bool +pdkMightRead(const lineNode *pl, const char *what) +{ +//printf("pdkMightRead() for |%s|%s|\n", pl->line, what); + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8MightReadFlag(pl, what)); + else if (strcmp(what, "a") && strcmp(what, "p")) + return true; + + // Instructions that never read anything. + if (ISINST(pl->line, "engint") || + ISINST(pl->line, "disgint") || + ISINST(pl->line, "nop") || + ISINST(pl->line, "pop") || ISINST(pl->line, "popaf") || + ISINST(pl->line, "stopsys") || + ISINST(pl->line, "wdreset")) + return false; + + if (ISINST(pl->line, "ret") && strchr(pl->line + 2, '#') && !strcmp(what, "a")) + return false; + if (ISINST(pl->line, "ret")) + return isReturned(what); + + if (ISINST(pl->line, "mov")) + return argIs (strchr (pl->line, ','), what); + + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return !strcmp(what, "a"); + + // Two-operand instructions that read both operands + if (ISINST (pl->line, "add") || + ISINST (pl->line, "and") || + ISINST (pl->line, "or") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "xor")) + return argIs (pl->line + 3, what) || argIs (strchr (pl->line, ','), what); + if (ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what) || argIs (strchr (pl->line, ','), what); + if (ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn")) + return argIs (pl->line + 6, what) || argIs (strchr (pl->line, ','), what); + + // One-operand instructions + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "pcadd") || + ISINST (pl->line, "stt16")) + return argIs (pl->line + 5, what); + + // Todo: addc, subc, xch + + return true; +} + +static bool +stm8SurelyWritesFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return true; + + // Instructions that write all flags + if (ISINST (pl->line, "add") || + ISINST (pl->line, "addc") || + ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn") || + ISINST (pl->line, "dec") || + ISINST (pl->line, "dzsn") || + ISINST (pl->line, "inc") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "subc")) + return true; + + // Instructions that write c only + if (ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return !strcmp(what, "c"); + + // Instructions that write z only + if (ISINST (pl->line, "and") || + ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "or") || + ISINST (pl->line, "xor")) + return !strcmp(what, "z"); + + // mov writes z when the destination is a and hte source not an immediate only. + if (ISINST (pl->line, "mov") && !strcmp(what, "z") && pl->line[4] == 'a' && pl->line[5] == ',' && !strchr(pl->line, '#')) + return true; + + return false; +} + +static bool +pdkSurelyWrites(const lineNode *pl, const char *what) +{ + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8SurelyWritesFlag(pl, what)); + + if (ISINST(pl->line, "mov") || ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what); + + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return !strcmp(what, "a"); + + // TODO: Other instructions + + // One-operand instructions that write their argument + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "ldt16")) + return argIs (pl->line + 5, what); + + return false; +} + + +static bool +pdkUncondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "goto") || ISINST(pl->line, "pcadd")); +} + +static bool +pdkCondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "ceqsn") || ISINST(pl->line, "cneqsn") || + ISINST(pl->line, "t0sn") || ISINST(pl->line, "t1sn") || + ISINST(pl->line, "izsn") || ISINST(pl->line, "dzsn")); +} + +static bool +pdkSurelyReturns(const lineNode *pl) +{ + return(ISINST(pl->line, "ret")); +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or a opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *what, const char *untilOp, + lineNode **plCond) +{ + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) + continue; + D(("Scanning %s for %s\n", (*pl)->line, what)); + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + { + D(("S4O_ABORT at inline asm\n")); + return S4O_ABORT; + } + + if ((*pl)->visited) + { + D(("S4O_VISITED\n")); + return S4O_VISITED; + } + + (*pl)->visited = TRUE; + + if(pdkMightRead(*pl, what)) + { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } + + // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c' + if(pdkSurelyWrites(*pl, what)) + { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } + + if(pdkUncondJump(*pl)) + { + *pl = findLabel (*pl); + if (!*pl) + { + D(("S4O_ABORT at unconditional jump\n")); + return S4O_ABORT; + } + } + if(pdkCondJump(*pl)) + { + *plCond = (*pl)->next->next; + if (!*plCond) + { + D(("S4O_ABORT at conditional jump\n")); + return S4O_ABORT; + } + D(("S4O_CONDJMP\n")); + return S4O_CONDJMP; + } + + /* Don't need to check for de, hl since stm8MightRead() does that */ + if(pdkSurelyReturns(*pl)) + { + D(("S4O_TERM\n")); + return S4O_TERM; + } + } + D(("S4O_ABORT\n")); + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *what) +{ + lineNode *plConditional; + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, what, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return true; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + D(("CONDJMP trying other branch first\n")); + if (!doTermScan (&pl2, what)) + return false; + D(("Other branch OK.\n")); + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return false; + } + } +} + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = false; +} + +bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head) +{ + lineNode *pl; + + _G.head = head; + + unvisitLines (_G.head); + + pl = endPl->next; + return (doTermScan (&pl, what)); +} + +bool pdknotUsedFrom(const char *what, const char *label, lineNode *head) +{ + lineNode *cpl; + + for (cpl = head; cpl; cpl = cpl->next) + if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label))) + return (pdknotUsed (what, cpl, head)); + + return false; +} + +int +pdkinstructionSize(lineNode *pl) +{ + return 1; +} + |
