* device/lib/Makefile.in: remove abspath for PORTDIR, introduced in
[fw/sdcc] / src / izt / gen.c
index c6536360de79483bfff0eec1a27653e840b3f2fb..6579981f1efe1fa4dd41bb9b6ae45208d8aa693f 100644 (file)
 #include "izt.h"
+#include "gen.h"
 
-void iemit(const char *format, ...)
+static struct
+  {
+    struct
+      {
+       lineNode *head;
+       lineNode *current;
+      }
+    lines;
+    struct
+      {
+       hTab *base;
+       hTab *proc;
+      }
+    htabs;
+  }
+_G;
+
+static void
+_tidyUp (char *buf)
+{
+  // Clean up the line so that it is 'prettier'.
+  if (*buf == ';')
+    {
+      // If this is a comment line (starts with a ';') then indent it.
+      // PENDING: The outputter does its own pretty print.  Disable for now.
+    }
+  else if (strchr (buf, ':'))
+    {
+      // Is a label - cant do anything.
+    }
+  else
+    {
+      /* Change the first (and probably only) ' ' to a tab so
+         everything lines up.
+       */
+      while (*buf)
+       {
+         if (*buf == ' ')
+           {
+             *buf = '\t';
+             return;
+           }
+         buf++;
+       }
+    }
+}
+
+void
+iemit (const char *szFormat,...)
+{
+  char buffer[1024];
+  va_list ap;
+
+  va_start (ap, szFormat);
+
+  tvsprintf (buffer, sizeof(buffer), szFormat, ap);
+
+  _tidyUp (buffer);
+
+  if (_G.lines.current == NULL)
+    {
+      _G.lines.head = newLineNode (buffer);
+      _G.lines.current = _G.lines.head;
+    }
+  else
+    {
+      _G.lines.current = connectLine (_G.lines.current, newLineNode (buffer));
+    }
+
+  // PENDING: Inline support.
+  //    _G.lines.current->isInline = inLine;
+}
+
+// Mapping between operand type and a friendly name.
+typedef struct
+{
+  const int op;
+  const char *name;
+}
+OPNAME;
+
+static OPNAME _opnames[] =
+{
+  {'!', "genNot"},
+  {'~', "genCpl"},
+  {UNARYMINUS, "genUminus"},
+  {IPUSH, "genIpush"},
+  {IPOP, "genIfx"},
+  {CALL, "genCall"},
+  {PCALL, "genPcall"},
+  {FUNCTION, "genFunction"},
+  {ENDFUNCTION, "genEndFunction"},
+  {RETURN, "genRet"},
+  {LABEL, "genLabel"},
+  {GOTO, "genGoto"},
+  {'+', "genPlus"},
+  {'-', "genMinus"},
+  {'*', "genMult"},
+  {'/', "genDiv"},
+  {'%', "genMod"},
+  {'>', "genCmpGt"},
+  {'<', "genCmpLt"},
+  {EQ_OP, "genCmpEq"},
+  {AND_OP, "genAndOp"},
+  {OR_OP, "genOrOp"},
+  {'^', "genXor"},
+  {'|', "genOr"},
+  {BITWISEAND, "genAnd"},
+  {INLINEASM, "genInline"},
+  {RRC, "genRRC"},
+  {RLC, "genRLC"},
+  {GETHBIT, "genHBIT"},
+  {LEFT_OP, "genLeftShift"},
+  {RIGHT_OP, "genRightShift"},
+  {GET_VALUE_AT_ADDRESS, "genPointerGet"},
+  {'=', "genAssign"},
+  {IFX, "genIfx"},
+  {ADDRESS_OF, "genAddrOf"},
+  {JUMPTABLE, "genJumpTab"},
+  {CAST, "genCast"},
+  {RECEIVE, "genReceive"},
+  {SEND, "addSet"},
+  {0, NULL}
+};
+
+// Possible return codes for a find matcher.
+enum
+  {
+    FIND_LESS_THAN = -1,
+    // This element does match.
+    FIND_MATCH = 0,
+    FIND_GREATER_THAN = 1,
+    // This element doesnt match.
+    FIND_NO_MATCH = FIND_GREATER_THAN,
+    // This element marks the end of list.
+    FIND_EOL
+  };
+
+// Limits the given integer to the find result numbers.
+static int
+_limitToFind (int i)
+{
+  if (i < 0)
+    {
+      return FIND_LESS_THAN;
+    }
+  else if (i > 0)
+    {
+      return FIND_GREATER_THAN;
+    }
+  else
+    {
+      return FIND_MATCH;
+    }
+}
+
+// Matches an opname id to the given id.
+static int
+_opnamesMatcher (void *pthis, void *pkey)
+{
+  OPNAME *name = pthis;
+
+  if (name->name == NULL)
+    {
+      return FIND_EOL;
+    }
+  else
+    {
+      return _limitToFind (name->op - *(int *) pkey);
+    }
+}
+
+// Find an element of an unordered list.
+static void *
+_find (void *base, size_t elemSize, void *pkey, int (*match) (void *pthis, void *pkey))
+{
+  do
+    {
+      switch (match (base, pkey))
+       {
+       case FIND_MATCH:
+         return base;
+       case FIND_EOL:
+         return NULL;
+       case FIND_LESS_THAN:
+       case FIND_GREATER_THAN:
+         base = (char *) base + elemSize;
+         break;
+       default:
+         wassert (0);
+       }
+    }
+  while (1);
+}
+
+// Finds the friendly operation name for an op number.
+static const char *
+_findOpName (int op)
+{
+  OPNAME *name = _find (_opnames, sizeof (*_opnames), &op, _opnamesMatcher);
+  if (name)
+    {
+      return name->name;
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+// PENDING
+static bool
+_isResultRemat (iCode * ic)
 {
-    va_list ap;
-    va_start(ap, format);
+  if (SKIP_IC (ic) || ic->op == IFX)
+    return 0;
+
+  if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic)))
+    {
+      symbol *sym = OP_SYMBOL (IC_RESULT (ic));
+      if (sym->remat && !POINTER_SET (ic))
+       return 1;
+    }
+
+  return 0;
+}
+
+// Print out the generated lines.
+static void
+_printLines (void)
+{
+  // Currently a holder function.  The Z80 needs some special mangling
+  // for bank support.
+  printLine (_G.lines.head, codeOutBuf);
+}
+
+void
+izt_initEmitters (void)
+{
+  _G.htabs.base = newHashTable (100);
+  _G.htabs.proc = newHashTable (100);
+
+  izt_initBaseEmitters (&_G.htabs.base);
+}
+
+static int
+_emitterCompare (const void *p1, const void *p2)
+{
+  wassert (p1);
+  wassert (p2);
+  return ((EMITTER *) p1)->op == ((EMITTER *) p2)->op;
+}
+
+static bool
+_tryEmittingiCode (hTab * h, iCode * ic)
+{
+  EMITTER key; // = {ic->op, NULL}; Borland C chokes on this; initialize below
+  EMITTER *found;
+
+  key.op = ic->op;
+  key.emit = NULL;
+
+  found = hTabFindByKey (h, ic->op, &key, _emitterCompare);
+
+  if (found)
+    {
+      found->emit (ic);
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+// Add a NULL terminated array of emitters into the given hash table.
+void
+izt_addEmittersToHTab (hTab ** into, EMITTER _base_emitters[])
+{
+  while (_base_emitters->emit != NULL)
+    {
+      hTabAddItemLong (into, _base_emitters->op, _base_emitters, _base_emitters);
+      _base_emitters++;
+    }
+}
+
+void
+izt_gen (iCode * iic)
+{
+  iCode *ic = iic;
+  int cln = 0;
+
+  _G.lines.head = NULL;
+  _G.lines.current = NULL;
+
+  // No debug info support.
+
+  for (; ic; ic = ic->next)
+    {
+      const char *name;
+
+      // Print out the source file line number.
+      if (cln != ic->lineno)
+       {
+         iemit ("; %s %d", ic->filename, ic->lineno);
+         cln = ic->lineno;
+       }
+
+      if (ic->generated)
+       {
+         // Code has already been generated.  Skip.
+         continue;
+       }
+      if (_isResultRemat (ic))
+       {
+         // The code is spilt and remat. - no need to generate.
+         continue;
+       }
+
+      // Print the friendly name of the operation, if it has one.
+      name = _findOpName (ic->op);
+      if (name)
+       {
+         iemit ("; %s", name);
+       }
+      else
+       {
+         iemit ("; warning: unrecognised operation name for %u", ic->op);
+       }
+
+      fflush (stdout);
+      // Now actually call the code generator.
+      // The current processor handler gets first try.
+      if (_tryEmittingiCode (_G.htabs.proc, ic))
+       {
+         // Yay.
+       }
+      else if (_tryEmittingiCode (_G.htabs.base, ic))
+       {
+         // Base handler took it.
+       }
+      else
+       {
+         // None took it.  Warn the developer.
+         iemit ("; warning: no handler for code %u", ic->op);
+       }
+    }
 
-    vprintf(format, ap);
-    printf("\n");
+  // Pass the code through the peephole optimiser.
+  if (!options.nopeep)
+    {
+      peepHole (&_G.lines.head);
+    }
 
-    va_end(ap);
+  // And emit the remainder.
+  _printLines ();
 }