home *** CD-ROM | disk | FTP | other *** search
- /* << ACE >>
-
- -- Amiga BASIC Compiler --
-
- ** Optimiser **
- ** Copyright (C) 1998 David Benn
- **
- ** 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
- ** of the License, 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- Author: David J Benn
- Date: 8th,9th,11th,14th June 1992,
- 2nd,5th July 1992,
- 6th December 1992,
- 25th December 1993,
- 5th,9th-11th April 1994,
- 24th July 1994,
- 11th September 1994,
- 11th March 1995
- */
-
- #include "acedef.h"
- #include <string.h>
-
- /* globals */
- CODE *curr, *first, *second;
- char opcode[40], srcopr[40], destopr[40];
- SHORT peep = 0;
-
- /* externals */
- extern CODE *code;
-
- /* functions */
- BOOL is_a_move (char *opcode)
- {
- if (opcode[0] == 'm')
- {
- if (strcmp (opcode, "move.b") == 0)
- return (TRUE);
- else if (strcmp (opcode, "move.w") == 0)
- return (TRUE);
- else if (strcmp (opcode, "move.l") == 0)
- return (TRUE);
- else
- return (FALSE);
- }
- else
- return (FALSE);
- }
-
- BOOL push_pop_pair (void)
- {
- /*
- ** Eliminate: stack push/pop pair
- ** eg: move.l d0,-(sp)
- ** move.l (sp)+,d1 -> move.l d0,d1 [A]
- **
- ** OR
- **
- ** redundant dest/src register pair (from other optimisations)
- ** eg: move.l #3,d0
- ** move.l d0,-4(a4) -> move.l #3,-4(a4) [B]
- **
- ** Don't apply this transformation to instructions which
- ** use labelled (BSS/DATA) items since ACE (esp. in gfx.c)
- ** does things with such items which if optimised can lead
- ** to a loss of valid code (eg. a d0 move may be eliminated
- ** when it is required for a shared library function call;
- ** see the code for COLOR as an example). This should only
- ** be a problem for case [B] above, not case [A] since no
- ** registers are removed in [A].
- */
-
- if (strcmp (first->opcode, second->opcode) == 0 &&
- is_a_move (first->opcode)
-
- &&
-
- ((strcmp (first->destopr, "-(sp)") == 0 &&
- strcmp (second->srcopr, "(sp)+") == 0) /* [A] */
-
- ||
-
- (strcmp (first->destopr, "d0") == 0 &&
- strcmp (second->srcopr, "d0") == 0 &&
- first->srcopr[0] != '_' &&
- second->destopr[0] != '_'))) /* [B] */
- {
- /* copy required code */
- strcpy (opcode, first->opcode);
- strcpy (srcopr, first->srcopr);
- strcpy (destopr, second->destopr);
-
- /* modify first line */
- if (strcmp (srcopr, destopr) != 0)
- change (first, opcode, srcopr, destopr); /* src operand != dest operand */
- else
- {
- /*
- ** src operand == dest operand
- **
- ** eg. move.l d0,-(sp)
- ** move.l (sp)+,d0
- **
- ** = move.l d0,d0 [elimimate this!]
- */
- change (first, "nop", " ", " ");
- peep++;
- }
-
- /* eliminate second line */
- change (second, "nop", " ", " ");
-
- /* skip second line */
- curr = second->next;
-
- peep++;
-
- return (TRUE);
- }
- else
- return (FALSE);
- }
-
- BOOL sign_bit_extension (void)
- {
- /*
- ** Combine: move.b #n,dm
- ** ext.w dm
- **
- ** -> move.w #n,dm
- **
- ** OR
- ** move.w #n,dm
- ** ext.l dm
- **
- ** -> move.l #n,dm
- **
- **
- ** NOT move.w (sp)+,dm
- ** ext.l dm
- **
- ** since this would change a short pop into a long pop!
- **
- ** NOR move.w _mylabel,dm
- ** ext.l dm
- **
- ** since this would try to move data beyond the confines
- ** of the BSS/DATA block starting from _mylabel.
- **
- ** In short, this technique is only useful for immediate mode
- ** addressed operands.
- **
- ** Note that this kind of optimisation may lead to redundant register
- ** moves (eg. compile the 2 line program: 'DEFLNG a-z / X = 1' and
- ** work through the transformations), so push_pop_pair() should be
- ** called after this function.
- */
-
- if (strcmp (second->opcode, "ext.l") == 0 &&
- strcmp (first->opcode, "move.w") == 0 &&
- first->srcopr[0] == '#')
- {
- strcpy (opcode, "move.l");
- strcpy (srcopr, first->srcopr);
- strcpy (destopr, first->destopr);
- change (first, opcode, srcopr, destopr);
- change (second, "nop", " ", " ");
- curr = second->next;
- peep++;
- return (TRUE);
- }
- else if (strcmp (second->opcode, "ext.w") == 0 &&
- strcmp (first->opcode, "move.b") == 0 &&
- first->srcopr[0] == '#')
- {
- strcpy (opcode, "move.w");
- strcpy (srcopr, first->srcopr);
- strcpy (destopr, first->destopr);
- change (first, opcode, srcopr, destopr);
- change (second, "nop", " ", " ");
- curr = second->next;
- peep++;
- return (TRUE);
- }
- else
- return (FALSE);
- }
-
- BOOL negate_constant (void)
- {
- /*
- ** Constant Negation: move.w #n,-(sp)
- ** neg.w (sp)
- ** move.w (sp)+,d0
- ** becomes:
- ** move.w #-n,-(sp)
- ** move.w(sp)+,d0
- **
- ** The latter is then a candidate for removal by push_pop_pair().
- */
-
- if (((strcmp (second->opcode, "neg.w") == 0 &&
- strcmp (first->opcode, "move.w") == 0) ||
-
- (strcmp (second->opcode, "neg.l") == 0 &&
- strcmp (first->opcode, "move.l") == 0)) &&
-
- first->srcopr[0] == '#')
- {
- strcpy (opcode, first->opcode);
- srcopr[0] = '#';
-
- if (first->srcopr[1] != '-')
- {
- /*
- ** Constant is not negative so negate it.
- */
- srcopr[1] = '-';
- srcopr[2] = '\0';
- strcat (srcopr, &first->srcopr[1]);
- }
- else
- {
- /*
- ** Constant is negative so skip '-'.
- */
- srcopr[1] = '\0';
- strcat (srcopr, &first->srcopr[2]);
- }
-
- strcpy (destopr, first->destopr);
- change (first, opcode, srcopr, destopr);
- change (second, "nop", " ", " ");
- curr = second->next;
- peep++;
- return (TRUE);
- }
- else
- return (FALSE);
- }
-
- SHORT peephole (void)
- {
- /*
- ** Perform a series of peephole
- ** optimisations on the code list.
- */
- BOOL past_head;
- int opt_type;
-
- if (code == NULL)
- return (0);
-
- for (opt_type = 1; opt_type <= 4; opt_type++)
- {
- /* Start of code list */
- curr = code;
-
- past_head = FALSE;
-
- do
- {
- /* get next two lines of code (skipping nops) */
- first = curr;
- if (first != NULL)
- {
- second = first->next;
-
- while (second && strcmp (second->opcode, "nop") == 0)
- second = second->next;
-
- curr = second;
- }
- else
- {
- curr = second = NULL;
- }
-
- /*
- ** Remove redundant code in current peephole.
- **
- ** Only do this if we're past the head of the
- ** code list but haven't yet reached the end of
- ** this list.
- */
- if (past_head && second != NULL)
- switch (opt_type)
- {
- case 1:
- negate_constant ();
- break;
-
- case 2:
- push_pop_pair ();
- break;
-
- case 3:
- sign_bit_extension ();
- break;
-
- case 4:
- push_pop_pair ();
- break;
- }
-
- if (!past_head)
- past_head = TRUE;
- }
- while (curr != NULL);
- }
-
- /* total # of removals */
- return (peep);
- }
-
- void optimise (void)
- {
- SHORT opeep;
-
- printf ("\noptimising...\n");
- opeep = peephole ();
- printf ("%d peephole ", opeep);
- if (opeep == 1)
- printf ("removal.");
- else
- printf ("removals."); /* opeep==0 or opeep>1 */
- }
-