/*------------------------------------------------------------------------- SDCCgen.c - source files for target code generation common functions Copyright (C) 2012, Borut Razem This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -------------------------------------------------------------------------*/ #include "common.h" #include "dbuf_string.h" /* Use the D macro for basic (unobtrusive) debugging messages */ #define D(x) do if (options.verboseAsm) {x;} while(0) genLine_t genLine; /*-----------------------------------------------------------------*/ /* newLineNode - creates a new peep line */ /*-----------------------------------------------------------------*/ lineNode * newLineNode (const char *line) { lineNode *pl; pl = Safe_alloc (sizeof (lineNode)); pl->line = Safe_strdup (line); pl->ic = NULL; return pl; } /*-----------------------------------------------------------------*/ /* connectLine - connects two lines */ /*-----------------------------------------------------------------*/ lineNode * connectLine (lineNode * pl1, lineNode * pl2) { if (!pl1 || !pl2) { fprintf (stderr, "trying to connect null line\n"); return NULL; } pl2->prev = pl1; pl1->next = pl2; return pl2; } void destroy_line_list (void) { lineNode *pl; pl = genLine.lineCurr; while (pl) { lineNode *p; if (pl->line) Safe_free (pl->line); if (pl->aln) Safe_free (pl->aln); p = pl; pl = pl->prev; Safe_free (p); } genLine.lineHead = genLine.lineCurr = NULL; } /*-----------------------------------------------------------------*/ /* emit_raw - emit raw unformatted line */ /*-----------------------------------------------------------------*/ static void add_line_node (const char *line) { lineNode *pl; pl = Safe_alloc (sizeof (lineNode)); #if 1 memcpy (pl, (lineElem_t *) & genLine.lineElement, sizeof (lineElem_t)); #else pl->ic = genLine.lineElement.ic; pl->isInline = genLine.lineElement.isInline; pl->isComment = genLine.lineElement.isComment; pl->isDebug = genLine.lineElement.isDebug; pl->isLabel = genLine.lineElement.isLabel; pl->visited = genLine.lineElement.visited; pl->aln = genLine.lineElement.aln; #endif pl->line = Safe_strdup (line); if (genLine.lineCurr) { pl->next = NULL; genLine.lineCurr->next = pl; pl->prev = genLine.lineCurr; genLine.lineCurr = pl; } else { pl->prev = pl->next = NULL; genLine.lineCurr = genLine.lineHead = pl; } } void emit_raw (const char *line) { const char *p = line; while (isspace ((unsigned char) *p)) p++; if (*p) { if (!port->rtrackUpdate || !port->rtrackUpdate(line)) { genLine.lineElement.isComment = (*p == ';'); add_line_node (line); } } } /*-----------------------------------------------------------------*/ /* format_opcode - format the opcode and arguments for emitting */ /*-----------------------------------------------------------------*/ const char * format_opcode (const char *inst, const char *fmt, va_list ap) { struct dbuf_s dbuf; dbuf_init (&dbuf, INITIAL_INLINEASM); if (inst && *inst) { dbuf_append_str (&dbuf, inst); if (fmt && *fmt) { dbuf_append_char (&dbuf, '\t'); dbuf_tvprintf (&dbuf, fmt, ap); } } else { if (fmt && *fmt) { dbuf_tvprintf (&dbuf, fmt, ap); } } return dbuf_detach_c_str (&dbuf); } void va_emitcode (const char *inst, const char *fmt, va_list ap) { const char *line = format_opcode (inst, fmt, ap); emit_raw (line); dbuf_free (line); } /*-----------------------------------------------------------------*/ /* emitcode - writes the code into a file : for now it is simple */ /*-----------------------------------------------------------------*/ void emitcode (const char *inst, const char *fmt, ...) { va_list ap; va_start (ap, fmt); va_emitcode (inst, fmt, ap); va_end (ap); } void emitLabel (symbol *tlbl) { if (!tlbl) return; emitcode ("", "!tlabeldef", labelKey2num (tlbl->key)); genLine.lineCurr->isLabel = 1; } /*-----------------------------------------------------------------*/ /* genInline - write the inline code out */ /*-----------------------------------------------------------------*/ void genInline (iCode * ic) { char *buf, *bp, *begin; bool inComment = FALSE; D (emitcode (";", "genInline")); genLine.lineElement.isInline += (!options.asmpeep); buf = bp = begin = Safe_strdup (IC_INLINE (ic)); /* Emit each line as a code */ while (*bp) { switch (*bp) { case ';': inComment = TRUE; ++bp; break; case '\x87': case '\n': inComment = FALSE; *bp++ = '\0'; /* Don't emit leading whitespaces */ while (isspace (*begin)) ++begin; if (*begin) emitcode (begin, NULL); begin = bp; break; default: /* Add \n for labels, not dirs such as c:\mydir */ if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) { ++bp; *bp = '\0'; ++bp; emitcode (begin, NULL); begin = bp; } else ++bp; break; } } if (begin != bp) { /* Don't emit leading whitespaces */ while (isspace (*begin)) ++begin; if (*begin) emitcode (begin, NULL); } Safe_free (buf); /* consumed; we can free it here */ dbuf_free (IC_INLINE (ic)); genLine.lineElement.isInline -= (!options.asmpeep); } /*-----------------------------------------------------------------*/ /* printLine - prints a line chain into a given file */ /*-----------------------------------------------------------------*/ void printLine (lineNode * head, struct dbuf_s *oBuf) { iCode *last_ic = NULL; bool debug_iCode_tracking = (getenv ("DEBUG_ICODE_TRACKING") != NULL); while (head) { if (head->ic != last_ic) { last_ic = head->ic; if (debug_iCode_tracking) { if (head->ic) dbuf_printf (oBuf, "; block = %d, seq = %d\n", head->ic->block, head->ic->seq); else dbuf_append_str (oBuf, "; iCode lost\n"); } } /* don't indent comments & labels */ if (head->line && (head->isComment || head->isLabel)) { dbuf_printf (oBuf, "%s\n", head->line); } else { if (head->isInline && *head->line == '#') { /* comment out preprocessor directives in inline asm */ dbuf_append_char (oBuf, ';'); } dbuf_printf (oBuf, "\t%s\n", head->line); } head = head->next; } } /*-----------------------------------------------------------------*/ /* ifxForOp - returns the icode containing the ifx for operand */ /*-----------------------------------------------------------------*/ iCode * ifxForOp (operand * op, const iCode * ic) { iCode *ifxIc; /* if true symbol then needs to be assigned */ if (!IS_TRUE_SYMOP (op)) { /* if this has register type condition and while skipping ipop's (see bug 1509084), the next instruction is ifx with the same operand and live to of the operand is upto the ifx only then */ for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next) ; if (ifxIc && ifxIc->op == IFX && IC_COND (ifxIc)->key == op->key && OP_SYMBOL (op)->liveTo <= ifxIc->seq) return ifxIc; } return NULL; }