home *** CD-ROM | disk | FTP | other *** search
- /*
- C* Peephole optimizer.
-
- source: ph.c
- started: October 26, 1985
- version:
- February 22, 1989
-
- PUBLIC DOMAIN SOFTWARE
-
- The CSTAR program was placed in the public domain on June 15, 1991,
- by its author and sole owner,
-
- Edward K. Ream
- 1617 Monroe Street
- Madison, WI 53711
- (608) 257-0802
-
- CSTAR may be used for any commercial or non-commercial purpose.
-
- See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
- */
- #include "cstar.h"
-
- /*
- Declare routines of this file.
- */
- void peep_hole (void);
-
- static void c_delete (register struct node *p);
- static struct node * peep0 (register struct node *p);
- static struct node * peep1 (register struct node *p);
- static struct node * peep2 (register struct node *p);
- static struct node * peep3 (register struct node *p);
- static struct node * peep4 (register struct node *p);
- static struct node * peep5 (register struct node *p);
- static struct node * dec_ref (register struct node *p);
- static int bxx2byy (int code);
-
-
- /*
- ----- P E E P H O L E R O U T I N E S ----
- */
-
- /*
- Unlink node p from doubly-linked code list.
- */
- static void
- c_delete(register struct node *p)
- {
- TRACEP("c_delete", printf("(%p)\n", p));
-
- if (p == NULL) {
- return;
- }
-
- /* Remove the forward link to p. */
- if (p -> c_back == NULL) {
- code_head = p -> c_next;
- }
- else {
- (p -> c_back) -> c_next = p -> c_next;
- }
-
- /* Remove the back link to p. */
- if (p -> c_next == NULL) {
- code_tail = p -> c_back;
- }
- else {
- (p -> c_next) -> c_back = p -> c_back;
- }
- }
-
- /*
- Peephole optimizer loop.
-
- All subsidiary routines must set changed as follows:
-
- changed: TRUE if any optimization was performed.
-
- This almost always finishes in two passes over the code.
-
- */
- static int changed;
- static int advanced;
- static int pass;
-
- void
- peep_hole(void)
- {
- register struct node *p;
- register int g_chg;
-
- TRACE("nopeep", printf("\n>> Disabling Peep Hole...\n\n"); return;);
-
- TRACE("peep_hole", printf("\n>> Peep Hole Optimizer...\n\n"));
-
- pass = 0;
- do {
- g_chg = FALSE;
- changed = FALSE;
- pass++;
-
- p = code_head;
-
- TRACEP("peep_hole", printf("p=%p, pass %d\n\n", p, pass));
-
- while (p) {
- if (changed) {
- g_chg = TRUE;
-
- /* Try to avoid multiple passes. */
- if (p -> c_back) {
- p = p -> c_back;
- }
- }
- changed = FALSE;
-
- p = peep0(p); if (changed) {continue;}
- p = peep1(p); if (changed) {continue;}
- p = peep2(p); if (changed) {continue;}
- p = peep3(p); if (changed) {continue;}
- p = peep4(p); if (changed) {continue;}
- p = peep5(p); if (changed) {continue;}
-
- p = p -> c_next;
- }
- } while (g_chg);
- }
-
- /*
- Peephole optimization 0
-
- X_CLR: convert clr.l dx to moveq.l #0,dx
- X_MOVE: delete move to self--compiler never uses flags based on this
- convert long constant move into A to int if possible
- X_TST: delete test following move or arithmetic (move to self
- deleted first) provided that argument matches. MOVEA
- is no problem since TST A doesn't work anyhow
- X_ADD/ADDA: if constant is added to areg, and const is over 8
- and within -32768 to +32767, convert to LEA.
- X_OLABEL: delete if reference count is 0
-
- Eliminate redundant moves and tests
- */
- static struct node *
- peep0(register struct node *p)
- {
- register struct node *q, *t;
- register struct node *arg1;
- register long value;
-
- TRACEP("peep0v", printf("(%p)\n", p));
-
- switch(p -> c_code) {
- case X_CLR:
- if (pass == 1) {
- if (p -> c_len1 == 4 && is_dloc(p -> c_arg1)) {
- p -> c_arg2 = p -> c_arg1;
- p -> c_arg1 = zero_loc;
- p -> c_code = X_MOVEQ;
- }
- }
- break;
-
- case X_MOVE:
- /*
- NOTE: we never use move-to-self to set the flags, since
- it is useless for that purpose. In any event, if such
- a move did precede an X_TST, it would be gone before
- the attempt to remove the X_TST was completed.
- */
- if (pass == 1) {
- arg1 = p -> c_arg1;
- if (is_equiv(arg1, p -> c_arg2) &&
- !has_effect(arg1 -> n_mode)) {
- q = p -> c_next;
- c_delete(p);
- changed = TRUE;
- return q;
- }
- if (is_zloc(arg1) && !is_aloc(p -> c_arg2)) {
- p -> c_arg1 = p -> c_arg2;
- p -> c_arg2 = NULL;
- p -> c_code = X_CLR;
- }
- else if (p -> c_len1 == 4 && is_aloc(p -> c_arg2) &&
- is_cloc(arg1)) {
- if (!arg1 -> n_cid && arg1 -> n_const < 32767 &&
- arg1 -> n_const >= -32768L) {
- TRACEP("peep0",
- printf("change a0 long move"));
- p -> c_len1 = 2;
- }
- }
- }
- break;
-
- case X_TST:
- /* note that X_TST is not applied to an aloc */
- /* note that X_MOVE and X_ADD might not set the flags */
- arg1 = p -> c_arg1;
- q = p;
- while (q = q -> c_back) {
- switch(q -> c_code) {
- case O_LINENUM:
- continue;
- /* labels should NOT be skipped! */
-
- case X_MOVE:
- case X_ADD:
- case X_SUB:
- case X_EOR:
- case X_OR:
- case X_AND:
- case X_MULS:
- case X_MULU:
- case X_DIVS:
- case X_DIVU:
- case X_EXT:
- TRACEP("peep0",
- printf("X_TST binary: ");
- pr_loc(arg1);
- printf(","); pr_loc(q -> c_arg2);
- printf("; ");
- printf("%d,%d\n", p -> c_len1,
- q -> c_len1);
- );
-
- if (is_equiv(arg1, q -> c_arg2)) {
- if (p -> c_len1 != q -> c_len1) {
- break;
- }
- q = p -> c_next;
- c_delete(p);
- changed = TRUE;
- return q;
- }
- break;
- case X_NEG:
- case X_NOT:
- case X_TST:
- if (is_equiv(arg1, q -> c_arg1)) {
- if (p -> c_len1 != q -> c_len1) {
- break;
- }
- q = p -> c_next;
- c_delete(p);
- changed = TRUE;
- return q;
- }
- break;
- }
- break;
- }
- break;
-
- case O_LABEL:
- if (p -> c_refcount == 0) {
- changed = TRUE;
-
- TRACEP("peep0", printf("remove label: %p\n", p));
-
- q = p -> c_next;
- c_delete(p);
- return q;
- }
- break;
-
- case X_ADD:
- case X_ADDA:
- if (pass != 1) {
- break;
- }
- arg1 = p -> c_arg1;
- if (is_aloc(p -> c_arg2) &&
- !arg1 -> n_cid && is_cloc(arg1)) {
- }
- else {
- break;
- }
-
- TRACEP("peep0", printf("check constant ADDA %p\n", p));
-
- value = arg1 -> n_const;
- if (value < 0) {
- if (value >= -8) {
- p -> c_code = X_SUBQ;
- arg1 -> n_const = -value;
- }
- else if (value >= -32768) {
- p -> c_len1 = 0;
- p -> c_code = X_LEA;
- arg1 -> n_mode = EA_MODE;
- arg1 -> n_reg1 =
- p -> c_arg2 -> n_reg1;
- }
- }
- else if (value == 0) {
- q = p -> c_next;
- changed = TRUE;
- c_delete(p);
- return q;
- }
- else {
- if (value <= 8) {
- p -> c_code = X_ADDQ;
- }
- else if (value <= 32767) {
- p -> c_len1 = 0;
- p -> c_code = X_LEA;
- arg1 -> n_mode = EA_MODE;
- arg1 -> n_reg1 =
- p -> c_arg2 -> n_reg1;
- }
- }
- break;
- }
- return p;
- }
-
- /*
- Peephole optimization 1
-
- Merge adjacent internal labels.
- */
- static struct node *
- peep1(register struct node *p1)
- {
- register int count, type;
- register struct node *p2, *q;
-
- TRACEP("peep1v", printf("(%p)\n", p1));
-
- /* Internal label node ? */
- if (p1 -> c_code != O_LABEL) {
- return p1;
- }
- p2 = p1 -> c_next;
-
- while (p2 && p2 -> c_code == O_LINENUM) {
- p2 = p2 -> c_next;
- }
-
- if (p2 == NULL || p2 -> c_code != O_LABEL) {
- return p1;
- }
-
- /* Replace all references to p1 by references to p2. */
- count = 0;
- for (q = code_head; q; q = q -> c_next) {
- type = q -> c_code;
- if (type == X_BRA || is_bxx(type)) {
- if (q -> c_arg1 == p1) {
- count++;
- q -> c_arg1 = p2;
- }
- }
- }
- if (count != p1 -> c_refcount) {
- fatal("peep1: mismatched reference counts");
- }
-
- /* Do the optimization. */
- TRACEP("peep1", printf("merge adjacent labels: %p, %p\n", p1, p2));
-
- changed = TRUE;
- (p2 -> c_refcount) += count;
- c_delete(p1);
- return p2;
- }
-
- /*
- Peephole optimization 2
-
- Replace bxx lab1
- ...
- lab1: bra lab2
- ...
- lab2:
- by
- bxx lab2
- ...
- lab1: bra lab2
- ...
- lab2:
-
- Increment the refcount of lab2 and decrement refcount of lab1.
- Eliminate lab1 if its reference count falls to zero.
-
- Cycles of jumps in this context indicate potential infinite loops;
- if the cycle closes, there is no way out.
-
- TERMINATION PROOF: if this optimization encounters a closed cycle,
- it fails and generates an error. If it encounters an open cycle,
- it carries the branch reference forward to the end of the cycle,
- and thus eliminates the cycle, and will not succeed again at that
- same place.
-
- At the present time, all other optimizations remove some code,
- so restoring a pattern visible to this one requires removal
- of code and must terminate. This does not rule out the possibility
- of a cyclic interaction between this one and some future optimizer
- that only rearranges code.
- */
- static struct node *
- peep2(register struct node *p)
- {
- register struct node *q, *target;
- register int type;
-
- TRACEP("peep2v", printf("(%p)\n", p));
-
- #ifdef DEBUG
- if (p == NULL) {
- g_error(NULL, "peep2: shouldn't happen");
- return p;
- }
- #endif /* DEBUG */
-
- type = p -> c_code;
-
- if (type != X_BRA && !is_bxx(type)) {
- return p;
- }
-
- /* the current node is a jump of some sort */
- target = NULL;
- q = p -> c_arg1;
- while (q) {
- TRACEP("peep2v", printf("mark: q=%p\n", q));
-
- #ifdef DEBUG
- type = q -> c_code;
- if (type != O_LABEL && type != O_ULABEL) {
- fatal("peep2: jump to a non-label");
- }
- #endif /* DEBUG */
-
- /* Check for cycle and mark the label node. */
- if (q -> c_mark) {
- /* Disable optimization. */
- g_help(NULL, "closed cycle of branches!");
- target = NULL;
- break;
- }
- else {
- q -> c_mark = TRUE;
- }
-
- /* See if the next instruction is an unconditional branch. */
- do {
- q = q -> c_next;
- }
- while (q && q -> c_code == O_LINENUM);
-
- if (q == NULL || (q -> c_code != X_BRA)) {
- break;
- }
-
- /* We have (another) jump to an unconditional jump. */
- target = q = q -> c_arg1;
- TRACEP("peep2", printf("new target=%p\n", target));
- }
-
- /* Unmark all the marked nodes before the optimization. */
- for (q = p -> c_arg1; q && q -> c_mark ;q = q -> c_arg1) {
-
- TRACEP("peep2v", printf("unmark: q=%p\n", q));
-
- q -> c_mark = FALSE;
-
- do {
- q = q -> c_next;
- }
- while (q && q -> c_code == O_LINENUM);
-
- if (q == NULL || (q -> c_code != X_BRA)) {
- break;
- }
- }
-
- /* Do the optimization. */
- if (target) {
-
- TRACEP("peep2",
- printf("retarget branch: %p to %p\n", p, target));
-
- (void) dec_ref(p -> c_arg1);
- p -> c_arg1 = target;
- target -> c_refcount++;
- changed = TRUE;
- }
-
- /* This optimization never alters the code pointer. */
- return p;
- }
-
- /*
- Peephole optimization 3--flip polarity of certain conditional branches
-
- Replace bxx lab1
- bra lab2
- lab1:
- by
- byy lab2
- lab1:
-
- Also adjust and possibly remove lab1.
- */
- static struct node *
- peep3(register struct node *p)
- {
- register struct node *q1, *q2;
-
- TRACEP("peep3v", printf("(%p)\n", p));
-
- /* Conditional jump */
- if (!is_bxx(p -> c_code)) {
- return p;
- }
-
- /* Followed by an unconditional jump? */
- q1 = p -> c_next;
-
- while (q1 && q1 -> c_code == O_LINENUM) {
- q1 = q1 -> c_next;
- }
- if (q1 == NULL || q1 -> c_code != X_BRA) {
- return p;
- }
-
- /* Followed by the target of the first jump? */
- q2 = q1 -> c_next;
- if (q2 == NULL || q2 != p -> c_arg1) {
- return p;
- }
-
- /* Do the optimization. */
- TRACEP("peep3",
- printf("switching bxx polarity: %p\n", p);
- printf("p=%p, q1=%p, q2=%p\n", p, q1, q2));
-
- changed = TRUE;
- p -> c_code = bxx2byy(p -> c_code);
- p -> c_arg1 = q1 -> c_arg1;
- c_delete(q1);
- (void) dec_ref(q2);
-
- return p;
- }
-
- /*
- Peephole optimization 4--delete jump to immediately following label
-
- Replace jmp lab1
- lab1:
-
- by lab1:
-
- Decrement the reference count of lab1 and eliminate lab1 if
- its reference count falls to zero.
- */
- static struct node *
- peep4(register struct node *p)
- {
- register int type;
- register struct node *q;
-
- TRACEP("peep4v", printf("(%p)\n", p));
-
- type = p -> c_code;
-
- if (type != X_BRA && !is_bxx(type)) {
- return p; /* it's not a jump */
- }
-
- /* Does the jump go to the next instruction? */
- q = p -> c_next;
- while (q && q -> c_code == O_LINENUM) {
- q = q -> c_next;
- }
-
- if (q && /* (struct node *) */ p -> c_arg1 == q) {
-
-
- TRACEP("peep4",
- printf("remove jump to next: %p\n", p);
- printf("p=%p, q=%p\n", p, q));
-
- c_delete(p); /* delete the jump */
- q = dec_ref(q); /* and maybe the label as well */
- changed = TRUE;
- return q;
- }
- else {
- return p;
- }
- }
-
- /*
- Peephole optimization 5--remove unreachable code
-
- i.e., all code between an unconditional jump and
- the next label having any references
- */
- static struct node *
- peep5(register struct node *p)
- {
- register struct node *q;
- register int type;
-
- TRACEP("peep5v", printf("(%p)\n", p));
-
- /* Do we have an unconditional jump? */
- if (p -> c_code != X_BRA) {
- return p;
- }
-
- /* Remove the next instruction until it is a label with references */
- /* Adjust reference counts when removing branches! */
- q = p -> c_next;
- while (q) {
- type = q -> c_code;
- if (type == X_BRA || is_bxx(type)) {
- (void) dec_ref(q -> c_arg1);
- }
- else if (type == O_LABEL || type == O_ULABEL) {
- if (q -> c_refcount) {
- TRACEP("peep5v", printf("label found, stop\n"));
- break;
- }
- }
- TRACEP("peep5", printf("remove unreachable code: %p\n", q));
-
- changed = TRUE;
- c_delete(q);
- q = q -> c_next;
- }
- return p;
- }
-
- /*
- Return the negation of a conditional jump code.
- */
- static int
- bxx2byy(int code)
- {
- TRACEP("bxx2byy", printf("(%d)\n", code));
-
- switch(code) {
- case X_BEQ: return X_BNE;
- case X_BNE: return X_BEQ;
-
- case X_BLT: return X_BGE;
- case X_BGE: return X_BLT;
-
- case X_BGT: return X_BLE;
- case X_BLE: return X_BGT;
-
- case X_BHI: return X_BLS; /* Note */
- case X_BLS: return X_BHI;
-
- case X_BCC: return X_BCS;
- case X_BCS: return X_BCC;
-
- case X_BVC: return X_BVS;
- case X_BVS: return X_BVC;
-
- case X_BPL: return X_BMI;
- case X_BMI: return X_BPL;
-
- default:
- printf("bxx2byy: bad code %d\n", code);
- return code;
- }
- }
-
- /*
- Decrement the referece count of a label node and
- remove the label if it falls to zero.
-
- Return the pointer to the label node or the following node
- if the label node was eliminated.
- */
- static struct node *
- dec_ref(register struct node * p)
- {
- register struct node *q;
-
- TRACEP("dec_ref", printf("(%p)\n", p));
-
- #ifdef DEBUG
- if ( (p == NULL) ||
- (p -> c_code != O_LABEL && p -> c_code != O_ULABEL) ||
- (p -> c_refcount) <= 0
- ) {
- fatal("decref: shouldn't happen");
- }
- #endif
-
- p -> c_refcount--;
- if (p -> c_refcount == 0) {
- changed = TRUE;
- TRACE("dec_ref", printf("remove label: %p\n", p));
- q = p -> c_next;
- c_delete(p);
- return q;
- }
- else {
- return p;
- }
- }
-