/*-------------------------------------------------------------------------
  SDCCdwarf2.c - generate DWARF2 debug information

             Written By -  Erik Petrich . epetrich@users.sourceforge.net (2004)

   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!
-------------------------------------------------------------------------*/


#include "common.h"
#include "SDCCdwarf2.h"

/*************************************************************
 *
 *
 *
 *
 *************************************************************/

extern set *includeDirsSet;

int dwOpenFile (const char *file);
int dwCloseFile (void);
int dwWriteFunction (symbol *pSym, iCode *ic);
int dwWriteEndFunction (symbol *pSym, iCode *ic, int offset);
int dwWriteLabel (symbol *pSym, const iCode *ic);
int dwWriteScope (iCode *ic);
int dwWriteSymbol (symbol *pSym);
int dwWriteType (structdef *sdef, int block, int inStruct, const char *tag);
int dwWriteModule (const char *name);
int dwWriteCLine (iCode *ic);
int dwWriteALine (const char *module, int Line);
int dwWriteFrameAddress (const char *variable, struct reg_info *reg, int offset);
int dwWriteBasicSymbol (symbol *sym, int isStructSym, int isFunc);
     

DEBUGFILE dwarf2DebugFile = 
  {
    &dwOpenFile,
    &dwCloseFile,
    &dwWriteModule,
    &dwWriteFunction,
    &dwWriteEndFunction,
    &dwWriteLabel,
    &dwWriteScope,
    &dwWriteSymbol,
    &dwWriteType,
    &dwWriteCLine,
    &dwWriteALine,
    &dwWriteFrameAddress
  };

FILE *dwarf2FilePtr = NULL;
char *dwModuleName = NULL;
dwtag *dwRootTag = NULL;
dwtag *dwFuncTag = NULL;
dwtag *dwScopeTag = NULL;
hTab * dwAbbrevTable;
int dwAbbrevNum = 0;
hTab * dwTypeTagTable;
int dwRefNum = 0;
int dwScopeBlock = 0;
long dwScopeLevel = 0;
int dwDebugSymbol = 0;
dwcfins * dwCIEins = NULL;
dwlocregion * dwFrameLastLoc = NULL;
dwloclist * dwRootLocList = NULL;
dwloclist * dwFrameLocList = NULL;
int dwLineBase = -5;
int dwLineRange = 15;
int dwLineOpcodeBase = 10;
set * dwFilenameSet = NULL;
dwline * dwLineFirst = NULL;
dwline * dwLineLast = NULL;
dwlocregion * dwCFILastLoc = NULL;
dwcfilist * dwCFIRoot = NULL;


/*----------------------------------------------------------------------*/
/* dwNewDebugSymbol - returns the name for a new debug symbol           */
/*----------------------------------------------------------------------*/
static char *
dwNewDebugSymbol (void)
{
  char debugSym[SDCC_NAME_MAX];
        
  sprintf (debugSym, "S%s$%s$%d", dwModuleName, currFunc->name, dwDebugSymbol);
  dwDebugSymbol++;
  return Safe_strdup (debugSym);
}


/*----------------------------------------------------------------------*/
/* dwWriteByte - generate a single byte assembler constant in the form: */
/*                                                                      */
/*       .db label+offset  ; comment                                    */
/*                                                                      */
/* The label and comment parameters are optional                        */
/*----------------------------------------------------------------------*/
static void
dwWriteByte (const char * label, int offset, const char * comment)
{
  tfprintf (dwarf2FilePtr, "\t!db\t");
  if (label)
    {
      if (offset)
        fprintf (dwarf2FilePtr, "%s+%d", label, offset);
      else
        fprintf (dwarf2FilePtr, "%s", label);
    }
  else
    fprintf (dwarf2FilePtr, "%d", offset);
  
  if (comment)
    fprintf (dwarf2FilePtr, "\t;%s\n", comment);
  else
    fprintf (dwarf2FilePtr, "\n");
}

/*----------------------------------------------------------------------*/
/* dwWriteHalf - generate a two byte assembler constant in the form:    */
/*                                                                      */
/*       .dw label+offset  ; comment                                    */
/*                                                                      */
/* The label and comment parameters are optional                        */
/*----------------------------------------------------------------------*/
static void
dwWriteHalf (const char * label, int offset, char * comment)
{
  tfprintf (dwarf2FilePtr, "\t!dw\t");
  if (label)
    {
      if (offset)
        fprintf (dwarf2FilePtr, "%s+%d", label, offset);
      else
        fprintf (dwarf2FilePtr, "%s", label);
    }
  else
    fprintf (dwarf2FilePtr, "%d", offset);
  
  if (comment)
    fprintf (dwarf2FilePtr, "\t;%s\n", comment);
  else
    fprintf (dwarf2FilePtr, "\n");
}

/*----------------------------------------------------------------------*/
/* dwWriteWord - generate a four byte assembler constant in the form:   */
/*                                                                      */
/*       .dd label+offset  ; comment                                    */
/*                                                                      */
/* The label and comment parameters are optional                        */
/*----------------------------------------------------------------------*/
static void
dwWriteWord (const char * label, int offset, char * comment)
{
  /* FIXME: need to implement !dd pseudo-op in the assembler. In the */
  /* meantime, we use dw with zero padding and hope the values fit  */
  /* in only 16 bits.                                               */
#if 0
  tfprintf (dwarf2FilePtr, "\t!dd\t");
  if (label)
    {
      if (offset)
        fprintf (dwarf2FilePtr, "%s+%d", label, offset);
      else
        fprintf (dwarf2FilePtr, "%s", label);
    }
  else
    fprintf (dwarf2FilePtr, "%d", offset);
#else
  tfprintf (dwarf2FilePtr, "\t!dw\t");
  if (port->little_endian)
    {
      if (label)
        {
          if (offset)
            fprintf (dwarf2FilePtr, "(%s+%d),0", label, offset);
          else
            fprintf (dwarf2FilePtr, "(%s),0", label);
        }
      else
        fprintf (dwarf2FilePtr, "%d,%d", offset, offset >> 16);
    }
  else
    {
      if (label)
        {
          if (offset)
            fprintf (dwarf2FilePtr, "0,(%s+%d)", label, offset);
          else
            fprintf (dwarf2FilePtr, "0,(%s)", label);
        }
      else
        fprintf (dwarf2FilePtr, "%d,%d", offset >> 16, offset);
    }
#endif
  
  if (comment)
    fprintf (dwarf2FilePtr, "\t;%s\n", comment);
  else
    fprintf (dwarf2FilePtr, "\n");
}


/*----------------------------------------------------------------------*/
/* dwWriteULEB128 - generate an unsigned variable length assembler      */
/*                  constant in the form:                               */
/*                                                                      */
/*       .uleb128 label+offset  ; comment                               */
/*                                                                      */
/* The label and comment parameters are optional                        */
/*----------------------------------------------------------------------*/
static void
dwWriteULEB128 (char * label, int offset, char * comment)
{
  tfprintf (dwarf2FilePtr, "\t.uleb128\t");
  if (label)
    {
      if (offset)
        fprintf (dwarf2FilePtr, "%s+%d", label, offset);
      else
        fprintf (dwarf2FilePtr, "%s", label);
    }
  else
    fprintf (dwarf2FilePtr, "%d", offset);
  
  if (comment)
    fprintf (dwarf2FilePtr, "\t;%s\n", comment);
  else
    fprintf (dwarf2FilePtr, "\n");
}

/*----------------------------------------------------------------------*/
/* dwWriteSLEB128 - generate a signed variable length assembler         */
/*                  constant in the form:                               */
/*                                                                      */
/*       .sleb128 label+offset  ; comment                               */
/*                                                                      */
/* The label and comment parameters are optional                        */
/*----------------------------------------------------------------------*/
static void
dwWriteSLEB128 (char * label, int offset, char * comment)
{
  tfprintf (dwarf2FilePtr, "\t.sleb128\t");
  if (label)
    {
      if (offset)
        fprintf (dwarf2FilePtr, "%s+%d", label, offset);
      else
        fprintf (dwarf2FilePtr, "%s", label);
    }
  else
    fprintf (dwarf2FilePtr, "%d", offset);
  
  if (comment)
    fprintf (dwarf2FilePtr, "\t;%s\n", comment);
  else
    fprintf (dwarf2FilePtr, "\n");
}


/*----------------------------------------------------------------------*/
/* dwSizeofULEB128 - return the size (in bytes) of an unsigned variable */
/*                   length constant                                    */
/*----------------------------------------------------------------------*/
static int
dwSizeofULEB128 (int unsigned value)
{
  int size = 0;
  
  do
    {
      value >>= 7;
      size++;
    }
  while (value);
  
  return size;
}

/*----------------------------------------------------------------------*/
/* dwSizeofSLEB128 - return the size (in bytes) of a signed variable    */
/*                   length constant                                    */
/*----------------------------------------------------------------------*/
static int
dwSizeofSLEB128 (int value)
{
  int size = 0;
  int negative = (value < 0);
  int sign;
  
  while (1)
    {
      size++;
      sign = value & 0x40;
      value >>= 7;
      if (negative)
        value |= (0x7f << (sizeof(int)*8 - 7));
      if ((value == 0 && !sign) || (value == -1 && sign))
        break;
    }
  
  return size;
}

/*----------------------------------------------------------------------*/
/* dwWriteString - generate a string constant in the form:              */
/*                                                                      */
/*       .ascii /string/  ; comment                                     */
/*                                                                      */
/* The comment parameter is optional. The string may contain any        */
/* non-null characters                                                  */
/*----------------------------------------------------------------------*/
static void
dwWriteString (const char * string, const char * comment)
{
  char * escaped = string_escape (string);
  tfprintf (dwarf2FilePtr, "\t!ascii\n", escaped);
  Safe_free (escaped);
  dwWriteByte (NULL, 0, comment);
}


/*----------------------------------------------------------------------*/
/* dwWriteAddress - generate an assembler constant in the form:         */
/*                                                                      */
/*       .dw label+offset  ; comment                                    */
/* or    .dd label+offset  ; comment                                    */
/*                                                                      */
/* depending on how the relevant ABI defines the address size (may be   */
/* larger than the CPU's actual address size). The label and comment    */
/* parameters are optional                                              */
/*----------------------------------------------------------------------*/
static void
dwWriteAddress (const char * label, int offset, char * comment)
{
  switch (port->debugger.dwarf.addressSize)
    {
    case 2:
      dwWriteHalf (label, offset, comment);
      break;
    case 4:
      dwWriteWord (label, offset, comment);
      break;
    default:
      werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
              "unsupported port->debugger.dwarf.addressSize");
    }
}


/*----------------------------------------------------------------------*/
/* dwWriteHalfDelta - generate a two byte assembler constant in the     */
/*                    form:                                             */
/*                                                                      */
/*       .dw offset+label1-label2                                       */
/*                                                                      */
/* The offset parameter is optional                                     */
/*----------------------------------------------------------------------*/
static void
dwWriteHalfDelta (char * label1, char * label2, int offset)
{
  if (offset)
    tfprintf (dwarf2FilePtr, "\t!dw\t%d+%s-%s\n", offset, label1, label2);
  else
    tfprintf (dwarf2FilePtr, "\t!dw\t%s-%s\n", label1, label2);
}

/*----------------------------------------------------------------------*/
/* dwWriteWordDelta - generate a four byte assembler constant in the    */
/*                    form:                                             */
/*                                                                      */
/*       .dd label1-label2                                              */
/*----------------------------------------------------------------------*/
static void 
dwWriteWordDelta (char * label1, char * label2)
{
  /* FIXME: need to implement !dd pseudo-op; this hack only */
  /* works for positive offsets of less than 64k            */
#if 0
  tfprintf (dwarf2FilePtr, "\t!dd\t%s-%s\n", label1,label2);
#else
  if (port->little_endian)
    {
      tfprintf (dwarf2FilePtr, "\t!dw\t%s-%s,%d\n", label1, label2, 0);
    }
  else
    {
      tfprintf (dwarf2FilePtr, "\t!dw\t%d,%s-%s\n", 0, label1, label2);
    }
#endif
}


/*----------------------------------------------------------------------*/
/* dwWriteAddressDelta - generate an assembler constant in the form:    */
/*                                                                      */
/*       .dw label1-label2                                              */
/* or    .dd label1-label2                                              */
/*                                                                      */
/* depending on how the relevant ABI defines the address size (may be   */
/* larger than the CPU's actual address size)                           */
/*----------------------------------------------------------------------*/
static void
dwWriteAddressDelta (char * label1, char * label2)
{
  switch (port->debugger.dwarf.addressSize)
    {
    case 2:
      dwWriteHalfDelta (label1, label2, 0);
      break;
    case 4:
      dwWriteWordDelta (label1, label2);
      break;
    default:
      werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
              "unsupported port->debugger.dwarf.addressSize");
    }
}

#if 0
/*----------------------------------------------------------------------*/
/* dwWriteULEB128Delta - generate an unsigned variable byte assembler   */
/*                       constant in the form:                          */
/*                                                                      */
/*       .uleb128 offset+label1-label2                                  */
/*                                                                      */
/* The offset parameter is optional                                     */
/*----------------------------------------------------------------------*/
static void
dwWriteULEB128Delta (char * label1, char * label2, int offset)
{
  if (offset)
    tfprintf (dwarf2FilePtr, "\t.uleb128\t%d+%s-%s\n", offset, label1, label2);
  else
    tfprintf (dwarf2FilePtr, "\t.uleb128\t%s-%s\n", label1, label2);
}
#endif

/*------------------------------------------------------------------------*/

/*----------------------------------------------------------------------*/
/* dwNewCFIlist - allocates a new CFI list node                         */
/*----------------------------------------------------------------------*/
dwcfilist *
dwNewCFIlist ()
{
  dwcfilist * p;
  
  p = Safe_alloc (sizeof (dwcfilist));
  
  return p;
}

/*----------------------------------------------------------------------*/
/* dwNewLoc - allocates a new location expression node                  */
/*----------------------------------------------------------------------*/
dwloc *
dwNewLoc (int opcode, const char * label, int offset)
{
  dwloc * lp;
  
  lp = Safe_alloc (sizeof (dwloc));
  
  lp->opcode = opcode;
  lp->operand.label = label;
  lp->operand.offset = offset;

  return lp;
}

/*-------------------------------------------------------------------------*/
/* dwSizeofLoc - returns the size (in bytes) of a chain of location        */
/*               expression nodes as they would be encoded by dwWriteLoc() */
/*-------------------------------------------------------------------------*/
static int
dwSizeofLoc (dwloc * lp)
{
  int size = 0;
  
  while (lp)
    {
      size++;
      switch (lp->opcode)
        {
        case DW_OP_addr:
          size += port->debugger.dwarf.addressSize;
          break;
          
        case DW_OP_deref_size:
        case DW_OP_xderef_size:
        case DW_OP_pick:
        case DW_OP_const1u:
        case DW_OP_const1s:
          size += 1;
          break;
        
        case DW_OP_skip:
        case DW_OP_bra:
        case DW_OP_const2u:
        case DW_OP_const2s:
          size += 2;
          break;
        
        case DW_OP_const4u:
        case DW_OP_const4s:
          size += 4;
          break;
        
        case DW_OP_const8u:
        case DW_OP_const8s:
          size += 8;
          break;

        case DW_OP_piece:
        case DW_OP_regx:
        case DW_OP_plus_uconst:
          size += dwSizeofULEB128 (lp->operand.offset);
          break;
        
        case DW_OP_breg0:
        case DW_OP_breg1:
        case DW_OP_breg2:
        case DW_OP_breg3:
        case DW_OP_breg4:
        case DW_OP_breg5:
        case DW_OP_breg6:
        case DW_OP_breg7:
        case DW_OP_breg8:
        case DW_OP_breg9:
        case DW_OP_breg10:
        case DW_OP_breg11:
        case DW_OP_breg12:
        case DW_OP_breg13:
        case DW_OP_breg14:
        case DW_OP_breg15:
        case DW_OP_breg16:
        case DW_OP_breg17:
        case DW_OP_breg18:
        case DW_OP_breg19:
        case DW_OP_breg20:
        case DW_OP_breg21:
        case DW_OP_breg22:
        case DW_OP_breg23:
        case DW_OP_breg24:
        case DW_OP_breg25:
        case DW_OP_breg26:
        case DW_OP_breg27:
        case DW_OP_breg28:
        case DW_OP_breg29:
        case DW_OP_breg30:
        case DW_OP_breg31:
        case DW_OP_fbreg:
          size += dwSizeofSLEB128 (lp->operand.offset);
          break;
        }
      
      lp = lp->next;
    }
  
  return size;
}

/*------------------------------------------------------------------------*/
/* dwWriteLoc - writes a chain of location expression nodes               */
/*------------------------------------------------------------------------*/
static void
dwWriteLoc (dwloc *lp)
{
  while (lp)
    {
      dwWriteByte (NULL, lp->opcode, NULL);
      switch (lp->opcode)
        {
        case DW_OP_addr:
          dwWriteAddress (lp->operand.label, lp->operand.offset, NULL);
          break;
          
        case DW_OP_deref_size:
        case DW_OP_xderef_size:
        case DW_OP_pick:
        case DW_OP_const1u:
        case DW_OP_const1s:
          dwWriteByte (NULL, lp->operand.offset, NULL);
          break;
        
        case DW_OP_skip:
        case DW_OP_bra:
        case DW_OP_const2u:
        case DW_OP_const2s:
          dwWriteHalf (NULL, lp->operand.offset, NULL);
          break;
        
        case DW_OP_const4u:
        case DW_OP_const4s:
          dwWriteWord (NULL, lp->operand.offset, NULL);
          break;
        
        case DW_OP_piece:
        case DW_OP_regx:
        case DW_OP_plus_uconst:
          dwWriteULEB128 (NULL, lp->operand.offset, NULL);
          break;
        
        case DW_OP_breg0:
        case DW_OP_breg1:
        case DW_OP_breg2:
        case DW_OP_breg3:
        case DW_OP_breg4:
        case DW_OP_breg5:
        case DW_OP_breg6:
        case DW_OP_breg7:
        case DW_OP_breg8:
        case DW_OP_breg9:
        case DW_OP_breg10:
        case DW_OP_breg11:
        case DW_OP_breg12:
        case DW_OP_breg13:
        case DW_OP_breg14:
        case DW_OP_breg15:
        case DW_OP_breg16:
        case DW_OP_breg17:
        case DW_OP_breg18:
        case DW_OP_breg19:
        case DW_OP_breg20:
        case DW_OP_breg21:
        case DW_OP_breg22:
        case DW_OP_breg23:
        case DW_OP_breg24:
        case DW_OP_breg25:
        case DW_OP_breg26:
        case DW_OP_breg27:
        case DW_OP_breg28:
        case DW_OP_breg29:
        case DW_OP_breg30:
        case DW_OP_breg31:
        case DW_OP_fbreg:
          dwWriteSLEB128 (NULL, lp->operand.offset, NULL);
          break;
        }
        
      lp = lp->next;
    }
}

/*----------------------------------------------------------------------*/
/* dwNewLocList - allocates a new list of location expression node      */
/*----------------------------------------------------------------------*/
static dwloclist *
dwNewLocList (void)
{
  dwloclist * llp;
  
  llp = Safe_alloc (sizeof (dwloclist));
  
  return llp;
}

/*----------------------------------------------------------------------*/
/* dwSizeofLocRegion - returns the size (in bytes) of a chain of        */
/*                     location regions (inluding their location        */
/*                     expression nodes) as encoded by dwWriteLocLists  */
/*----------------------------------------------------------------------*/
static int
dwSizeofLocRegion (dwlocregion * lrp)
{
  int size = 0;
  
  while (lrp)
    {
      size += 2 * port->debugger.dwarf.addressSize;
      size += 2 + dwSizeofLoc (lrp->loc);
      lrp = lrp->next;
    }
  
  size += 2 * port->debugger.dwarf.addressSize;
  return size;
}

/*-----------------------------------------------------------------------*/
/* dwAssignLocListAddresses - assign the address offsets of the location */
/*                            lists so that they can be referenced from  */
/*                            the tag structure                          */
/*-----------------------------------------------------------------------*/
static void
dwAssignLocListAddresses (void)
{
  dwloclist * llp;
  int address = 0;

  llp = dwRootLocList;
  while (llp)
    {
      llp->baseOffset = address;
      address += dwSizeofLocRegion (llp->region);

      llp = llp->next;
    }
}

/*-----------------------------------------------------------------------*/
/* dwWriteLocLists - write all of the location lists in dwRootLocList to */
/*                   the .debug_loc section                              */
/*-----------------------------------------------------------------------*/
static void
dwWriteLocLists (void)
{
  dwlocregion * lrp;
  dwloclist * llp;

  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_loc (NOLOAD)");
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_loc_start");

  llp = dwRootLocList;
  while (llp)
    {
      //fprintf (dwarf2FilePtr, "; baseOffset = 0x%x\n", llp->baseOffset);
      lrp = llp->region;
      while (lrp)
        {
          dwWriteAddress (lrp->startLabel, 0, NULL);
          dwWriteAddress (lrp->endLabel, 0, NULL);
          dwWriteHalf (NULL, dwSizeofLoc (lrp->loc), NULL);
          dwWriteLoc (lrp->loc);
          lrp = lrp ->next;
        }

      dwWriteAddress (NULL, 0, NULL);
      dwWriteAddress (NULL, 0, NULL);

      llp = llp->next;
    }
    
}


/*------------------------------------------------------------------------*/


/*----------------------------------------------------------------------*/
/* dwNewAttr - allocate a new tag attribute node                        */
/*----------------------------------------------------------------------*/
static dwattr *
dwNewAttr (int attr)
{
  dwattr * ap;
  
  ap = Safe_alloc ( sizeof (dwattr));
  ap->attr = attr;
  
  return ap;
}

/*----------------------------------------------------------------------*/
/* dwFreeAttr - deallocate a tag attribute node                         */
/*----------------------------------------------------------------------*/
static void
dwFreeAttr (dwattr * ap)
{
  Safe_free (ap);
}


/*-------------------------------------------------------------------------*/
/* dwNewAttrString - allocate a new tag attribute node with a string value */
/*-------------------------------------------------------------------------*/
static dwattr *
dwNewAttrString (int attr, const char * string)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_string;
  ap->val.string = string;
  return ap;
}


/*---------------------------------------------------------------------*/
/* dwNewAttrConst - allocate a new tag attribute node with an unsigned */
/*                  numeric constant value                             */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrConst (int attr, unsigned int data)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  if (data <= 0xffu)
    ap->form = DW_FORM_data1;
  else if (data <= 0xffffu)
    ap->form = DW_FORM_data2;
  else
    ap->form = DW_FORM_data4;
  
  ap->val.data = data;
  return ap;
}

/* disabled to eliminiate unused function warning */
#if 0
/*---------------------------------------------------------------------*/
/* dwNewAttrSignedConst - allocate a new tag attribute node with a     */
/*                        signed numeric constant value                */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrSignedConst (int attr, int data)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  if (data <= 0x7f && data >= -0x80)
    ap->form = DW_FORM_data1;
  else if (data <= 0xffff && data >= -0x8000)
    ap->form = DW_FORM_data2;
  else
    ap->form = DW_FORM_data4;
  
  ap->val.data = data;
  return ap;
}
#endif

/*---------------------------------------------------------------------*/
/* dwNewAttrFlag - allocate a new tag attribute node with a boolean    */
/*                 flag value (zero/non-zero)                          */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrFlag (int attr, int data)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_flag;
  
  ap->val.data = data;
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwNewAttrAddrSymbol - allocate a new tag attribute node with the    */
/*                       address of a C symbol plus an offset          */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrAddrSymbol (int attr, symbol * sym, int offset)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_addr;
  
  ap->val.symaddr.label = sym->rname;
  ap->val.symaddr.offset = offset;
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwNewAttrAddrLabel - allocate a new tag attribute node with the     */
/*                      address of an assembler label plus an offset   */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrAddrLabel (int attr, char * label, int offset)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_addr;
  
  ap->val.symaddr.label = label;
  ap->val.symaddr.offset = offset;
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwNewAttrTagRef - allocate a new tag attribute node that references */
/*                   a tag node                                        */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrTagRef (int attr, dwtag * tp)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_ref4;
  
  ap->val.ref = tp;
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwNewAttrLocRef - allocate a new tag attribute node that references */
/*                   a location list                                   */
/*---------------------------------------------------------------------*/
static dwattr *
dwNewAttrLocRef (int attr, dwloclist * llp)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_data4;
  
  ap->val.loclist = llp;
  return ap;
}

/*-----------------------------------------------------------------------*/
/* dwNewAttrLabelRef - allocate a new tag attribute node that references */
/*                     the address of an assembler label plus an offset  */
/*-----------------------------------------------------------------------*/
static dwattr *
dwNewAttrLabelRef (int attr, char * label, int offset)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_data4;
  
  ap->val.symaddr.label = label;
  ap->val.symaddr.offset = offset;
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwNewAttrLoc - allocate a new tag attribute node for a chain of     */
/*                location expression nodes                            */
/*---------------------------------------------------------------------*/
dwattr *
dwNewAttrLoc (int attr, dwloc * lp)
{
  dwattr * ap;
  
  ap = dwNewAttr (attr);
  ap->form = DW_FORM_block1;
  ap->val.loc = lp;
  
  return ap;
}

/*---------------------------------------------------------------------*/
/* dwWriteAttr - write a tag attribute node                            */
/*---------------------------------------------------------------------*/
static void
dwWriteAttr (dwattr * ap)
{
  
  switch (ap->form)
    {
      case DW_FORM_addr:
        dwWriteAddress (ap->val.symaddr.label, ap->val.symaddr.offset, NULL);
        break;
      
      case DW_FORM_block:
        dwWriteULEB128 (NULL, dwSizeofLoc (ap->val.loc), NULL);
        dwWriteLoc (ap->val.loc);
        break;
      
      case DW_FORM_block1:
        dwWriteByte (NULL, dwSizeofLoc (ap->val.loc), NULL);
        dwWriteLoc (ap->val.loc);
        break;
      
      case DW_FORM_block2:
        dwWriteHalf (NULL, dwSizeofLoc (ap->val.loc), NULL);
        dwWriteLoc (ap->val.loc);
        break;
      
      case DW_FORM_block4:
        dwWriteWord (NULL, dwSizeofLoc (ap->val.loc), NULL);
        dwWriteLoc (ap->val.loc);
        break;
      
      case DW_FORM_data1:
      case DW_FORM_flag:
        dwWriteByte (NULL, ap->val.data, NULL);
        break;
      
      case DW_FORM_data2:
        dwWriteHalf (NULL, ap->val.data, NULL);
        break;
      
      case DW_FORM_data4:
        switch (ap->attr)
          {
          case DW_AT_stmt_list:
            dwWriteWord (ap->val.symaddr.label, ap->val.symaddr.offset, NULL);
            break;
          case DW_AT_location:
          case DW_AT_frame_base:
            dwWriteWord ("Ldebug_loc_start", ap->val.loclist->baseOffset, NULL);
            break;
          default:
            dwWriteWord (NULL, ap->val.data, NULL);
          }
        break;
      
      case DW_FORM_udata:
        dwWriteULEB128 (NULL, ap->val.data, NULL);
        break;
      
      case DW_FORM_sdata:
        dwWriteSLEB128 (NULL, ap->val.data, NULL);
        break;

      case DW_FORM_string:
        dwWriteString (ap->val.string, NULL);
        break;
      
      case DW_FORM_ref1:
        dwWriteByte (NULL, ap->val.ref->baseOffset, NULL);
        break;
      
      case DW_FORM_ref2:
        dwWriteHalf (NULL, ap->val.ref->baseOffset, NULL);
        break;
      
      case DW_FORM_ref4:
        dwWriteWord (NULL, ap->val.ref->baseOffset, NULL);
        break;
        
      default:
        werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
                "unsupported DWARF form");
        exit (1);
    }
}

/*---------------------------------------------------------------------*/
/* dwSizeofAttr - returns the size (in bytes) of a tag attribute node  */
/*                as encoded by dwWriteAttr                            */
/*---------------------------------------------------------------------*/
static int
dwSizeofAttr (dwattr * ap)
{
  int size;
  
  switch (ap->form)
    {
      case DW_FORM_addr:
        return port->debugger.dwarf.addressSize;
      
      case DW_FORM_block:
        size = dwSizeofLoc (ap->val.loc);
        return size + dwSizeofULEB128 (size);
      
      case DW_FORM_block1:
        size = dwSizeofLoc (ap->val.loc);
        return size + 1;
      
      case DW_FORM_block2:
        size = dwSizeofLoc (ap->val.loc);
        return size + 2;
      
      case DW_FORM_block4:
        size = dwSizeofLoc (ap->val.loc);
        return size + 4;
      
      case DW_FORM_data1:
      case DW_FORM_flag:
        return 1;
      
      case DW_FORM_data2:
        return 2;
      
      case DW_FORM_data4:
        return 4;
      
      case DW_FORM_udata:
        return dwSizeofULEB128 (ap->val.data);
      
      case DW_FORM_sdata:
        return dwSizeofSLEB128 (ap->val.data);

      case DW_FORM_string:
        return 1 + strlen (ap->val.string);
      
      case DW_FORM_ref1:
        return 1;
      
      case DW_FORM_ref2:
        return 2;
      
      case DW_FORM_ref4:
        return 4;
        
      default:
        werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
                "unsupported DWARF form");
        exit (1);
    }
    
}


/*---------------------------------------------------------------------*/
/* dwFindAttr - for a tag node, return a pointer to a particular       */
/*              attribute node, or NULL if not found                   */
/*---------------------------------------------------------------------*/
static dwattr *
dwFindAttr (dwtag * tp, int attr)
{
  dwattr * ap;
  
  ap = tp->attribs;
  while (ap)
    {
      if (ap->attr == attr)
        return ap;
      ap = ap->next;
    }
  
  return NULL;
}



/*------------------------------------------------------------------------*/


/*----------------------------------------------------------------------*/
/* dwNewTag - allocate a new tag node                                   */
/*----------------------------------------------------------------------*/
static dwtag *
dwNewTag (int tag)
{
  dwtag * tp;
  
  tp = Safe_alloc ( sizeof (dwtag));
  tp->tag = tag;
  
  return tp;
}

/*----------------------------------------------------------------------*/
/* dwAddTagAttr - add an attribute to a tag                             */
/*----------------------------------------------------------------------*/
static void
dwAddTagAttr (dwtag * tp, dwattr * ap)
{
  dwattr * curap;
  
  if (!tp->attribs)
    tp->attribs = ap;
  else if (ap->attr < tp->attribs->attr)
    {
      ap->next = tp->attribs;
      tp->attribs = ap;
    }
  else
    {
      curap = tp->attribs;
      while (curap->next && curap->next->attr < ap->attr)
        curap = curap->next;
      ap->next = curap->next;
      curap->next = ap;
    }
}

/*----------------------------------------------------------------------*/
/* dwSetTagAttr - repleace an existing attribute of a tag with a new    */
/*                attribute or add if non-existent                      */
/*----------------------------------------------------------------------*/
static void
dwSetTagAttr (dwtag *tp, dwattr * ap)
{
  dwattr * curap;
  
  curap = dwFindAttr (tp, ap->attr);
  if (curap)
    {
      ap->next = curap->next;
      *curap = *ap;
      dwFreeAttr (ap);
    }
  else
    dwAddTagAttr (tp, ap);
}


/*----------------------------------------------------------------------*/
/* dwAddTagChild - add a tag as a child of another tag                  */
/*----------------------------------------------------------------------*/
static dwtag *
dwAddTagChild (dwtag * parent, dwtag * child)
{
  child->parent = parent;
  if (parent->lastChild)
    {
      parent->lastChild->siblings = child;
      parent->lastChild = child;
    }
  else
    {
      parent->firstChild = child;
      parent->lastChild = child;
    }
  return parent;
}

/*----------------------------------------------------------------------*/
/* dwMatchTagAttr - returns true if two tags are equal in value,        */
/*                  attributes, and offspring status (the child tags    */
/*                  need not match, but they must both have children or */
/*                  both not have children)                             */
/*----------------------------------------------------------------------*/
static int
dwMatchTagAttr (const void * tp1v, const void * tp2v)
{
  const dwtag * tp1 = tp1v;
  const dwtag * tp2 = tp2v;
  dwattr * ap1;
  dwattr * ap2;

  if (!tp1 || !tp2)
    return 0;
    
  ap1 = tp1->attribs;
  ap2 = tp2->attribs;

  if (tp1->tag != tp2->tag)
    return 0;
  
  if (tp1->firstChild && !tp2->lastChild)
    return 0;
  if (!tp1->firstChild && tp2->lastChild)
    return 0;
    
  while (ap1 && ap2)
    {
      if (ap1->attr != ap2->attr)
        return 0;
      if (ap1->form != ap2->form)
        return 0;

      ap1 = ap1->next;
      ap2 = ap2->next;
    }
  
  return 1;
}

/*----------------------------------------------------------------------*/
/* dwHashTag - return a hash code for a tag based on its value and      */
/*             attributes                                               */
/*----------------------------------------------------------------------*/
static int
dwHashTag (dwtag * tp)
{
  dwattr * ap = tp->attribs;
  int hash = tp->tag;

  while (ap)
    {
      hash = (hash << 6) ^ ((hash >> 11) & 0xff);
      hash ^= (ap->attr) | (ap->form << 8);
      
      ap = ap->next;
    }  
  if (hash<0)
    return -hash;
  else
    return hash;
}

/*----------------------------------------------------------------------*/
/* dwTraverseTag - perform a depth-first preorder traversal of a tag    */
/*                 tree, calling the user function at each node. The    */
/*                 user function is also called with a NULL tag pointer */
/*                 after the last sibling of each immediate family is   */
/*                 processed.                                           */
/*----------------------------------------------------------------------*/
static int
dwTraverseTag (dwtag *tp, int (*somefunc)(dwtag *tp, void * info), void * info)
{
  int rvalue = 0;
  
  while (tp)
    {
      rvalue += (*somefunc)(tp, info);
      if (tp->firstChild)
        rvalue += dwTraverseTag (tp->firstChild, somefunc, info);
      tp = tp->siblings;
    }
  rvalue += (*somefunc)(NULL, info);
  
  return rvalue;
}

/*----------------------------------------------------------------------*/
/* dwAssignAbbrev - find a matching abbreviation for a tag or create a  */
/*                  a new one and assign it                             */
/*----------------------------------------------------------------------*/
static int
dwAssignAbbrev (dwtag *tp, void *info)
{
  dwtag * oldtp;
  int * anp = info;     /* pointer to current abbreviation number */
  int key;
  
  if (!tp)
    return 0;

  key = dwHashTag (tp) % dwAbbrevTable->size;
  oldtp = hTabFindByKey (dwAbbrevTable, key, tp, dwMatchTagAttr);
  if (oldtp)
    {
      tp->abbrev = oldtp->abbrev;
      return 0;
    }
  else
    {
      tp->abbrev = ++(*anp);
      hTabAddItemLong (&dwAbbrevTable, key, tp, tp);
      return 1;
    }
}

/*-----------------------------------------------------------------------*/
/* dwWriteAbbrevs - write the abbreviations to the .debug_abbrev section */
/*-----------------------------------------------------------------------*/
static void
dwWriteAbbrevs (int abbrevNum)
{
  dwtag * tp;
  dwattr * ap;
  int key;
  dwtag ** tptable = NULL;
  int abbrev;
  
  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_abbrev (NOLOAD)");
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_abbrev");

  /* Sort the abbreviations by their abbreviation number so that   */
  /* we can output them in this order. I don't see any requirement */
  /* in the standard for this, but some things seem to assume it.  */
  tptable = Safe_calloc (1+abbrevNum, sizeof(dwtag *));
  tp = hTabFirstItem (dwAbbrevTable, &key);
  for (; tp; tp = hTabNextItem (dwAbbrevTable, &key))
    tptable[tp->abbrev] = tp;

  for (abbrev=1; abbrev<=abbrevNum; abbrev++)
    {
      tp = tptable[abbrev];
      if (!tp) continue;

      dwWriteULEB128 (NULL, tp->abbrev, NULL);
      dwWriteULEB128 (NULL, tp->tag, NULL);
      dwWriteByte (NULL, tp->firstChild ? DW_CHILDREN_yes : DW_CHILDREN_no,
                   NULL);
      ap = tp->attribs;
      while (ap)
        {
          dwWriteULEB128 (NULL, ap->attr, NULL);
          dwWriteULEB128 (NULL, ap->form, NULL);
          ap = ap->next;
        }
      dwWriteULEB128 (NULL, 0, NULL);
      dwWriteULEB128 (NULL, 0, NULL);
      
    }
  dwWriteULEB128 (NULL, 0, NULL);
  
  Safe_free (tptable);
  hTabDeleteAll (dwAbbrevTable);
}



/*-----------------------------------------------------------------------*/
/* dwWriteTag - write the encoded tag information                        */
/*-----------------------------------------------------------------------*/
static int
dwWriteTag (dwtag *tp, void *info)
{
  dwattr * ap;

  if (!tp)
    {
      /* mark the end of this series of siblings */
      dwWriteULEB128 (NULL, 0, NULL);
      return 0;
    }

  //fprintf (dwarf2FilePtr, "; baseOffset = 0x%x\n", tp->baseOffset);
  
  /* write the tag abbreviation */
  dwWriteULEB128 (NULL, tp->abbrev, NULL);
  
  /* write the values of the attributes */
  ap = tp->attribs;
  while (ap)
    {
      dwWriteAttr (ap);
      ap = ap->next;
    }
    
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteTags - write all the tags to the .debug_info section           */
/*-----------------------------------------------------------------------*/
static void
dwWriteTags (void)
{  
  if (!dwRootTag)
    return;

  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_info (NOLOAD)");
  
  dwWriteWordDelta ("Ldebug_info_end", "Ldebug_info_start");
  
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_info_start");
  
  dwWriteHalf (NULL, 2, NULL); /* DWARF version */
  
  dwWriteWord ("Ldebug_abbrev", 0, NULL);
    
  dwWriteByte (NULL, port->debugger.dwarf.addressSize, NULL);

  // The root tag has no siblings and must not have an end-of-sibling-
  // chain marker, so handle it separately and start the traversal with
  // its children.
  dwWriteTag (dwRootTag, NULL);
  if (dwRootTag->firstChild)
    dwTraverseTag (dwRootTag->firstChild, dwWriteTag, NULL);

  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_info_end");

}

/*-----------------------------------------------------------------------*/
/* dwAssignTagAddress - assign the current address to the current tag.   */
/*                      Compute the next address based on the tag size   */
/*-----------------------------------------------------------------------*/
static int
dwAssignTagAddress (dwtag *tp, void *info)
{
  int * tap = info;
  dwattr * ap;

  if (!tp)
    {
      *tap += 1;
      return 0;
    }
      
  tp->baseOffset = *tap;

  *tap += dwSizeofULEB128 (tp->abbrev);
  
  ap = tp->attribs;
  while (ap)
    {
      *tap += dwSizeofAttr (ap);
      ap = ap->next;
    }
    
  return 0;
}

/*-----------------------------------------------------------------------*/
/* dwAddSibAttr - if a tag has children and a sibling, add a sibling     */
/*                attribute (it allows debuggers to jump to the sibling  */
/*                and skip the child data)                               */
/*-----------------------------------------------------------------------*/
static int
dwAddSibAttr (dwtag *tp, void *info)
{
  if (!tp)
    return 0;
  if (tp == dwRootTag)
    return 0;
  
  if (tp->firstChild && tp->siblings)
    dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_sibling, tp->siblings));

  return 0;
}

/*-----------------------------------------------------------------------*/
/* dwDeleteTagAttr - given a pointer to an attribute type, delete any    */
/*                   matching attribute                                  */
/*-----------------------------------------------------------------------*/
static int
dwDeleteTagAttr (dwtag *tp, void *info)
{
  int attr = *((int *) info);
  dwattr * ap;
  
  if (!tp)
    return 0;

  ap = tp->attribs;
  if (ap && ap->attr == attr)
    {
      tp->attribs = ap->next;
      return 1;
    }
  
  while (ap)
    {
      if (ap->next && ap->next->attr == attr)
        {
          ap->next = ap->next->next;
          return 1;
        }
      ap = ap->next;
    }
  
  return 0;
}


/*------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/* dwWritePubnames - write all the public names to the .debug_pubnames   */
/*                   section. Externally visible functions and variables */
/*                   are considered to have public names.                */
/*-----------------------------------------------------------------------*/
static void
dwWritePubnames (void)
{
  dwtag * tp;
  dwattr * ap1;
  dwattr * ap2;
  
  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_pubnames (NOLOAD)");
  
  dwWriteWordDelta ("Ldebug_pubnames_end", "Ldebug_pubnames_start");
  
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_pubnames_start");
  
  dwWriteHalf (NULL, 2, NULL); /* DWARF version */
  
  dwWriteWord ("Ldebug_info_start-4", 0, NULL);
  dwWriteWordDelta ("4+Ldebug_info_end", "Ldebug_info_start");

  if (dwRootTag && dwRootTag->firstChild)
    {
      tp = dwRootTag->firstChild;
      while (tp)
        {
          if (tp->tag == DW_TAG_variable || tp->tag == DW_TAG_subprogram)
            {
              /* If it has a name and is externally visible, it's a pubname */
              ap1 = dwFindAttr (tp, DW_AT_external);
              ap2 = dwFindAttr (tp, DW_AT_name);
              if (ap1 && ap1->val.data && ap2)
                {
                  dwWriteWord (NULL, tp->baseOffset, NULL);
                  dwWriteString (ap2->val.string, NULL);
                }
            }
        
          tp = tp->siblings;
        }
    }  
  dwWriteWord (NULL, 0, NULL);
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_pubnames_end");
}

/*------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/* dwFindFileIndex - find the index of a filename in dwFilenameSet; if   */
/*                   it does not exist, it is added                      */
/*-----------------------------------------------------------------------*/
static int
dwFindFileIndex (char * filename)
{
  char * includeDir;
  dwfile * srcfile;
  int fileIndex = 1;
  int dirIndex = 1;

  /* Just do a linear search for the file. There should be hardly */
  /* a penalty since 1) most calls search for the first file, and */
  /* 2) the file set is usually small (often just 1 item)         */
  for (srcfile = setFirstItem (dwFilenameSet);
       srcfile;
       srcfile = setNextItem(dwFilenameSet), fileIndex++ )
    {
      if (!strcmp (srcfile->name, filename))
        return fileIndex;
    }

  for (includeDir = setFirstItem (includeDirsSet);
       includeDir;
       includeDir = setNextItem(includeDirsSet), dirIndex++ )
    {
      if (!strncmp (includeDir, filename, strlen (includeDir))
          && strlen (filename) > strlen (includeDir))
        {
          if (IS_DIR_SEPARATOR(filename[strlen (includeDir)]))
            break;
        }
    }
  if (!includeDir)
    dirIndex = 0;

  srcfile = Safe_alloc (sizeof (dwfile));
  srcfile->name = filename;
  srcfile->dirIndex = dirIndex;
  srcfile->timestamp = 0;
  srcfile->length = 0;

  addSet (&dwFilenameSet, srcfile);
  return fileIndex;
}

/*-----------------------------------------------------------------------*/
/* dwWriteLineNumber - write line number (and related position info) to  */
/*                     address corespondence data for a single node      */
/*-----------------------------------------------------------------------*/
static void
dwWriteLineNumber (dwline * lp)
{
  static int curFileIndex = 1;
  static int curLine = 1;
  static char * curLabel = NULL;
  static int curOffset = 0;
  int deltaLine = lp->line - curLine;
  int deltaAddr = lp->offset - curOffset;
  int deltaAddrValid = curLabel && lp->label && !strcmp (lp->label, curLabel);

  //fprintf (dwarf2FilePtr, "; line %d\n", lp->line);
  if (lp->begin_sequence)
    {
      curFileIndex = 1;
      curLine = 1;
      curLabel = NULL;
      curOffset = 0;
      
      if (lp->end_sequence)
        return;
    }
  
  if (lp->fileIndex != curFileIndex)
    {
      dwWriteByte (NULL, DW_LNS_set_file, NULL);
      dwWriteULEB128 (NULL, lp->fileIndex, NULL);
      curFileIndex = lp->fileIndex;
    }
  
  if (lp->basic_block)
    {
      dwWriteByte (NULL, DW_LNS_set_basic_block, NULL);
    }
  
  if (lp->begin_sequence)
    {
      dwWriteByte (NULL, 0, NULL);
      dwWriteULEB128 (NULL, 1+port->debugger.dwarf.addressSize, NULL);
      dwWriteByte (NULL, DW_LNE_set_address, NULL);
      dwWriteAddress (lp->label, lp->offset, NULL);
      curLabel = lp->label;
      curOffset = lp->offset;

      dwWriteByte (NULL, DW_LNS_advance_line, NULL);
      dwWriteSLEB128 (NULL, lp->line - 1, NULL);
      curLine = lp->line;

      dwWriteByte (NULL, DW_LNS_copy, NULL);
    }
  else if (lp->end_sequence)
    {
      if (deltaAddrValid)
        {
          dwWriteByte (NULL, DW_LNS_advance_pc, NULL);
          dwWriteULEB128 (NULL, deltaAddr, NULL);
        }
      else
        {
          dwWriteByte (NULL, DW_LNS_fixed_advance_pc, NULL);
          dwWriteHalfDelta (lp->label, curLabel, lp->offset-curOffset);
          curLabel = lp->label;
          curOffset = lp->offset;
        }
      
      dwWriteByte (NULL, 0, NULL);
      dwWriteULEB128 (NULL, 1, NULL);
      dwWriteByte (NULL, DW_LNE_end_sequence, NULL);
    }
  else
    {
      int usedSpecial = 0;
      
      /* Metrowerks CW08 V3.0 gets confused by this. Just use the long */
      /* encoding until we can find a more compatible phrasing.        */
      #if 0
      if (deltaLine >= dwLineBase && deltaLine < (dwLineBase+dwLineRange))
        {
          int opcode;
          
          /* try to build a "special" opcode */
          opcode = dwLineOpcodeBase + (deltaLine - dwLineBase);
          if (deltaAddrValid)
            opcode += deltaAddr*dwLineRange;
          
          if (opcode >= dwLineOpcodeBase && opcode <= 255)
            {
              /* ok, we can use a "special" opcode */
              
              /* If the deltaAddr value was symbolic, it can't be part */
              /* of the "special" opcode, so encode it separately      */
              if (!deltaAddrValid)
                {
                  dwWriteByte (NULL, DW_LNS_advance_pc, NULL);
                  dwWriteULEB128Delta (lp->label, curLabel, lp->offset-curOffset);
                  curLabel = lp->label;
                  curOffset = lp->offset;
                }

              /* Write the "special" opcode */        
              dwWriteByte (NULL, opcode, NULL);
              curLine = lp->line;
              usedSpecial = 1;
            }
        }
      #endif
      
      /* If we couldn't use the "special" opcode, we will have to */
      /* encode this the long way.                                */
      if (!usedSpecial)
        {
#if 0 // Fails to assemble for initializations of local static variables. TODO: Use this, when possible, as DW_LNS_fixed_advance_pc is much shorter than DW_LNE_set_address below.
          dwWriteByte (NULL, DW_LNS_fixed_advance_pc, NULL);
          dwWriteHalfDelta (lp->label, curLabel, lp->offset-curOffset);
#else // Todo: This was implemented to make initializations of local static variables compile, but stepping through those initializations in gdb reports "Cannot find bounds of current function".
          dwWriteByte (NULL, 0, NULL);
          dwWriteULEB128 (NULL, 1+port->debugger.dwarf.addressSize, NULL);
          dwWriteByte (NULL, DW_LNE_set_address, NULL);
          dwWriteAddress (lp->label, lp->offset, NULL);
#endif
          curLabel = lp->label;
          curOffset = lp->offset;
        
          dwWriteByte (NULL, DW_LNS_advance_line, NULL);
          dwWriteSLEB128 (NULL, deltaLine, NULL);
          curLine = lp->line;
          
          dwWriteByte (NULL, DW_LNS_copy, NULL);
        }
        
    }
    
}

/*-----------------------------------------------------------------------*/
/* dwWriteLineNumbers - write all the source line number position data   */
/*                      to the .debug_line section                       */
/*-----------------------------------------------------------------------*/
static void
dwWriteLineNumbers (void)
{
  char * includeDir;
  dwfile * srcfile;
  dwline * lp;
  
  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_line (NOLOAD)");
  
  dwWriteWordDelta ("Ldebug_line_end", "Ldebug_line_start");
  
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_start");
  
  dwWriteHalf (NULL, 2, NULL); /* DWARF version */

  dwWriteWordDelta ("Ldebug_line_stmt-6", "Ldebug_line_start");

  dwWriteByte (NULL, 1, NULL); /* we track everything in 1 byte increments */
  
  dwWriteByte (NULL, 1, NULL); /* assume every line is a new statement */

  dwWriteByte (NULL, dwLineBase, NULL);
  dwWriteByte (NULL, dwLineRange, NULL);
  
  dwWriteByte (NULL, 9+1, NULL);  /* there are 9 standard opcodes */
  
  dwWriteByte (NULL, 0, NULL);  /* number of DW_LNS_copy arguments */
  dwWriteByte (NULL, 1, NULL);  /* number of DW_LNS_advance_pc arguments */
  dwWriteByte (NULL, 1, NULL);  /* number of DW_LNS_advance_line arguments */
  dwWriteByte (NULL, 1, NULL);  /* number of DW_LNS_set_file arguments */
  dwWriteByte (NULL, 1, NULL);  /* number of DW_LNS_set_column arguments */
  dwWriteByte (NULL, 0, NULL);  /* number of DW_LNS_negate_stmt arguments */
  dwWriteByte (NULL, 0, NULL);  /* number of DW_LNS_set_basic_block arguments */
  dwWriteByte (NULL, 0, NULL);  /* number of DW_LNS_const_add_pc arguments */
  dwWriteByte (NULL, 1, NULL);  /* number of DW_LNS_fixed_advance_pc arguments */

  /* Write the list of source directories searched */
  for (includeDir = setFirstItem (includeDirsSet);
       includeDir;
       includeDir = setNextItem(includeDirsSet) )
    dwWriteString (includeDir, NULL);
  dwWriteByte (NULL, 0, NULL);
  
  /* Write the list of source files used */
  for (srcfile = setFirstItem (dwFilenameSet);
       srcfile;
       srcfile = setNextItem(dwFilenameSet) )
    {
      dwWriteString (srcfile->name, NULL);
      dwWriteULEB128 (NULL, srcfile->dirIndex, NULL);
      dwWriteULEB128 (NULL, srcfile->timestamp, NULL);
      dwWriteULEB128 (NULL, srcfile->length, NULL);
    }
  dwWriteByte (NULL, 0, NULL);
  
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_stmt");

  lp = dwLineFirst;
  if (lp)
    lp->begin_sequence = 1;
  while (lp)
    {
      dwWriteLineNumber (lp);
      if (lp->end_sequence && lp->next)
        lp->next->begin_sequence = 1;
      lp = lp->next;
    }
    
  tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_end");
}

/*------------------------------------------------------------------------*/


static void
dwWriteCFAinstructions (dwcfins *ip)
{
  dwcfop * op = ip->first;
  
  while (op)
    {
      dwWriteByte (NULL, op->opcode, NULL);
      switch (op->opcode >> 6)
        {
        case 0:
          switch (op->opcode)
            {
            case DW_CFA_set_loc:
              dwWriteAddress (op->label, op->operand1, NULL);
              break;
            
            case DW_CFA_advance_loc1:
              dwWriteByte (NULL, op->operand1, NULL);
              break;
            
            case DW_CFA_advance_loc2:
              dwWriteHalf (NULL, op->operand1, NULL);
              break;
            
            case DW_CFA_advance_loc4:
              dwWriteWord (NULL, op->operand1, NULL);
              break;
            
            case DW_CFA_def_cfa:
            case DW_CFA_register:
            case DW_CFA_offset_extended:
              dwWriteULEB128 (NULL, op->operand1, NULL);
              dwWriteULEB128 (NULL, op->operand2, NULL);
              break;
            
            case DW_CFA_undefined:
            case DW_CFA_same_value:
            case DW_CFA_def_cfa_register:
            case DW_CFA_def_cfa_offset:
            case DW_CFA_restore_extended:
              dwWriteULEB128 (NULL, op->operand1, NULL);
              break;
            }
          break;
        
        case DW_CFA_restore >> 6:
        case DW_CFA_advance_loc >> 6:
          break;
        
        case DW_CFA_offset >> 6:
          dwWriteULEB128 (NULL, op->operand1, NULL);
          break;
        }
      op = op->next;
    }
}

static int
dwSizeofCFAinstructions (dwcfins *ip)
{
  int size = 0;
  dwcfop * op = ip->first;
  
  while (op)
    {
      size++;
      switch (op->opcode >> 6)
        {
        case 0:
          switch (op->opcode)
            {
            case DW_CFA_set_loc:
              size += port->debugger.dwarf.addressSize;
              break;
            
            case DW_CFA_advance_loc1:
              size += 1;
              break;
            
            case DW_CFA_advance_loc2:
              size += 2;
              break;
            
            case DW_CFA_advance_loc4:
              size += 4;
              break;
            
            case DW_CFA_def_cfa:
            case DW_CFA_register:
            case DW_CFA_offset_extended:
              size += dwSizeofULEB128 (op->operand1);
              size += dwSizeofULEB128 (op->operand2);
              break;
            
            case DW_CFA_undefined:
            case DW_CFA_same_value:
            case DW_CFA_def_cfa_register:
            case DW_CFA_def_cfa_offset:
            case DW_CFA_restore_extended:
              size += dwSizeofULEB128 (op->operand1);
              break;
            }
          break;
        
        case DW_CFA_restore >> 6:
        case DW_CFA_advance_loc >> 6:
          break;
        
        case DW_CFA_offset >> 6:
          size += dwSizeofULEB128 (op->operand1);
          break;
        }
      op = op->next;
    }
  return size;
}

static dwcfop *
dwNewCFop (int opcode)
{
  dwcfop * op;
  
  op = Safe_alloc (sizeof (dwcfop));
  op->opcode = opcode;
  
  return op;
}

static dwcfins *
dwNewCFins (void)
{
  return (dwcfins *) Safe_alloc (sizeof (dwcfins));
}

static void
dwAddCFinsOp (dwcfins * ip, dwcfop *op)
{
  if (ip->last)
    ip->last->next = op;
  else
    ip->first = op;
  ip->last = op;
}

static void
dwGenCFIins (int callsize, int id)
{
  dwcfins * ip;
  dwcfop * op;
  int i;
  char s[32];
  int padding;
  
  tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_frame (NOLOAD)");

  /* FIXME: these two dw should be combined into a dd */
  tfprintf (dwarf2FilePtr, "\t!dw\t0\n");
  tfprintf (dwarf2FilePtr, "\t!dw\t");
  tfprintf (dwarf2FilePtr, "Ldebug_CIE%d_end-Ldebug_CIE%d_start\n", id, id);

  snprintf(s, sizeof(s), "Ldebug_CIE%d_start", id);
  tfprintf (dwarf2FilePtr, "!slabeldef\n", s);
  
  tfprintf (dwarf2FilePtr, "\t!dw\t0xffff\n");
  tfprintf (dwarf2FilePtr, "\t!dw\t0xffff\n");  /* CIE_id */

  tfprintf (dwarf2FilePtr, "\t!db\t%d\n",1);    /* CIE version number */

  tfprintf (dwarf2FilePtr, "\t!db\t%d\n",0);    /* augmentation (none) */

  dwWriteULEB128 (NULL, 1, NULL);       /* code alignment factor */
  
  dwWriteSLEB128 (NULL, (port->stack.direction > 0) ? 1 : -1, NULL); /* data alignment factor */
  
  dwWriteByte (NULL, port->debugger.dwarf.regNumRet, NULL);
  
  ip = dwNewCFins ();
  
  /* Define the CFA as the SP at the previous frame (call site) */
  /* The return address is then at CFA-1 (for stm8)             */
  op = dwNewCFop (DW_CFA_def_cfa);
  op->operand1 = port->debugger.dwarf.regNumSP;
  op->operand2 = port->debugger.dwarf.offsetSP;
  if (callsize == 3)
    op->operand2 = port->debugger.dwarf.offsetSP + port->stack.isr_overhead;
  dwAddCFinsOp (ip, op);

  op = dwNewCFop (DW_CFA_offset + port->debugger.dwarf.regNumRet);
  op->operand1 = 1;  /* perhaps we need a way to configure this relative position for the ret pos? ARE*/
  dwAddCFinsOp (ip, op);

  if (port->debugger.dwarf.cfiUndef)
    for (i=0; i < port->debugger.dwarf.cfiUndef->size; i++)
      {
        if (bitVectBitValue (port->debugger.dwarf.cfiUndef, i))
          {
            op = dwNewCFop (DW_CFA_undefined);
            op->operand1 = i;
            dwAddCFinsOp (ip, op);
          }
    }
  
  if (port->debugger.dwarf.cfiSame)
    for (i=0; i < port->debugger.dwarf.cfiSame->size; i++)
      {
        if (bitVectBitValue (port->debugger.dwarf.cfiSame, i))
          {
            op = dwNewCFop (DW_CFA_same_value);
            op->operand1 = i;
            dwAddCFinsOp (ip, op);
          }
      }

  dwWriteCFAinstructions (ip);

  //pad with NOPs if needed to maintain 32-bit alignment
  padding = (4 - ((5 + dwSizeofCFAinstructions (ip)) & 3)) & 3;
  while (padding)
    {
      dwWriteByte (NULL, DW_CFA_nop, NULL);
      padding--;
    }
  
  op = ip->first;
  while (op)
  {
    dwcfop * next;
    next = op->next;
    Safe_free(op);
    op = next;
  }
  Safe_free(ip);

  snprintf(s, sizeof(s), "Ldebug_CIE%d_end", id);
  tfprintf (dwarf2FilePtr, "!slabeldef\n",s);
}


static void
dwWriteFDE (dwfde * fp, int id)
{
  int length = dwSizeofCFAinstructions(fp->ins) + 4
               + port->debugger.dwarf.addressSize * 2;
  int padding = (4 - (length & 3)) & 3;

  //length
  dwWriteWord (NULL, length + padding , NULL);

  //CIE ptr
  char s[32];
  snprintf(s, sizeof(s), "Ldebug_CIE%d_start-4", id);
  dwWriteWord (s, 0, NULL);
  
  //initial loc
  dwWriteAddress(fp->startLabel, 0, "initial loc");

  //address range
  dwWriteAddressDelta (fp->endLabel, fp->startLabel);

  //instructions
  dwWriteCFAinstructions (fp->ins);

  //pad with NOPs if needed to maintain 32-bit alignment
  while (padding)
    {
      dwWriteByte (NULL, DW_CFA_nop, NULL);
      padding--;
    }
}

static void
dwWriteFrames (void)
{
  dwfde fp;
  dwcfop * op;
  dwcfilist * cfip;
  dwlocregion * lrp;

  int id = 0;
  for (cfip = dwCFIRoot; cfip; cfip = cfip->next)
    {
      if (!cfip->startLabel || !cfip->endLabel)
        continue;
        
      fp.startLabel=cfip->startLabel;
      fp.endLabel=cfip->endLabel;

      fp.ins = dwNewCFins();

      lrp = cfip->region;

      /* emit fde records */
      while (lrp)
        {
          op = dwNewCFop (DW_CFA_set_loc);
          op->label = lrp->startLabel;
          op->operand1 = 0;
          dwAddCFinsOp (fp.ins, op);
          op = dwNewCFop (DW_CFA_def_cfa_offset);
          if (cfip->callsize == 3)
            op->operand1 = lrp->loc->operand.offset +  port->debugger.dwarf.offsetSP - 1 + port->stack.isr_overhead;
          else
            op->operand1 = lrp->loc->operand.offset +  port->debugger.dwarf.offsetSP - 1; /* another -1 constant that needs attention ARE*/
          dwAddCFinsOp (fp.ins, op);

          lrp = lrp ->next;
        }

      dwGenCFIins(cfip->callsize, id);
      dwWriteFDE(&fp, id++);

      op = fp.ins->first;
      while (op)
      {
        dwcfop * next;
        next = op->next;
        Safe_free(op);
        op = next;
      }
    }
}



/*------------------------------------------------------------------------*/




/*-----------------------------------------------------------------------*/
/* dwHashType - return a hash code for a type chain                      */
/*-----------------------------------------------------------------------*/
static int
dwHashType (sym_link * type)
{
  int hash = 0;

  while (type)
    {
      hash = (hash << 5) ^ ((hash >> 8) & 0xff);
      if (IS_DECL (type))
        {
          hash ^= DCL_TYPE (type);
        }
      else
        {
          hash ^= SPEC_NOUN (type)
               | (SPEC_CONST (type) << 4)
               | (SPEC_VOLATILE (type) << 5)
               | (SPEC_LONG (type) << 6)
               | (SPEC_LONGLONG (type) << 7);
        }
      
      type = type->next;
    }  

  if (hash<0)
    return -hash;
  else
    return hash;
}

/*-----------------------------------------------------------------------*/
/* dwMatchType - returns true if two types match exactly (including type */
/*               qualifiers)                                             */
/*-----------------------------------------------------------------------*/
static int
dwMatchTypes (const void * type1v, const void * type2v)
{
  sym_link * type1 = (sym_link *)type1v;
  sym_link * type2 = (sym_link *)type2v;
  
  if (!type1 || !type2)
    return 0;
  
  while (type1 && type2)
    {
      if (IS_SPEC(type1))
        {
          if (IS_SPEC (type2))
            {
              if (SPEC_NOUN (type1) != SPEC_NOUN (type2))
                return 0;
              if (SPEC_NOUN (type1) == V_STRUCT
                  && SPEC_STRUCT (type1) != SPEC_STRUCT (type2))
                return 0;
              if (SPEC_CONST (type1) != SPEC_CONST (type2))
                return 0;
              if (SPEC_VOLATILE (type1) != SPEC_VOLATILE (type2))
                return 0;
              if (SPEC_SHORT (type1) != SPEC_SHORT (type2))
                return 0;
              if (SPEC_LONG (type1) != SPEC_LONG (type2))
                return 0;
              if (SPEC_LONGLONG (type1) != SPEC_LONGLONG (type2))
                return 0;
              if (SPEC_USIGN (type1) != SPEC_USIGN (type2))
                return 0;
            }
          else
            return 0;
        }
      else
        {
          if (IS_DECL (type2))
            {
              if (DCL_TYPE (type1) != DCL_TYPE (type2))
                return 0;
              if (DCL_PTR_CONST (type1) != DCL_PTR_CONST (type2))
                return 0;
              if (DCL_PTR_VOLATILE (type1) != DCL_PTR_VOLATILE (type2))
                return 0;
              if (DCL_TYPE (type1) == ARRAY
                  && DCL_ELEM (type1) != DCL_ELEM (type2))
                return 0;
              /* FIXME: need to match function pointer parameters */
            }
          else
            return 0;
        }
      
      type1 = type1->next;
      type2 = type2->next;
    }

  if (!type1 && !type2)
    return 1;
  else
    return 0;
}

/*-----------------------------------------------------------------------*/
/* dwTagFromType - returns the tag describing a type. If new tags need   */
/*                 to be created, they will be added under the specified */
/*                 parent tag                                            */
/*-----------------------------------------------------------------------*/
static dwtag *
dwTagFromType (sym_link * type, dwtag * parent)
{
  dwtag * oldtp;
  dwtag * tp = NULL;
  dwtag * modtp;
  dwtag * subtp;
  int key;
  int tableUpdated = 0;
  
  key = dwHashType (type) % dwTypeTagTable->size;
  oldtp = hTabFindByKey (dwTypeTagTable, key, type, dwMatchTypes);
  if (oldtp)
    return oldtp;
  else
    {
      if (IS_DECL (type))
        {
          switch (DCL_TYPE (type))
            {
            case POINTER:
            case FPOINTER:
            case CPOINTER:
            case GPOINTER:
            case PPOINTER:
            case IPOINTER:
            case EEPPOINTER:
            case UPOINTER:
              tp = dwNewTag (DW_TAG_pointer_type);
              if (type->next && !IS_VOID (type->next))
                {
                  subtp = dwTagFromType (type->next, parent);
                  dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp));
                }
              dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                getSize (type)));
              dwAddTagChild (parent, tp);
              if (DCL_PTR_VOLATILE (type))
                {
                  modtp = dwNewTag (DW_TAG_volatile_type);
                  dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp));
                  dwAddTagChild (parent, modtp);
                  tp = modtp;
                }
              if (DCL_PTR_CONST (type))
                {
                  modtp = dwNewTag (DW_TAG_const_type);
                  dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp));
                  dwAddTagChild (parent, modtp);
                  tp = modtp;
                }
              break;
              
            case ARRAY:
              tp = dwNewTag (DW_TAG_array_type);
              subtp = dwTagFromType (type->next, parent);
              dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp));
              if (!subtp->parent)
                dwAddTagChild (tp, subtp);
              if (DCL_ELEM (type))
                {
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  subtp = dwNewTag (DW_TAG_subrange_type);
                  dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_upper_bound,
                                                       DCL_ELEM (type)-1));
                  dwAddTagChild (tp, subtp);
                }

              break;
            
            case FUNCTION:
              tp = dwNewTag (DW_TAG_subroutine_type);
              if (type->next && !IS_VOID (type->next))
                {
                  subtp = dwTagFromType (type->next, parent);
                  dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp));
                }
              /* FIXME: need to handle function parameters */
              break;
              
            default:
              werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
                      "unknown DCL_TYPE");
              exit (1);
            }
        }
      else
        {
          if (IS_STRUCT (type))
            {
              struct structdef * sdp = SPEC_STRUCT (type);
              symbol * field;
              
              tp = dwNewTag (sdp->type == STRUCT ? DW_TAG_structure_type
                                                 : DW_TAG_union_type);
              if (*(sdp->tag))
                dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sdp->tag));
              
              /* FIXME: should only specify the size if we know this */
              /* is a complete type */
              dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                getSize (type)));
              
              /* Must add this before processing the struct fields */
              /* in case there is a recursive definition.          */
              hTabAddItemLong (&dwTypeTagTable, key, type, tp);
              tableUpdated = 1;

              field = sdp->fields;
              while (field)
                {
                  dwtag * memtp;
                  dwloc * lp;

                  if (IS_BITFIELD (field->type) && !SPEC_BLEN(field->type))
                    {
                      field = field->next;
                      continue;
                    }

                  memtp = dwNewTag (DW_TAG_member);
                  if (*(field->name))
                    dwAddTagAttr (memtp, dwNewAttrString (DW_AT_name,
                                                          field->name));
                  if (IS_BITFIELD (field->type))
                    {
                      unsigned blen = SPEC_BLEN (field->type);
                      unsigned bstr = SPEC_BSTR (field->type);
                      sym_link * type;
                      
                      dwAddTagAttr (memtp,
                                    dwNewAttrConst (DW_AT_byte_size,
                                                    (blen+7)/8));
                      dwAddTagAttr (memtp,
                                    dwNewAttrConst (DW_AT_bit_size, blen));
                      dwAddTagAttr (memtp,
                                    dwNewAttrConst (DW_AT_bit_offset,
                                                    ((blen+7) & ~7)
                                                    - (blen+bstr)));
                      if (blen < 8)
                        type = typeFromStr ("Uc");
                      else
                        type = typeFromStr ("Ui");
                      subtp = dwTagFromType (type, tp);
                      dwAddTagAttr (memtp, dwNewAttrTagRef (DW_AT_type, subtp));
                    }
                  else
                    {
                      subtp = dwTagFromType (field->type, tp);
                      dwAddTagAttr (memtp, dwNewAttrTagRef (DW_AT_type, subtp));
                      if (!subtp->parent)
                        dwAddTagChild (parent, subtp);
                    }

                  lp = dwNewLoc (DW_OP_plus_uconst, NULL, field->offset);
                  dwAddTagAttr (memtp,
                                dwNewAttrLoc (DW_AT_data_member_location, lp));

                  dwAddTagChild (tp, memtp);
                  
                  field = field->next;
                }
            }
          else if (SPEC_VOLATILE (type) || SPEC_CONST (type))
            {
              sym_link temptype = *type;
              
              SPEC_VOLATILE (&temptype) = 0;
              SPEC_CONST (&temptype) = 0;
              tp = dwTagFromType (&temptype, parent);
              if (SPEC_VOLATILE (type))
                {
                  modtp = dwNewTag (DW_TAG_volatile_type);
                  dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp));
                  dwAddTagChild (parent, modtp);
                  tp = modtp;
                }
              if (SPEC_CONST (type))
                {
                  modtp = dwNewTag (DW_TAG_const_type);
                  dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp));
                  dwAddTagChild (parent, modtp);
                  tp = modtp;
                }
            }
          else
            {
              switch (SPEC_NOUN (type))
                {
                case V_INT:
                  tp = dwNewTag (DW_TAG_base_type);
                  if (SPEC_USIGN (type))
                    {
                      dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                        DW_ATE_unsigned));
                      if (SPEC_LONGLONG (type))
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name,
                                                           "unsigned long long"));
                      else if (SPEC_LONG (type))
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name,
                                                           "unsigned long"));
                      else
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name,
                                                           "unsigned int"));
                    }
                  else
                    {
                      dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                        DW_ATE_signed));
                      if (SPEC_LONGLONG (type))
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "long long"));
                      else if (SPEC_LONG (type))
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "long"));
                      else
                        dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "int"));
                    }
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  dwAddTagChild (dwRootTag, tp);
                  break;
                  
                case V_FLOAT:
                  tp = dwNewTag (DW_TAG_base_type);
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                    DW_ATE_float));
                  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "float"));
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  dwAddTagChild (dwRootTag, tp);
                  break;

                case V_FIXED16X16:
                  tp = dwNewTag (DW_TAG_base_type);
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                    DW_ATE_float));
                  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "fixed16x16"));
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  dwAddTagChild (dwRootTag, tp);
                  break;
                
                case V_BOOL:
                  tp = dwNewTag (DW_TAG_base_type);
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                    DW_ATE_boolean));
                  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "_Bool"));
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  dwAddTagChild (dwRootTag, tp);
                  break;

                case V_CHAR:
                  tp = dwNewTag (DW_TAG_base_type);
                  if (SPEC_USIGN (type))
                    {
                      dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                        DW_ATE_unsigned_char));
                      dwAddTagAttr (tp, dwNewAttrString (DW_AT_name,
                                                         "unsigned char"));
                    }
                  else
                    {
                      dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding,
                                                        DW_ATE_signed));
                      dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "signed char"));
                    }
                  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size,
                                                    getSize (type)));
                  dwAddTagChild (dwRootTag, tp);
                  break;
                
                case V_VOID:
                case V_BIT:
                case V_BITFIELD:
                case V_SBIT:
                case V_DOUBLE:
                default:
                  
                  werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
                      "unhandled base type");
                  printTypeChain (type, NULL);
                  exit (1);
  
                }
            }
        }
    }
  
  if (!tableUpdated)
    hTabAddItemLong (&dwTypeTagTable, key, type, tp);
  if (!tp->parent)
    dwAddTagChild (parent, tp);
  return tp;
}
/*------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------*/
/* dwOpenFile - open the debugging file (just initialize, since all      */
/*              DWARF data goes into the assembly output file)           */
/*-----------------------------------------------------------------------*/
int
dwOpenFile(const char *file)
{
  dwTypeTagTable = newHashTable (128);
  
  return 1;
}

/*-----------------------------------------------------------------------*/
/* dwCloseFile - close the debugging file (do nothing, since all DWARF   */
/*               data goes into the assembly output file)                */
/*-----------------------------------------------------------------------*/
int
dwCloseFile (void)
{
  return 1;
}
  

/*-----------------------------------------------------------------------*/
/* dwGenerateScopes - recursively traverse an ast, generating lexical    */
/*                    block tags for block scopes found                  */
/*-----------------------------------------------------------------------*/
static void  
dwGenerateScopes (dwtag *tp, ast * tree)
{
  dwtag *subtp;

  if (!tree)
    return;
  
  if (!IS_AST_OP (tree))
    return;
      
  if (tree->opval.op == BLOCK)
    {
      subtp = dwNewTag (DW_TAG_lexical_block);
      if (tree->right)
        {
          dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_user_block, tree->right->block));
          dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_user_level, tree->right->level));
      
          dwAddTagChild (tp, subtp);
        }
      dwGenerateScopes (subtp, tree->right);
    }
  else
    {
      dwGenerateScopes (tp, tree->left);
      dwGenerateScopes (tp, tree->right);
    }
}

/*-----------------------------------------------------------------------*/
/* dwFindScope - return the lexical block tag for a particular block     */
/*               scope, or NULL if not found                             */
/*-----------------------------------------------------------------------*/
static dwtag *
dwFindScope (dwtag * tp, int block)
{
  dwtag * rettp;
  dwattr * ap;
  
  if (!tp)
    return NULL;
  
  while (tp)
    {
      if (tp->tag == DW_TAG_lexical_block)
        {
          ap = tp->attribs;
          while (ap)
            {
              if (ap->attr == DW_AT_user_block)
                {
                  if (ap->val.data == block)
                    return tp;
                }
              ap = ap->next;
            }
            
          rettp = dwFindScope (tp->firstChild, block);
          if (rettp)
            return rettp;
        }
      tp = tp->siblings;
    }
  
  return NULL;  
}

/*------------------------------------------------------------------------*/
/* dwWriteSymbolInternal - create tag information for a variable or       */
/*                         parameter and place it in the correct position */
/*                         within the tag tree                            */
/*------------------------------------------------------------------------*/
static int
dwWriteSymbolInternal (symbol *sym)
{
  dwtag *tp;
  dwtag *subtp;
  dwloc *lp;
  dwtag *scopetp;
  symbol *symloc;
  dwtag *functp;
  dwattr *funcap;
  bool inregs = FALSE;

  if (!sym->level || IS_EXTERN (sym->etype))
    scopetp = dwRootTag;
  else
    {
      assert(sym->localof);
      if (!sym->localof)
        return 0;
        
      /* Find the tag for the function this symbol is defined in */
      functp = dwRootTag->firstChild;
      while (functp)
        {
          if (functp->tag == DW_TAG_subprogram)
            {
              funcap = dwFindAttr (functp, DW_AT_name);
              if (funcap && !strcmp (funcap->val.string, sym->localof->name))
                break;
            }
          functp = functp->siblings;
        }

      /* There might be no function found if it was only inlined and */
      /* not generated as a stand-alone function. In that case,      */
      /* dwWriteSymbolInternal will be called again later in another */
      /* context where the function will be found. */
      if (!functp)
        return 0;
        
      /* Find the correct scope within this function */
      scopetp = dwFindScope (functp->firstChild, sym->block);
      if (!scopetp)
        scopetp = functp;
    }
  
  tp = dwNewTag (sym->_isparm ? DW_TAG_formal_parameter : DW_TAG_variable);
  
  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sym->name));
  
  /* Find the ultimate symbol holding the value. */
  /* Might be:                                   */
  /*   a) original symbol,                       */
  /*   b) register equivalent,                   */
  /*   c) spill location                         */
  symloc = sym;
  if (/*!sym->allocreq &&*/ sym->reqv)
    {
      symloc = OP_SYMBOL (symloc->reqv);

      for (int i = 0; i < symloc->nRegs; i++)
        if (symloc->regs[i])
          {
            inregs = TRUE;
            break;
          }

      if (!inregs && symloc->isspilt && !symloc->remat)
        symloc = symloc->usl.spillLoc;
    }

  lp = NULL;
  if (inregs) /* Variable (partially) in registers*/
    {
      dwloc *reglp;
      dwloc *lastlp = NULL;
      symbol *spillloc = NULL;
      int regNum, i, stack = 0;
      int stackchange = port->little_endian ? port->stack.direction : -port->stack.direction;

      if ((spillloc = symloc->usl.spillLoc) && spillloc->onStack)
        stack = (port->little_endian ? spillloc->stack + symloc->nRegs - 1 : spillloc->stack);

      /* register allocation */
      for (i = (port->little_endian ? 0 : symloc->nRegs-1);
           (port->little_endian ? (i < symloc->nRegs) : (i >= 0));
           (port->little_endian ? i++ : i--), stack += stackchange)
        {
          if (!symloc->regs[i]) /* Spilt byte of variable */
            {
              if (!(spillloc && spillloc->onStack))
                {
                  lp = NULL;
                  break;
                }
              reglp = dwNewLoc (DW_OP_fbreg, NULL, stack);
            }
          else /* Byte in registers */
            {
              regNum = port->debugger.dwarf.regNum (symloc->regs[i]);
              if (regNum >= 0 && regNum <= 31)
                reglp = dwNewLoc (DW_OP_reg0 + regNum, NULL, 0);
              else if (regNum >= 0)
                reglp = dwNewLoc (DW_OP_regx, NULL, regNum);
              else
                {
                  /* We are forced to give up if the ABI for this port */
                  /* does not define a number for this register        */
                  lp = NULL;
                  break;
                }
            }
          
          if (lastlp)
            lastlp->next = reglp;
          else
            lp = reglp;
          lastlp = reglp;
          
          if (symloc->nRegs != 1)
            {
              reglp = dwNewLoc (DW_OP_piece, NULL, 1);
              lastlp->next = reglp;
              lastlp = reglp;
            }
        }
    }
  else if (symloc->onStack)
    {
      /* stack allocation */
      lp = dwNewLoc (DW_OP_fbreg, NULL, symloc->stack);
    }
  else
    {
      /* global allocation */
      if (sym->level && !sym->allocreq)
        lp = NULL;
      else
        lp = dwNewLoc (DW_OP_addr, symloc->rname, 0);
    }

  /* Only create the DW_AT_location if a known location exists.   */
  /* It might not exist if the variable has been optimized away   */
  /* or if the compiler has lost track of it (not good, but still */
  /* happens sometimes -- need to improve induction)              */
  if (lp)
    dwAddTagAttr (tp, dwNewAttrLoc (DW_AT_location, lp));
  
  if (!IS_STATIC (sym->etype) && !sym->level)
    dwAddTagAttr (tp, dwNewAttrFlag (DW_AT_external, 1));
  if (IS_EXTERN (sym->etype))
    dwAddTagAttr (tp, dwNewAttrFlag (DW_AT_declaration, 1));
  
  subtp = dwTagFromType (sym->type, scopetp);
  dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp));
  if (!subtp->parent)
    dwAddTagChild (scopetp, subtp);
  
  dwAddTagChild (scopetp, tp);
  return 1;
}
 

/*-----------------------------------------------------------------------*/
/* dwWriteFunction - generate a tag for a function.                      */
/*-----------------------------------------------------------------------*/
int
dwWriteFunction (symbol *sym, iCode *ic)
{
  dwtag * tp;
  value * args;

  /* Add this function to CFI list */
  dwcfilist * cfip;
  cfip = dwNewCFIlist();
  cfip->callsize = 1;
  if (FUNC_ISISR (sym->type))
    cfip->callsize = 3;
  cfip->next = dwCFIRoot;
  dwCFIRoot = cfip;
  dwCFILastLoc = NULL;
  
  dwFuncTag = tp = dwNewTag (DW_TAG_subprogram);
  
  dwAddTagAttr (dwFuncTag, dwNewAttrString (DW_AT_name, sym->name));
  
  dwAddTagAttr (dwFuncTag, dwNewAttrAddrSymbol (DW_AT_low_pc, sym, 0));
  
  if (FUNC_ISISR (sym->type))
    dwAddTagAttr (dwFuncTag, dwNewAttrConst (DW_AT_calling_convention,
                                              DW_CC_nocall));
  
  dwAddTagAttr (dwFuncTag, dwNewAttrFlag (DW_AT_external, 
                                           !IS_STATIC (sym->etype)));

  if (sym->type->next && !IS_VOID (sym->type->next))
    {
      dwtag * subtp;

      subtp = dwTagFromType (sym->type->next, dwRootTag);
      dwAddTagAttr (dwFuncTag, dwNewAttrTagRef (DW_AT_type, subtp));
    }
  dwAddTagChild (dwRootTag, dwFuncTag);
  
  args = FUNC_ARGS(sym->type);
  while (args)
    {
      dwWriteSymbolInternal (args->sym);
      args = args->next;
    }
  if (FUNC_HASVARARGS (sym->type))
    {
      dwAddTagChild (dwFuncTag, dwNewTag (DW_TAG_unspecified_parameters));
    }
  
  while (ic && ic->op != FUNCTION)
    ic = ic->next;
  if (ic && ic->op == FUNCTION && ic->tree && ic->tree->right)
    {
      dwGenerateScopes (dwFuncTag, ic->tree->right->left);
      dwGenerateScopes (dwFuncTag, ic->tree->right->right);
    }
  
  dwScopeTag = NULL;
  dwScopeLevel = 0;
  
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteEndFunction - write attributes to the current function tag     */
/*                      that are only known after code generation is     */
/*                      complete                                         */
/*-----------------------------------------------------------------------*/
int
dwWriteEndFunction (symbol *sym, iCode *ic, int offset)
{
  char debugSym[SDCC_NAME_MAX + 1];
  
  if (ic)
    {
      dwWriteCLine (ic);
      dwLineLast->offset += offset;
      dwLineLast->end_sequence = 1;
    }

  if (IS_STATIC (sym->etype))
    sprintf (debugSym, "XF%s$%s$0$0", dwModuleName, sym->name);
  else
    sprintf (debugSym, "XG$%s$0$0", sym->name);
  emitDebuggerSymbol (debugSym);

  dwAddTagAttr (dwFuncTag, dwNewAttrAddrLabel (DW_AT_high_pc,
                                               Safe_strdup(debugSym),
                                               offset));

  if (dwFrameLocList)
    {
      dwAddTagAttr (dwFuncTag, dwNewAttrLocRef (DW_AT_frame_base,
                                                dwFrameLocList));

      dwFrameLocList->next = dwRootLocList;
      dwRootLocList = dwFrameLocList;
      dwFrameLocList = NULL;
    }

  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteLabel - generate a tag for a source level label                */
/*-----------------------------------------------------------------------*/
int
dwWriteLabel (symbol *sym, const iCode *ic)
{
  char debugSym[SDCC_NAME_MAX + 1];
  dwtag * tp;
  
  /* ignore the compiler generated labels */
  if (sym->isitmp)
    return 1;

  sprintf (debugSym, "L%s$%s$%s", dwModuleName, currFunc->name, sym->name);
  emitDebuggerSymbol (debugSym);

  tp = dwNewTag (DW_TAG_label);
  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sym->name));
  dwAddTagAttr (tp, dwNewAttrAddrLabel (DW_AT_low_pc,
                                        Safe_strdup (debugSym), 0));

  dwAddTagChild (dwFuncTag, tp);
  
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteScope - add the starting and ending address attributes to a    */
/*                a lexical block tag (created during dwWriteFunction)   */
/*-----------------------------------------------------------------------*/
int
dwWriteScope (iCode *ic)
{
  char * debugSym = NULL;
  dwtag * scopetp;
  dwattr * ap;
  
  scopetp = dwFindScope (dwFuncTag->firstChild, ic->block);
  
  if (dwScopeTag && ic->level <= dwScopeLevel)
    {
      debugSym = dwNewDebugSymbol ();
      emitDebuggerSymbol (debugSym);
      dwSetTagAttr (dwScopeTag, dwNewAttrAddrLabel (DW_AT_high_pc, debugSym, 0));
                                                    
      dwScopeTag = scopetp;
      dwScopeLevel = ic->level;
    }
  if (scopetp)
    {
      ap = dwFindAttr (scopetp, DW_AT_low_pc);
      if (ap)
        return 1;
      
      if (!debugSym)
        debugSym = dwNewDebugSymbol ();
      emitDebuggerSymbol (debugSym);
      dwAddTagAttr (scopetp, dwNewAttrAddrLabel (DW_AT_low_pc, debugSym, 0));
                                                 
      dwScopeTag = scopetp;
      dwScopeLevel = ic->level;
    }
  
  return 1;
}

/*-----------------------------------------------------------------------*/
/* dwWriteSymbol - generate tags for global variables. This is actually  */
/*                 called for all variables and parameters, but we       */
/*                 process the non-global variables elsewhere.           */
/*-----------------------------------------------------------------------*/
int
dwWriteSymbol (symbol *sym)
{
  if (IS_FUNC (sym->type))
    return 1;

  /* If it is an iTemp, then it is a local variable; ignore it */
  if (sym->isitmp)
    return 1;

  /* If it is an unused extern ignore it, or it might produce link failure */
  if (IS_EXTERN (sym->etype) && !sym->used)
    return 1;

  /* Ignore parameters; they must be handled specially so that they will */
  /* appear in the correct order */
  if (sym->_isparm)
    return 1;
    
  return dwWriteSymbolInternal (sym);
}


/*-----------------------------------------------------------------------*/
/* dwWriteType                                                           */
/*-----------------------------------------------------------------------*/
int
dwWriteType (structdef *sdef, int block, int inStruct, const char *tag)
{
  /* FIXME: needs implementation */
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteModule - generates the root tag for this compilation unit      */
/*-----------------------------------------------------------------------*/
int
dwWriteModule (const char *name)
{
  dwtag * tp;
  char *verid = (char*)Safe_alloc(125);
  
  dwModuleName = Safe_strdup (name);
  
  sprintf(verid, "SDCC version %s #%s", SDCC_VERSION_STR, getBuildNumber());
    
  tp = dwNewTag (DW_TAG_compile_unit);
  dwAddTagAttr (tp, dwNewAttrString (DW_AT_producer, verid));

  dwAddTagAttr (tp, dwNewAttrConst (DW_AT_language, DW_LANG_C89));

  dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, fullSrcFileName));

  dwAddTagAttr (tp, dwNewAttrLabelRef (DW_AT_stmt_list,
                                       "Ldebug_line_start", -4));

  dwRootTag = tp;
  
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteCLine - generates a line number/position to address record for */
/*                C source                                               */
/*-----------------------------------------------------------------------*/
int
dwWriteCLine (iCode *ic)
{
  dwline * lp;
  char * debugSym;

  if (ic->inlined)
    return 0;

  lp = Safe_alloc (sizeof (dwline));

  lp->line = ic->lineno;
  
  debugSym = dwNewDebugSymbol ();
  emitDebuggerSymbol (debugSym);
  lp->label = debugSym;
  lp->offset = 0;

  lp->fileIndex = dwFindFileIndex (ic->filename);

  if (!dwLineFirst)
    dwLineFirst = lp;
  else
    dwLineLast->next = lp;
  dwLineLast = lp;
  
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteFrameAddress - note the current position of the frame pointer  */
/*                       address. The base address can be specified by   */
/*                       either a register or pointer variable, leaving  */
/*                       the other as NULL. If both are NULL, there is   */
/*                       no current frame pointer address defined.       */
/*-----------------------------------------------------------------------*/
int
dwWriteFrameAddress(const char *variable, struct reg_info *reg, int offset)
{
  char * debugSym = NULL;
  dwlocregion * lrp;
  dwlocregion * cfi_lrp;
  dwloc * lp;
  int regNum;
    
  /* If there was a region open, close it */
  if (dwFrameLastLoc)
    {
      debugSym = dwNewDebugSymbol ();
      emitDebuggerSymbol (debugSym);

      dwFrameLastLoc->endLabel = debugSym;
      dwFrameLastLoc = NULL;
    }

  if (dwCFILastLoc)
    {
      dwCFILastLoc->endLabel = debugSym;
      dwCFIRoot->endLabel = debugSym;
    }


  if (!variable && !reg)
    return 1;

  /* Create a new debugger symbol for the start of the region if */
  /* we can't recycle the symbol at the end of the previous      */
  if (!debugSym)
    {
      debugSym = dwNewDebugSymbol ();
      emitDebuggerSymbol (debugSym);
    }

  lrp = Safe_alloc (sizeof (dwlocregion));
  lrp->startLabel = debugSym;

  /*** Create a new loc region for CFI */
  cfi_lrp = Safe_alloc (sizeof (dwlocregion));
  cfi_lrp->startLabel = debugSym;
  cfi_lrp->loc = dwNewLoc (0, NULL, offset);
  if (dwCFILastLoc)
    {
      dwCFILastLoc->next = cfi_lrp;
    }
  else
    {
      dwCFIRoot->region = cfi_lrp;
      dwCFIRoot->startLabel = debugSym;
    }
  dwCFILastLoc = cfi_lrp;

  if (variable)         /* frame pointer based from a global variable */
    {
      dwloc * lp;

      lrp->loc = dwNewLoc (DW_OP_addr, variable, 0);
      lrp->loc->next = lp = dwNewLoc (DW_OP_deref_size, NULL, NEARPTRSIZE);
      if (offset)
        {
          lp->next = dwNewLoc (DW_OP_consts, NULL, offset);
          lp->next->next = dwNewLoc (DW_OP_plus, NULL, 0);
        }
    }
  else if (reg)         /* frame pointer based from a register */
    {
      regNum = port->debugger.dwarf.regNum (reg);
      assert (regNum>=0);
      
      if (regNum>=0 && regNum<=31)
        {
          if (offset)
            lrp->loc = dwNewLoc (DW_OP_breg0 + regNum, NULL, offset);
          else
            lrp->loc = dwNewLoc (DW_OP_reg0 + regNum, NULL, 0);
        }
      else
        {
          lrp->loc = lp = dwNewLoc (DW_OP_regx, NULL, regNum);
          if (offset)
            {
              lp->next = dwNewLoc (DW_OP_consts, NULL, offset);
              lp->next->next = dwNewLoc (DW_OP_plus, NULL, 0);
            }
        }
    }
  dwFrameLastLoc = lrp;

  if (!dwFrameLocList)
    dwFrameLocList = dwNewLocList();
  lrp->next = dwFrameLocList->region;
  dwFrameLocList->region = lrp;

  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwWriteALine - generates a line number/position to address record for */
/*                assembly source                                        */
/*-----------------------------------------------------------------------*/
int
dwWriteALine(const char *module, int Line)
{
  return 1;
}


/*-----------------------------------------------------------------------*/
/* dwarf2FinalizeFile - write all of the DWARF debugging data to the     */
/*                      debug file                                       */
/*-----------------------------------------------------------------------*/
int
dwarf2FinalizeFile (FILE *of)
{
  int tagAddress = 11;
  int abbrevNum = 0;
  int attr;

  dwarf2FilePtr = of;
  
  /* Write the .debug_line section */
  dwWriteLineNumbers ();
 
  /* Assign the location list addresses (for cross references) */
  dwAssignLocListAddresses ();
  
  /* Write the .debug_loc section */
  dwWriteLocLists ();

  /* Delete our scope related user attributes; they were only needed to help */
  /* build the tag tree and have no meaning to (and may confuse) debuggers   */
  attr = DW_AT_user_block;
  dwTraverseTag (dwRootTag, dwDeleteTagAttr, &attr);
  attr = DW_AT_user_level;
  dwTraverseTag (dwRootTag, dwDeleteTagAttr, &attr);
  
  /* Add a DW_AT_sibling attribute to all tags with children and siblings */
  dwTraverseTag (dwRootTag, dwAddSibAttr, NULL);

  /* Assign the tag abbreviations. The tags, attributes, and forms must   */
  /* not change after this point. The attribute values may change as long */
  /* as the size of the value does not.                                   */
  dwAbbrevTable = newHashTable (128);
  dwTraverseTag (dwRootTag, dwAssignAbbrev, &abbrevNum);
  
  /* Assign the tag addresses (for cross references) */
  dwTraverseTag (dwRootTag, dwAssignTagAddress, &tagAddress);
  
  /* Write the .debug_abbrev section */
  dwWriteAbbrevs (abbrevNum);  
  
  /* Write the .debug_info section */
  dwWriteTags ();

  /* Write the .debug_pubnames section */
  dwWritePubnames ();

  dwWriteFrames ();
  
  return 1;
}

