* link/z80/aslink.h: Fixed long file name support. Is now based off PATH_MAX instea...
[fw/sdcc] / src / z80 / gen.c
index d5009fd0f39f347ed547dd3dd4f10a91b11f793d..6f033efe1c4b090a37c011b3f64911f5eec8f23e 100644 (file)
@@ -1,6 +1,33 @@
 /*-------------------------------------------------------------------------
   gen.c - Z80 specific code generator.
+     
+  Michael Hope <michaelh@juju.net.nz> 2000
+  Based on the mcs51 generator -
+      Sandeep Dutta . sandeep.dutta@usa.net (1998)
+   and -  Jean-Louis VERN.jlvern@writeme.com (1999)
 
+  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.
+
+  In other words, you are welcome to use, share and improve this program.
+  You are forbidden to forbid anyone else to use, share and improve
+  what you give them.   Help stamp out software-hoarding!
+
+-------------------------------------------------------------------------*/
+
+/*
   Benchmarks on dhry.c 2.1 with 32766 loops and a 10ms clock:
                                        ticks dhry  size
   Base with asm strcpy / strcmp / memcpy: 23198 141 1A14
   4. Optimised strcmp fun              21999 149 2294
   5. Optimised strcmp further          21660 151 228C
   6. Optimised memcpy by unroling      20885 157 2201
+  7. After turning loop induction on   19862 165 236D
+  8. Same as 7 but with more info      
+  9. With asm optimised strings                17030 192 2223
 
-  Michael Hope <michaelh@juju.net.nz> 2000
-  Based on the mcs51 generator -
-      Sandeep Dutta . sandeep.dutta@usa.net (1998)
-   and -  Jean-Louis VERN.jlvern@writeme.com (1999)
-
-  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.
-
-  In other words, you are welcome to use, share and improve this program.
-  You are forbidden to forbid anyone else to use, share and improve
-  what you give them.   Help stamp out software-hoarding!
-
--------------------------------------------------------------------------*/
+  10 and below are with asm strings off.
+  
+  Apparent advantage of turning on regparams:
+  1.  Cost of push
+        Decent case is push of a constant 
+          - ld hl,#n; push hl: (10+11)*nargs
+  2.  Cost of pull from stack
+        Using asm with ld hl, etc
+          - ld hl,#2; add hl,sp; (ld bc,(hl); hl+=2)*nargs
+            10+11+(7+6+7+6)*nargs
+  3.  Cost of fixing stack
+          - pop hl*nargs
+            10*nargs
+  
+  So cost is (10+11+7+6+7+10)*nargs+10+11 
+      = 51*nargs+21
+      = 123 for mul, div, strcmp, strcpy
+  Saving of (98298+32766+32766+32766)*123 = 24181308
+  At 192 d/s for 682411768t, speed up to 199.  Hmm.
+*/
 
 #include <stdio.h>
 #include <stdlib.h>
 */
 
 enum {
-  DISABLE_DEBUG = 1
+  DISABLE_DEBUG = 0
 };
 
 static char *_z80_return[] =
@@ -2723,7 +2748,7 @@ genPlus (iCode * ic)
   if (genPlusIncr (ic) == TRUE)
     goto release;
 
-  emitDebug ("; genPlusIncr failed");
+  emitDebug ("; Can't optimise plus by inc, falling back to the normal way");
 
   size = getDataSize (IC_RESULT (ic));
 
@@ -2731,10 +2756,11 @@ genPlus (iCode * ic)
   if (isPair (AOP (IC_RESULT (ic))))
     {
       char *left, *right;
-
       left = aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE);
       right = aopGetLitWordLong (AOP (IC_RIGHT (ic)), 0, FALSE);
-      if (left && right)
+
+      if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT &&
+          left && right)
        {
          /* It's a pair */
          /* PENDING: fix */
@@ -2816,7 +2842,7 @@ genPlus (iCode * ic)
            }
          else if (size == 4)
            {
-             emitDebug ("; WARNING: This add is probably broken.\n");
+             wassertl (0, "Hit bad case for add");
            }
        }
     }
@@ -5566,6 +5592,246 @@ genReceive (iCode * ic)
   freeAsmop (IC_RESULT (ic), NULL, ic);
 }
 
+enum
+  {
+    /** Maximum number of bytes to emit per line. */
+    DBEMIT_MAX_RUN = 8
+  };
+
+/** Context for the byte output chunker. */
+typedef struct
+{
+  unsigned char buffer[DBEMIT_MAX_RUN];
+  int pos;
+} DBEMITCTX;
+
+
+/** Flushes a byte chunker by writing out all in the buffer and
+    reseting. 
+*/
+static void
+_dbFlush(DBEMITCTX *self)
+{
+  char line[256];
+
+  if (self->pos > 0)
+    {
+      int i;
+      sprintf(line, ".db 0x%02X", self->buffer[0]);
+
+      for (i = 1; i < self->pos; i++)
+        {
+          sprintf(line + strlen(line), ", 0x%02X", self->buffer[i]);
+        }
+      emit2(line);
+    }
+  self->pos = 0;
+}
+
+/** Write out another byte, buffering until a decent line is
+    generated.
+*/
+static void
+_dbEmit(DBEMITCTX *self, int c)
+{
+  if (self->pos == DBEMIT_MAX_RUN)
+    {
+      _dbFlush(self);
+    }
+  self->buffer[self->pos++] = c;
+}
+
+/** Context for a simple run length encoder. */
+typedef struct
+{
+  unsigned last;
+  unsigned char buffer[128];
+  int pos;
+  /** runLen may be equivalent to pos. */
+  int runLen;
+} RLECTX;
+
+enum
+  {
+    RLE_CHANGE_COST = 4,
+    RLE_MAX_BLOCK = 127
+  };
+
+/** Flush the buffer of a run length encoder by writing out the run or
+    data that it currently contains.
+*/
+static void
+_rleCommit(RLECTX *self)
+{
+  int i;
+  if (self->pos != 0)
+    {
+      DBEMITCTX db;
+      memset(&db, 0, sizeof(db));
+          
+      emit2(".db %u", self->pos);
+      
+      for (i = 0; i < self->pos; i++)
+        {
+          _dbEmit(&db, self->buffer[i]);
+        }
+      _dbFlush(&db);
+    }
+  /* Reset */
+  self->pos = 0;
+}
+
+/* Encoder design:
+   Can get either a run or a block of random stuff.
+   Only want to change state if a good run comes in or a run ends.
+   Detecting run end is easy.
+   Initial state?
+
+   Say initial state is in run, len zero, last zero.  Then if you get a
+   few zeros then something else then a short run will be output.
+   Seems OK.  While in run mode, keep counting.  While in random mode,
+   keep a count of the run.  If run hits margin, output all up to run,
+   restart, enter run mode.
+*/
+
+/** Add another byte into the run length encoder, flushing as
+    required.  The run length encoder uses the Amiga IFF style, where
+    a block is prefixed by its run length.  A positive length means
+    the next n bytes pass straight through.  A negative length means
+    that the next byte is repeated -n times.  A zero terminates the
+    chunks.
+*/
+static void
+_rleAppend(RLECTX *self, int c)
+{
+  int i;
+
+  if (c != self->last)
+    {
+      /* The run has stopped.  See if it is worthwhile writing it out
+         as a run.  Note that the random data comes in as runs of
+         length one.
+      */
+      if (self->runLen > RLE_CHANGE_COST)
+        {
+          /* Yes, worthwhile. */
+          /* Commit whatever was in the buffer. */
+          _rleCommit(self);
+          emit2(".db -%u,0x%02X", self->runLen, self->last);
+        }
+      else
+        {
+          /* Not worthwhile.  Append to the end of the random list. */
+          for (i = 0; i < self->runLen; i++)
+            {
+              if (self->pos >= RLE_MAX_BLOCK)
+                {
+                  /* Commit. */
+                  _rleCommit(self);
+                }
+              self->buffer[self->pos++] = self->last;
+            }
+        }
+      self->runLen = 1;
+      self->last = c;
+    }
+  else
+    {
+      if (self->runLen >= RLE_MAX_BLOCK)
+        {
+          /* Commit whatever was in the buffer. */
+          _rleCommit(self);
+
+          emit2 (".db -%u,0x%02X", self->runLen, self->last);
+          self->runLen = 0;
+        }
+      self->runLen++;
+    }
+}
+
+static void
+_rleFlush(RLECTX *self)
+{
+  _rleAppend(self, -1);
+  _rleCommit(self);
+  self->pos = 0;
+  self->last = 0;
+  self->runLen = 0;
+}
+
+/** genArrayInit - Special code for initialising an array with constant
+   data.
+*/
+static void
+genArrayInit (iCode * ic)
+{
+  literalList *iLoop;
+  int         ix;
+  int         elementSize = 0, eIndex, i;
+  unsigned    val, lastVal;
+  sym_link    *type;
+  RLECTX      rle;
+
+  memset(&rle, 0, sizeof(rle));
+
+  aopOp (IC_LEFT(ic), ic, FALSE, FALSE);
+
+  if (AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD)
+    {
+      /* Emit the support function call and the destination address. */
+      emit2("call __initrleblock");
+      emit2(".dw %s", aopGetWord (AOP(IC_LEFT(ic)), 0));
+    }
+  else
+    {
+      wassertl (0, "Unexpected operand to genArrayInit.\n");
+    }
+    
+  type = operandType(IC_LEFT(ic));
+    
+  if (type && type->next)
+    {
+      elementSize = getSize(type->next);
+    }
+  else
+    {
+      wassertl (0, "Can't determine element size in genArrayInit.");
+    }
+
+  iLoop = IC_ARRAYILIST(ic);
+  lastVal = (unsigned)-1;
+
+  /* Feed all the bytes into the run length encoder which will handle
+     the actual output.
+     This works well for mixed char data, and for random int and long
+     data.
+  */
+  while (iLoop)
+    {
+      ix = iLoop->count;
+
+      if (ix != 0)
+        {
+          for (i = 0; i < ix; i++)
+            {
+              for (eIndex = 0; eIndex < elementSize; eIndex++)
+                {
+                  val = (((int)iLoop->literalValue) >> (eIndex * 8)) & 0xff;
+                  _rleAppend(&rle, val);
+                }
+            }
+       }
+       
+      iLoop = iLoop->next;
+    }
+
+  _rleFlush(&rle);
+  /* Mark the end of the run. */
+  emit2(".db 0");
+
+  freeAsmop (IC_LEFT(ic), NULL, ic);
+}
+
 /*-----------------------------------------------------------------*/
 /* genZ80Code - generate code for Z80 based controllers            */
 /*-----------------------------------------------------------------*/
@@ -5837,6 +6103,10 @@ genZ80Code (iCode * lic)
          addSet (&_G.sendSet, ic);
          break;
 
+       case ARRAYINIT:
+           genArrayInit(ic);
+           break;
+           
        default:
          ic = ic;
          /*      piCode(ic,stdout); */