home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / compiler / sozobon / scsrc20 / top / reg.c < prev    next >
C/C++ Source or Header  |  1991-02-22  |  19KB  |  835 lines

  1. /* Copyright (c) 1989,1991 by Sozobon, Limited.  Author: Tony Andrews
  2.  *
  3.  * Permission is granted to anyone to use this software for any purpose
  4.  * on any computer system, and to redistribute it freely, with the
  5.  * following restrictions:
  6.  * 1) No charge may be made other than reasonable charges for reproduction.
  7.  * 2) Modified versions must be clearly marked as such.
  8.  * 3) The authors are not responsible for any harmful consequences
  9.  *    of using this software, even if they result from defects in it.
  10.  */
  11.  
  12. /*
  13.  * The code in this file deals with "registerizing" local variables and
  14.  * parameters. The general idea is to look for highly referenced local
  15.  * variables and parameters and effectively turn them into register
  16.  * variables automatically. Only the D registers are used, currently, so
  17.  * for pointer variables, a manual "register" declaration in the source
  18.  * code is actually better.
  19.  *
  20.  * We need to be certain of several things about a variable before placing
  21.  * it in a register. It's address must not be taken, and it must not be
  22.  * referred to through "aliases" (e.g. when casting to a shorter object).
  23.  * It must be able to fit in a register. And to keep things like printf from
  24.  * breaking, parameters can only be registerized if none of the parameters
  25.  * have their address taken.
  26.  *
  27.  * The compiler makes this all possible by placing "hints" within the
  28.  * generated assembly code. These hints appear as comments, but are parsed
  29.  * by the optimizer, and the information is stashed away by calling addvar().
  30.  * The hints give us the size and offset of each parameter and local variable.
  31.  * Their names are also given, although that information isn't needed here.
  32.  *
  33.  * There are tradeoffs to be wary of when registerizing. If no register
  34.  * variables exist yet, then "movem" instructions have to be added, requiring
  35.  * more references to make this worthwhile. In the case of parameters, the
  36.  * register has to be initialized from the stack. The four cases are:
  37.  *
  38.  *    Locals    w/ other regs:    1 reference  required
  39.  *        no other regs:    4 references required
  40.  *    Parms    w/ other regs:    2 references required
  41.  *        no other regs:    6 references required
  42.  *
  43.  * The numbers above represent the break-even point based on a savings of
  44.  * 2 bytes per reference, and the incremental cost of adding "movem" or
  45.  * "move" instructions as needed.
  46.  *
  47.  * This optimizes for space only. To optimize for time, each reference would
  48.  * be weighted based on the loop nesting level at which it occurs.
  49.  */
  50.  
  51. #include "top.h"
  52.  
  53. #define    MAXLOCALS    100
  54.  
  55. static    struct    linfo {
  56.     long    offset;        /* offset from A6 */
  57.     int    size;        /* size of the object */
  58.     int    ref;        /* # of references to the local */
  59.     int    reg;        /* reg. we assigned it to */
  60.     int    flags;        /* length, etc. */
  61. } locals[MAXLOCALS];
  62.  
  63. #define    ALIASED        0x1    /* offset is aliased with another */
  64. #define    ADDR_TAKEN    0x2    /* address of the variable was taken */
  65.  
  66. #define    IS_LOCAL(x)    (locals[(x)].offset < 0)
  67. #define    IS_PARM(x)    (locals[(x)].offset > 0)
  68.  
  69. static    bool    paddr;        /* address of a parameter was taken */
  70. static    int    lcnt;        /* number of local variables we've seen */
  71. static    int    rcnt;        /* number of locals that got registerized */
  72.  
  73. static    int    omask, nmask;    /* old and new register masks */
  74.  
  75. /*
  76.  * addvar(size, off) - add a variable entry for the current function
  77.  *
  78.  * These come from hints the compiler gives us about local variables.
  79.  * We use the size and offset here to make sure we don't have aliasing
  80.  * problems with the local variables we want to registerize.
  81.  */
  82. void
  83. addvar(size, off)
  84. int    size;
  85. int    off;
  86. {
  87.     locals[lcnt].offset = off;
  88.     locals[lcnt].size = size;
  89.     locals[lcnt].flags = 0;
  90.     locals[lcnt].ref = 0;
  91.  
  92.     lcnt++;
  93. }
  94.  
  95. /*
  96.  * clrvar() - clear the variable list
  97.  */
  98. void
  99. clrvar()
  100. {
  101.     register int    i;
  102.  
  103.     /*
  104.      * re-initialize the local information
  105.      */
  106.     for (i=0; i < MAXLOCALS ;i++) {
  107.         locals[i].ref = 0;
  108.         locals[i].reg = -1;
  109.         locals[i].flags = 0;
  110.         locals[i].offset = 0;
  111.         locals[i].size = 0;
  112.     }
  113.     paddr = FALSE;
  114.     rcnt = lcnt = 0;
  115. }
  116.  
  117. /*
  118.  * setreg() - try to "registerize" local variables in the given function
  119.  */
  120. void
  121. setreg(bp)
  122. BLOCK    *bp;
  123. {
  124.     void    lcheck(), lassign(), lrewrite();
  125.  
  126.     lcheck(bp);
  127.     lassign();
  128.  
  129. #ifdef    DEBUG
  130.     if (debug)
  131.         dump_table();
  132. #endif
  133.  
  134.     if (rcnt > 0)
  135.         lrewrite(bp);
  136.  
  137.     s_reg += rcnt;        /* keep totals for accounting */
  138. }
  139.  
  140. /*
  141.  * lcheck() - scan for local variable references in the given function
  142.  */
  143. static void
  144. lcheck(bp)
  145. BLOCK    *bp;
  146. {
  147.     void    ckref();
  148.     register int    i;
  149.     register BLOCK    *cb;
  150.     register INST    *ci;
  151.  
  152.     for (cb = bp; cb != NULL ;cb = cb->next) {
  153.         for (ci = cb->first; ci != NULL ;ci = ci->next) {
  154.             ckref(ci, &ci->src);
  155.             ckref(ci, &ci->dst);
  156.         }
  157.     }
  158.  
  159.     /*
  160.      * Now figure out which registers are currently used.
  161.      */
  162.     ci = bp->first->next;
  163.  
  164.     if (ci != NULL && ci->opcode == MOVEM) {
  165.         if (ci->src.amode == REG)
  166.             omask = RM(ci->src.areg);
  167.         else
  168.             omask = stomask(ci->src.astr);
  169.     } else
  170.         omask = 0;
  171. }
  172.  
  173. /*
  174.  * ckref() - check for a local variable reference
  175.  *
  176.  * If a local variable reference is found, it's added to the table or
  177.  * (if already there) its reference count is incremented. If we're
  178.  * taking its address, note that too.
  179.  */
  180. static void
  181. ckref(ip, op)
  182. INST    *ip;
  183. struct    opnd    *op;
  184. {
  185.     register int    i;
  186.     register int    sz;
  187.  
  188.     if (op->amode != REGID || op->areg != A6)
  189.         return;
  190.  
  191.     switch (ip->flags) {
  192.     case LENL:
  193.         sz = 4;
  194.         break;
  195.     case LENW:
  196.         sz = 2;
  197.         break;
  198.     case LENB:
  199.     default:        /* for LEA and PEA */
  200.         sz = 1;
  201.         break;
  202.     }
  203.  
  204.     /*
  205.      * is the local variable already in the table?
  206.      */
  207.     for (i=0; i < lcnt ;i++) {
  208.         if (locals[i].offset == op->disp && locals[i].size == sz) {
  209.             locals[i].ref++;
  210.             break;
  211.         }
  212.     }
  213.  
  214.     /*
  215.      * If not in the table, add an entry for it. If we add an entry
  216.      * here, it must be an alias for one of the entries we got via
  217.      * the compiler hints.
  218.      */
  219.     if (i == lcnt) {
  220.         locals[lcnt].offset = op->disp;
  221.         locals[lcnt].size = sz;
  222.         locals[lcnt].flags = 0;
  223.         locals[lcnt].ref = 1;
  224.  
  225.         lcnt++;
  226.     }
  227.  
  228.     if (ip->opcode == LEA || ip->opcode == PEA) {
  229.         locals[i].flags = ADDR_TAKEN;
  230.         /*
  231.          * If we took the address of a parameter, note that
  232.          * by setting 'paddr'.
  233.          */
  234.         if (IS_PARM(i))
  235.             paddr = TRUE;
  236.     }
  237. }
  238.  
  239. /*
  240.  * lassign() - assign local variable to registers
  241.  *
  242.  * Check for aliases, sort the table, and then decide how to assign
  243.  * the local variables to registers.
  244.  */
  245. static void
  246. lassign()
  247. {
  248.     void    ck_aliases(), sort_table(), do_sort();
  249.     register int    i;
  250.     register int    r;
  251.     int    minlref;    /* min. required references for a local */
  252.     int    minpref;    /* min. required references for a parameter */
  253.  
  254.     ck_aliases();        /* disqualify any "aliased" references */
  255.     sort_table();        /* and sort by reference count */
  256.  
  257.     /*
  258.      * If there were already "movem" instructions, then we should
  259.      * convert as many locals as possible to registers. If we're
  260.      * going to have to add the movem's, then we need at least 4
  261.      * references for this to be worthwhile. The 2 movem instructions
  262.      * take 8 bytes, and each reference conversion saves 2 bytes.
  263.      * This analysis optimizes for size.
  264.      */
  265.     minlref = (omask != 0) ? 1 : 4;
  266.     minpref = (omask != 0) ? 2 : 6;
  267.  
  268.     nmask = omask;
  269.  
  270.     for (i=0, r=D3; r <= D7 ;) {
  271.  
  272.         /*
  273.          * If the register is already in use, skip it.
  274.          */
  275.         if (omask & RM(r)) {
  276.             r++;
  277.             continue;
  278.         }
  279.  
  280.         /*
  281.          * If no more eligible variables, then stop.
  282.          */
  283.         if (locals[i].ref <= 0)
  284.             break;
  285.  
  286.         /*
  287.          * If something meets the minimums, then assign it to
  288.          * the current register, and adjust the minimums.
  289.          */
  290.         if ((IS_LOCAL(i) && locals[i].ref >= minlref) ||
  291.             (IS_PARM(i)  && locals[i].ref >= minpref)) {
  292.             locals[i].reg = r;
  293.             nmask |= RM(r);
  294.             minlref = 1;
  295.             minpref = 2;
  296.             r++;
  297.             i++;
  298.         } else {
  299.             /*
  300.              * If we run into something that isn't referenced
  301.              * enough, disqualify it and re-sort. There might
  302.              * still be something else worth doing.
  303.              */
  304.             locals[i].ref = -locals[i].ref;
  305.             do_sort();
  306.         }
  307.     }
  308.     rcnt = i;
  309. }
  310.  
  311. /*
  312.  * ck_aliases() - check for aliases in the locals table
  313.  *
  314.  * An alias occurs when two different offsets off of A6 both reference
  315.  * the same local. This can happen when casting to a smaller type. Since
  316.  * these references would be a pain to rewrite, we just bag it.
  317.  */
  318. static void
  319. ck_aliases()
  320. {
  321.     static    bool    ck_aref();
  322.     register int    i;
  323.     register int    s;
  324.     register long    d;
  325.  
  326.     for (i=0; i < lcnt ;i++) {
  327.         d = locals[i].offset;
  328.         s = locals[i].size;
  329.  
  330.         if (ck_aref(d, s))
  331.             locals[i].flags |= ALIASED;
  332.     }
  333. }
  334.  
  335. /*
  336.  * ck_aref() - check for an aliased reference
  337.  */
  338. static bool
  339. ck_aref(d, len)
  340. register long    d;
  341. register int    len;
  342. {
  343.     register int    i;
  344.  
  345.     for (i=0; i < lcnt ;i++) {
  346.         if (locals[i].offset == d && locals[i].size == len)
  347.             continue;
  348.  
  349.         if (overlaps(d, len, locals[i].offset, locals[i].size)) {
  350.             locals[i].flags |= ALIASED;
  351.             return TRUE;
  352.         }
  353.     }
  354.     return FALSE;
  355. }
  356.  
  357. static bool
  358. overlaps(d1, s1, d2, s2)
  359. register long    d1, d2;
  360. int        s1, s2;
  361. {
  362.     register long    e1, e2;
  363.  
  364.     e1 = d1 + s1 - 1;
  365.     e2 = d2 + s2 - 1;
  366.  
  367.     if (d1 >= d2 && d1 <= e2)    /* d1 inside d2 <=> e2 */
  368.         return TRUE;
  369.  
  370.     if (e1 >= d2 && e1 <= e2)    /* e1 inside d2 <=> e2 */
  371.         return TRUE;
  372.  
  373.     return FALSE;
  374. }
  375.  
  376. static void
  377. sort_table()
  378. {
  379.     register int    i;
  380.  
  381.     /*
  382.      * Remove uninteresting references from consideration:
  383.      *
  384.      * 1. Variables whose address was taken, or are aliased with another.
  385.      * 2. Variables that don't fit in a register.
  386.      */
  387.     for (i=0; i < lcnt ;i++) {
  388.         if (locals[i].flags&(ADDR_TAKEN|ALIASED) || locals[i].size > 4)
  389.             locals[i].ref = -locals[i].ref;
  390.     }
  391.  
  392.     /*
  393.      * If paddr is set, remove any parameters from consideration. We
  394.      * have to do this so that things like printf (that take the address
  395.      * of a parameter and increment it) don't break. Only if no parameter
  396.      * addresses are taken, can we consider registerizing any of them.
  397.      */
  398.     if (paddr) {
  399.         for (i=0; i < lcnt ;i++) {
  400.             if (IS_PARM(i) && (locals[i].ref > 0))
  401.                 locals[i].ref = -locals[i].ref;
  402.         }
  403.     }
  404.  
  405.     do_sort();
  406. }
  407.  
  408. static void
  409. do_sort()
  410. {
  411.     register int    i;
  412.     struct    linfo    l;
  413.  
  414.     /*
  415.      * simple bubble sort
  416.      */
  417.     for (i=0; i < (lcnt-1) ;) {
  418.         if (locals[i].ref < locals[i+1].ref) {
  419.             l = locals[i];
  420.             locals[i] = locals[i+1];
  421.             locals[i+1] = l;
  422.             if (i > 0)
  423.                 i--;
  424.         } else
  425.             i++;
  426.     }
  427. }
  428.  
  429. /*
  430.  * lrewrite() - rewrite the function based on the new register assignments
  431.  *
  432.  * Fixing the references is easy, but we have to fix up (or add) the movem
  433.  * instructions as well. Also, we call addinits() to initialize any registers
  434.  * that will contain parameters.
  435.  */
  436. static void
  437. lrewrite(bp)
  438. BLOCK    *bp;
  439. {
  440.     void    fixref(), fixmove(), addmovem();
  441.     INST    *findlnk();
  442.     register int    i;
  443.     register BLOCK    *cb;
  444.     register INST    *ci;
  445.  
  446.     /*
  447.      * First, rewrite all the references to the locals that
  448.      * we've reassigned to registers.
  449.      */
  450.     for (cb = bp; cb != NULL ;cb = cb->next) {
  451.         for (ci = cb->first; ci != NULL ;ci = ci->next) {
  452.             fixref(&ci->src);
  453.             fixref(&ci->dst);
  454.         }
  455.     }
  456.  
  457.     /*
  458.      * If the movem's are there, just find them and fix up the
  459.      * register specs.
  460.      */
  461.     ci = bp->first->next;
  462.     if (ci != NULL && ci->opcode == MOVEM) {
  463.  
  464.         /*
  465.          * First, add the initialization instructions.
  466.          */
  467.         addinits(bp, bp->first->next);
  468.  
  469.         fixmove(&ci->src);
  470.  
  471.         for (cb = bp; cb != NULL ;cb = cb->next) {
  472.             if (cb->flags & B_RET) {
  473.                 for (ci=cb->last; ci != NULL ;ci=ci->prev) {
  474.                     if (ci->opcode == MOVEM) {
  475.                         fixmove(&ci->dst);
  476.                         return;
  477.                     }
  478.                 }
  479.             }
  480.         }
  481.         return;
  482.     }
  483.  
  484. #ifdef    DEBUG
  485.     if (debug)
  486.         printf("adding movem instructions\n");
  487. #endif
  488.     /*
  489.      * There aren't any movem instructions, so we have to add
  490.      * them here. What a pain...
  491.      */
  492.     addmovem(bp, findlnk(bp), TRUE);
  493.     addinits(bp, findlnk(bp)->next);
  494.  
  495.     for (cb = bp; cb != NULL ;cb = cb->next) {
  496.         if (cb->last->opcode == RTS) {
  497.             for (ci=cb->last; ci != NULL ;ci=ci->prev) {
  498.                 if (ci->opcode == UNLK) {
  499.                     addmovem(cb, ci, FALSE);
  500.                     return;
  501.                 }
  502.             }
  503.         }
  504.     }
  505.     /*
  506.      * Reaching this point would be an error, you'd think. It means
  507.      * we didn't find the exit from this function. Strangely enough,
  508.      * this can actually happen in routines with infinite loops.
  509.      * Since the "return" block isn't reachable, branch optimization
  510.      * actually removes it. So we can't consider it an error here if
  511.      * we don't find any "unlk" instruction.
  512.      */
  513. }
  514.  
  515. static void
  516. fixmove(op)
  517. struct    opnd    *op;
  518. {
  519.     char    *masktos();
  520.  
  521.     freeop(op);
  522.     op->amode = ABS;
  523.     op->astr = strsave(masktos(nmask));
  524. }
  525.  
  526. /*
  527.  * findlnk() - find the LINK instruction in the given block
  528.  *
  529.  * When profiling, the LINK isn't the first instruction in the entry
  530.  * block. This function lets us handle both cases cleanly.
  531.  */
  532. static    INST *
  533. findlnk(bp)
  534. BLOCK    *bp;
  535. {
  536.     INST    *ip;
  537.  
  538.     for (ip=bp->first; ip != NULL ;ip = ip->next) {
  539.         if (ip->opcode == LINK)
  540.             return ip;
  541.     }
  542.     return NULL;
  543. }
  544.  
  545. static void
  546. addmovem(bp, ip, is_entry)
  547. BLOCK    *bp;            /* block where we're working */
  548. INST    *ip;            /* instruction before or after the movem */
  549. bool    is_entry;        /* true if we're doing the entry code */
  550. {
  551.     char    *masktos();
  552.     register INST    *ni;
  553.     struct    opnd    *op;
  554.     register int    i;
  555.  
  556.     if (ip == NULL)        /* no LINK found */
  557.         return;
  558.  
  559.     /*
  560.      * Allocate and initialize a new instruction
  561.      */
  562.     ni = (INST *) alloc(sizeof(INST));
  563.  
  564.     ni->flags = LENL;
  565.     ni->opcode = MOVEM;
  566.     ni->live = 0;
  567.     ni->rref = ni->rset = 0;
  568.  
  569.     ni->src.areg = ni->dst.areg = 0;
  570.     ni->src.ireg = ni->dst.ireg = 0;
  571.     ni->src.disp = ni->dst.disp = 0;
  572.  
  573.     /*
  574.      * Set up the SP reference
  575.      */
  576.     op = (is_entry) ? &ni->dst : &ni->src;
  577.     op->amode = (is_entry) ? REGI|DEC : REGI|INC;
  578.     op->areg = SP;
  579.  
  580.     /*
  581.      * Set up the register spec operand
  582.      */
  583.     op = (is_entry) ? &ni->src : &ni->dst;
  584.  
  585.     op->amode = ABS;
  586.     op->astr = strsave(masktos(nmask));
  587.  
  588.     /*
  589.      * If there's only one register being used, we should really
  590.      * change the operand to be register direct. This way, the
  591.      * peephole optimization will turn the "movem" into a simple
  592.      * "move". Since we're adding an instruction here, we really
  593.      * need to make it as painless as possible.
  594.      */
  595.     if (rcnt == 1) {
  596.         free(op->astr);
  597.         op->amode = REG;
  598.  
  599.         for (i=D0; i <= D7 ;i++) {
  600.             if (nmask & RM(i)) {
  601.                 op->areg = i;
  602.                 break;
  603.             }
  604.         }
  605.     }
  606.  
  607.     /*
  608.      * Link the instruction into the block
  609.      */
  610.     if (is_entry) {
  611.         ni->next = ip->next;    /* link the MOVEM to its neighbors */
  612.         ni->prev = ip;
  613.  
  614.         ip->next = ni;        /* link its neighbors to the MOVEM */
  615.  
  616.         if (bp->last == ip)
  617.             bp->last = ni;
  618.         else
  619.             ni->next->prev = ni;
  620.     } else {
  621.         ni->next = ip;        /* link the MOVEM to its neighbors */
  622.         ni->prev = ip->prev;
  623.  
  624.         ip->prev = ni;        /* link its neighbors to the MOVEM */
  625.  
  626.         if (bp->first == ip)
  627.             bp->first = ni;
  628.         else
  629.             ni->prev->next = ni;
  630.     }
  631. }
  632.  
  633. static void
  634. addinits(bp, ip)
  635. BLOCK    *bp;            /* block where we're working */
  636. INST    *ip;            /* instruction before the moves */
  637. {
  638.     char    *masktos();
  639.     register INST    *ni;
  640.     struct    opnd    *op;
  641.     register int    i;
  642.  
  643.     if (ip == NULL)        /* no LINK found */
  644.         return;
  645.  
  646.     for (i=0; i < rcnt ;i++) {
  647.         /*
  648.          * If it's a local variable, we don't have to do anything.
  649.          */
  650.         if (IS_LOCAL(i))
  651.             continue;
  652.  
  653.         /*
  654.          * Allocate and initialize a new instruction
  655.          */
  656.         ni = (INST *) alloc(sizeof(INST));
  657.     
  658.         switch (locals[i].size) {
  659.         case 1:
  660.             ni->flags = LENB;
  661.             break;
  662.         case 2:
  663.             ni->flags = LENW;
  664.             break;
  665.         case 4:
  666.             ni->flags = LENL;
  667.             break;
  668.         default:
  669.             fprintf(stderr, "Invalid length\n");
  670.             exit(1);
  671.         }
  672.  
  673.         ni->opcode = MOVE;
  674.         ni->live = 0;
  675.         ni->rref = ni->rset = 0;
  676.         ni->src.ireg = ni->dst.ireg = 0;
  677.     
  678.         /*
  679.          * Set up the variable reference.
  680.          */
  681.         ni->src.amode = REGID;
  682.         ni->src.areg  = A6;
  683.         ni->src.disp  = locals[i].offset;
  684.     
  685.         /*
  686.          * Set up the register spec operand
  687.          */
  688.         ni->dst.amode = REG;
  689.         ni->dst.areg  = locals[i].reg;
  690.         ni->dst.disp  = 0;
  691.  
  692.         /*
  693.          * Link the instruction into the block
  694.          */
  695.         ni->next = ip->next;    /* link MOVE to its neighbors */
  696.         ni->prev = ip;
  697.  
  698.         ip->next = ni;        /* link neighbors to the MOVE */
  699.  
  700.         if (bp->last == ip)
  701.             bp->last = ni;
  702.         else
  703.             ni->next->prev = ni;
  704.     }
  705. }
  706.  
  707. static void
  708. fixref(op)
  709. struct    opnd    *op;
  710. {
  711.     register int    i;
  712.  
  713.     if (op->amode != REGID || op->areg != A6)
  714.         return;
  715.  
  716.     /*
  717.      * Does the reference need to be changed?
  718.      */
  719.     for (i=0; i < rcnt ;i++) {
  720.         if (locals[i].offset == op->disp) {
  721.             op->amode = REG;
  722.             op->areg = locals[i].reg;
  723.             return;
  724.         }
  725.     }
  726. }
  727.  
  728. /*
  729.  * stomask() - convert a register list to a mask
  730.  *
  731.  * Convert a string like "Rm-Rn/Ro-Rp" or "Rm-Rn" to the appropriate
  732.  * mask value.
  733.  */
  734. static int
  735. stomask(s)
  736. char    *s;
  737. {
  738.     register int    mask;
  739.     register char    *p;
  740.  
  741.     mask = dorspec(s);
  742.  
  743.     for (p=s; *p && *p != '/' ;p++)
  744.         ;
  745.  
  746.     if (*p == '/')
  747.         mask |= dorspec(p+1);
  748.  
  749.     return mask;
  750. }
  751.  
  752. /*
  753.  * dorspec() - convert a partial register spec
  754.  *
  755.  * Convert a string like "Rm" or "Rm-Rn" to a mask.
  756.  */
  757. static int
  758. dorspec(s)
  759. register char    *s;
  760. {
  761.     register int    base;
  762.     register int    m, n;
  763.     register int    mask;
  764.  
  765.     base = (s[0] == 'd') ? D0 : A0;
  766.  
  767.     m = s[1] - '0' + base;
  768.  
  769.     if (s[2] != '-')
  770.         return RM(m);
  771.  
  772.     n = s[4] - '0' + base;
  773.  
  774.     for (mask=0; m <= n ;m++)
  775.         mask |= RM(m);
  776.  
  777.     return mask;
  778. }
  779.  
  780. /*
  781.  * masktos() - convert a register mask to a descriptive string
  782.  *
  783.  * Generates a string of the form "Rm/Rn/Ro/..."
  784.  */
  785. static char *
  786. masktos(mask)
  787. register int    mask;
  788. {
  789.     static    char    buf[64];
  790.     register char    *p = buf;
  791.     register int    r;
  792.  
  793.     for (r = D0; r <= D7 ;r++) {
  794.         if (mask & RM(r)) {
  795.             if (p != buf)
  796.                 *p++ = '/';
  797.             *p++ = 'd';
  798.             *p++ = (r - D0) + '0';
  799.         }
  800.     }
  801.     for (r = A0; r <= A7 ;r++) {
  802.         if (mask & RM(r)) {
  803.             if (p != buf)
  804.                 *p++ = '/';
  805.             *p++ = 'a';
  806.             *p++ = (r - A0) + '0';
  807.         }
  808.     }
  809.     *p = '\0';
  810.  
  811.     return buf;
  812. }
  813.  
  814. #ifdef    DEBUG
  815. dump_table()
  816. {
  817.     register int    i;
  818.  
  819.     printf("%d local variables and parameters found\n", lcnt);
  820.     for (i=0; i < lcnt ;i++) {
  821.         printf("len = %d\n", locals[i].size);
  822.         printf("%02d: disp=%3ld, len=%d ref=%2d reg=%s",
  823.             i, locals[i].offset,
  824.             locals[i].size,
  825.             locals[i].ref,
  826.             locals[i].reg >= 0 ? masktos(RM(locals[i].reg)) : "-");
  827.         if (locals[i].flags & ADDR_TAKEN)
  828.             printf(" ADDR_TAKEN");
  829.         if (locals[i].flags & ALIASED)
  830.             printf(" ALIASED");
  831.         printf("\n");
  832.     }
  833. }
  834. #endif
  835.