home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1988 by Sozobon, Limited. Author: Tony Andrews
- *
- * Permission is granted to anyone to use this software for any purpose
- * on any computer system, and to redistribute it freely, with the
- * following restrictions:
- * 1) No charge may be made other than reasonable charges for reproduction.
- * 2) Modified versions must be clearly marked as such.
- * 3) The authors are not responsible for any harmful consequences
- * of using this software, even if they result from defects in it.
- */
- #include "top.h"
-
- static int
- bcomp(bc)
- register int bc;
- {
- switch (bc) {
- case BHI: return BLS;
- case BLS: return BHI;
- case BCC: return BCS;
- case BCS: return BCC;
- case BNE: return BEQ;
- case BEQ: return BNE;
- case BVC: return BVS;
- case BVS: return BVC;
- case BPL: return BMI;
- case BMI: return BPL;
- case BGE: return BLT;
- case BLT: return BGE;
- case BGT: return BLE;
- case BLE: return BGT;
- default:
- fprintf(stderr, "bcomp() - bad branch code %d\n", bc);
- exit(EXIT_FAILURE);
- }
- }
-
- /*
- * isbranch(s) - determines if 's' is a branch opcode
- *
- * Returns 1 for branches whose destination is the first operand,
- * and 2 for branches whose dest. is the second.
- */
- static int
- isbranch(c)
- register int c;
- {
- switch (c) {
- case BRA: case BHI: case BLS: case BCC:
- case BCS: case BNE: case BEQ: case BVC:
- case BVS: case BPL: case BMI: case BGE:
- case BLT: case BGT: case BLE:
- return 1;
-
- case DBRA: case DBHI: case DBLS: case DBCC:
- case DBCS: case DBNE: case DBEQ: case DBMI:
- case DBGE: case DBLT: case DBGT: case DBLE:
- case DBT:
- return 2;
-
- default:
- return 0;
- }
- }
-
- /*
- * cblock(cp) - return the first block containing some code
- *
- * Starting with 'cp', find a block that has one or more instructions
- * in it. This is useful to collapse multiple null blocks into a single
- * logical point.
- */
- static BLOCK *
- cblock(bp)
- register BLOCK *bp;
- {
- while (bp->first == NULL && bp->bcond == NULL) {
- if (bp->bfall == NULL) {
- fprintf(ofp, "cblock() - error in block %s\n",
- bp->name);
- exit(EXIT_FAILURE);
- }
- bp = bp->bfall;
- }
-
- return bp;
- }
-
- /*
- * bsplit() - split up blocks with branches inside them
- *
- * Look for branch instructions in each block. If somewhere in the middle of
- * the block, split up the block. When done, the blocks are broken down into
- * true basic blocks.
- */
- static void
- bsplit(bp)
- BLOCK *bp;
- {
- register BLOCK *cp; /* the current block */
- register BLOCK *np; /* new block (if needed) */
- register INST *ip; /* current instruction */
-
- for (cp = bp; cp != NULL ;cp = cp->chain) {
- for (ip = cp->first; ip != NULL ;ip = ip->next) {
- if (isbranch(ip->opcode) && ip->next != NULL) {
- np = mksym(mktmp());
-
- np->chain = cp->chain;
- cp->chain = np;
-
- np->next = cp->next;
- cp->next = np;
-
- np->first = ip->next;
- np->first->prev = NULL;
- np->last = cp->last;
-
- cp->last = ip;
- cp->last->next = NULL;
-
- } else if (ip->opcode == DC) {
- BLOCK *db;
-
- /*
- * If the instruction is part of a branch
- * table, both the current block and the
- * destination need to be marked as "reached".
- */
- cp->flags |= B_ISREACHED;
-
- if ((db = getsym(ip->src.astr)) != NULL)
- db->flags |= B_ISREACHED;
- else {
- fprintf(stderr, "bsplit() - symbol '%s' not found\n", ip->src.astr);
- exit(EXIT_FAILURE);
- }
- }
- }
- }
- }
-
- /*
- * bfix() - fix up the branch pointers
- *
- * Go through each block setting up 'bcond' and 'bfall' properly. If the
- * last instruction in the block is an unconditional branch, remove it
- * and set 'bfall' instead.
- */
- static void
- bfix(bp)
- register BLOCK *bp;
- {
- register BLOCK *cp; /* current block */
- register INST *ip; /* current instruction */
-
- for (cp = bp; cp != NULL ;cp = cp->chain) {
-
- /* no instructions in the block */
- if (cp->first == NULL) {
- cp->bcond = NULL;
- cp->bfall = cp->next;
- continue;
- }
-
- /* the last instruction is a "return" */
- if (cp->last->opcode == RTS) {
- cp->bcond = cp->bfall = NULL;
- cp->flags |= B_RET;
- continue;
- }
-
- /* the last instruction isn't a branch */
- if (!isbranch(cp->last->opcode)) {
- cp->bcond = NULL;
- cp->bfall = cp->next;
- continue;
- }
-
- /*
- * If we reach this point, then we've got a branch we need
- * to remove at the end of this block.
- */
- cp->bfall = cp->next;
- if (isbranch(cp->last->opcode) == 1) {
- cp->bcode = cp->last->opcode;
- cp->bcond = getsym(cp->last->src.astr);
- } else {
- cp->bcode = -1;
- cp->bcond = getsym(cp->last->dst.astr);
- }
-
- if (cp->bcond == NULL) {
- fprintf(stderr, "top: branch to bad label '%s'\n",
- cp->last->src.astr);
- exit(EXIT_FAILURE);
- }
-
- if (cp->bcode < 0)
- continue;
-
- for (ip = cp->first; ip != NULL ;ip = ip->next) {
- if (ip->next == cp->last) {
- ip->next = NULL;
- break;
- }
- }
- free(cp->last->src.astr);
- free(cp->last);
-
- if (cp->first == cp->last)
- cp->first = cp->last = NULL;
- else
- cp->last = ip;
- /*
- * If the branch was unconditional, we want to represent
- * it as a "fall through", so fix the pointers to do that.
- */
- if (cp->bcode == BRA) {
- s_bdel++;
- cp->bfall = cp->bcond;
- cp->bcond = NULL;
- }
- }
- }
-
- /*
- * bclean() - remove references to empty blocks
- *
- * Called after bsplit() and bfix().
- */
- static void
- bclean(bp)
- register BLOCK *bp;
- {
- register BLOCK *cp;
-
- /*
- * First clean up references to empty blocks
- */
- for (cp = bp; cp != NULL ;cp = cp->chain) {
- if (cp->bcond != NULL)
- cp->bcond = cblock(cp->bcond);
- if (cp->bfall != NULL)
- cp->bfall = cblock(cp->bfall);
- }
-
- /*
- * Now there are generally blocks that are still linked by the
- * 'chain' pointers, but no longer referenced through 'bcond'
- * or 'bfall' pointers. They don't actually need to be deleted
- * since they won't cause trouble anywhere else.
- */
- }
-
- /*
- * bwalk() - recursive walk through the branch graph
- *
- * Starting at the entry point, walk through the block graph placing
- * blocks on the output list. By traversing the "bfall" nodes first
- * we attempt to make blocks that fall through come in order so that
- * branches are minimized.
- *
- *
- * The 'lp' variable below maintains our position in the generated list
- * of blocks.
- */
- static BLOCK *lp; /* pointer to the next block in the file */
-
- #define TOUCHED(bp) ((bp)->flags & B_TOUCHED)
-
- static bwalk(p)
- register BLOCK *p;
- {
- p->flags |= B_TOUCHED;
- lp->next = p;
- lp = lp->next;
-
- /*
- * We should swap 'bfall' and 'bcond' and alter 'bcode' IF:
- *
- * 1. Both bfall and bcond are non-null.
- * 2. The branch is a simple one (not a DBcc inst.)
- * 3. Branch reversals are enabled.
- * 4. The block at bfall has already been touched.
- * 5. The block at bcond hasn't been touched.
- *
- * In this case, we can avoid an extra branch if we reverse the
- * sense of the conditional branch, and swap the pointers.
- */
- if (p->bfall != NULL && p->bcond != NULL && p->bcode >= 0 &&
- do_brev && TOUCHED(p->bfall) && !TOUCHED(p->bcond)) {
- register BLOCK *tmp;
-
- s_brev++;
- tmp = p->bfall;
- p->bfall = p->bcond;
- p->bcond = tmp;
- if ((p->bcode = bcomp(p->bcode)) < 0) {
- fprintf(stderr, "top: internal error in bwalk()\n");
- exit(EXIT_FAILURE);
- }
- }
-
- if (p->bfall != NULL && !TOUCHED(p->bfall))
- bwalk(p->bfall);
-
- if (p->bcond != NULL && !TOUCHED(p->bcond))
- bwalk(p->bcond);
- }
-
- /*
- * bsort() - branch optimization
- *
- * Initialize and terminate the 'next' pointers before and after
- * traversing the branch graph.
- */
- static void
- bsort(bp)
- register BLOCK *bp;
- {
- register BLOCK *cb;
-
- lp = bp;
-
- bwalk(bp); /* traverse the tree starting at the entry point */
-
- /*
- * Now look for other parts of the tree that don't appear to be
- * reachable, but really are. This can happen through the jump
- * tables created for switch statements. For each such block,
- * walk the subtree starting with it.
- */
- for (cb = bp; cb != NULL ;cb = cb->chain) {
- if (!TOUCHED(cb) && (cb->flags & B_ISREACHED))
- bwalk(cb);
- }
-
- lp->next = NULL;
- }
-
- /*
- * bsetlab() - figure out which blocks really need labels
- *
- * This can be called AFTER bsort() to mark the blocks that are going to
- * need labels. Anything that we're going to branch to later gets a label.
- */
- static void
- bsetlab(bp)
- register BLOCK *bp;
- {
- for (; bp != NULL ;bp = bp->next) {
- if (bp->bcond != NULL)
- cblock(bp->bcond)->flags |= B_LABEL;
- if (bp->bfall != NULL && bp->bfall != bp->next)
- cblock(bp->bfall)->flags |= B_LABEL;
- /*
- * If the block is reached via a jump table, then we
- * need a label on THIS block directly.
- */
- if (bp->flags & B_ISREACHED)
- bp->flags |= B_LABEL;
- }
- }
-
- /*
- * bjoin() - join blocks that always come together
- *
- * If block 'b' always follows block 'a' unconditionally, then move the
- * instructions in 'b' to the end of 'a', and remove 'b'. This allows us
- * to do better peephole optimization since we can optimize sequences that
- * used to span blocks.
- */
- bjoin(bp)
- register BLOCK *bp;
- {
- BLOCK *np, *bnext;
-
- for (; bp != NULL ;bp = bnext) {
- bnext = bp->next;
-
- /*
- * First block can't end with a conditional branch.
- */
- if (bp->bcond != NULL)
- continue;
-
- /*
- * Block must fall through to the next thing in the file.
- */
- if (bp->next == NULL || bp->next != bp->bfall)
- continue;
-
- np = bp->next;
-
- /*
- * Second block can't be the destination of a branch.
- */
- if (np->flags & B_LABEL)
- continue;
-
- /*
- * Join the blocks...
- */
- if (np->first != NULL)
- np->first->prev = bp->last;
-
- if (bp->last != NULL)
- bp->last->next = np->first;
- else {
- bp->first = np->first;
- bp->last = np->last;
- }
-
- if (np->flags & B_RET)
- bp->flags |= B_RET;
-
- bp->last = np->last;
- bp->bfall = np->bfall;
- bp->bcond = np->bcond;
- bp->bcode = np->bcode;
- bp->next = np->next;
-
- /*
- * Fix pointers so we don't free the instructions
- * twice later on.
- */
- np->first = NULL;
- np->last = NULL;
-
- /*
- * If we've done a join, then we want to loop again on
- * the current block to see if any others can be joined.
- */
- bnext = bp;
- }
- }
-
- /*
- * bopt() - coordinates branch optimization for the given function
- */
- void
- bopt(bp)
- register BLOCK *bp;
- {
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bsplit()\n");
- #endif
- bsplit(bp);
-
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bfix()\n");
- #endif
- bfix(bp);
-
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bclean()\n");
- #endif
- bclean(bp);
-
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bsort()\n");
- #endif
- bsort(bp);
-
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bsetlab()\n");
- #endif
- bsetlab(bp);
-
- #ifdef DEBUG
- if (debug)
- fprintf(stderr, "bopt() - calling bjoin()\n");
- #endif
- bjoin(bp);
- }
-