mirror of
https://github.com/LIV2/bebbo-gcc.git
synced 2025-12-06 00:23:32 +00:00
6295 lines
150 KiB
C
6295 lines
150 KiB
C
/* Bebbo's Optimizations.
|
|
Copyright (C) 2010-2017 Free Software Foundation, Inc.
|
|
Copyright (C) 2017 Stefan "Bebbo" Franke.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC 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 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC 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 GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
/**
|
|
* SBF (Stefan "Bebbo" Franke):
|
|
*
|
|
* This pass performs multiple optimizations.
|
|
*
|
|
* #1 propagate_moves
|
|
* check if a->b->a can be moved out of a loop.
|
|
*
|
|
* #2 strcpy
|
|
* check if a temp reg can be eliminated.
|
|
*
|
|
* #3 const_comp_sub
|
|
* convert a compare with int constant into sub statement.
|
|
*
|
|
* #4 merge_add
|
|
* merge adds
|
|
*
|
|
* #5 elim_dead_assign
|
|
* eliminate some dead assignments.
|
|
*
|
|
* #6 shrink stack frame
|
|
* remove push/pop for unused variables
|
|
*
|
|
* #7 rename register
|
|
* rename registers without breaking register parameters, inline asm etc.
|
|
*
|
|
* Lessons learned:
|
|
*
|
|
* - do not trust existing code, better delete insns and inster a new one.
|
|
* - do not modify insns, create new insns from pattern
|
|
* - do not reuse registers, create new reg rtx instances
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#define INCLUDE_VECTOR
|
|
#define INCLUDE_SET
|
|
#define INCLUDE_MAP
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "backend.h"
|
|
#include "target.h"
|
|
#include "rtl.h"
|
|
#include "tm_p.h"
|
|
#include "insn-config.h"
|
|
#include "recog.h"
|
|
#include "cfgrtl.h"
|
|
#include "emit-rtl.h"
|
|
#include "tree.h"
|
|
#include "tree-pass.h"
|
|
#include "conditions.h"
|
|
#include "langhooks.h"
|
|
#include "output.h"
|
|
#include <vector>
|
|
#include <set>
|
|
#include <map>
|
|
#include <genrtl.h>
|
|
|
|
//#define XUSE(c) fputc(c, stderr)
|
|
#define XUSE(c)
|
|
|
|
int be_very_verbose;
|
|
bool be_verbose;
|
|
static int pass;
|
|
|
|
bool optimize_this_for_speed_p;
|
|
extern bool
|
|
optimize_function_for_speed_p (struct function *fun);
|
|
|
|
extern struct lang_hooks lang_hooks;
|
|
|
|
/* Lookup of the current function name. */
|
|
extern tree current_function_decl;
|
|
static tree last_function_decl;
|
|
static char fxname[512];
|
|
static char const *
|
|
get_current_function_name ()
|
|
{
|
|
if (current_function_decl == NULL)
|
|
strcpy (fxname, "<toplevel>");
|
|
else
|
|
strncpy (fxname, lang_hooks.decl_printable_name (current_function_decl, 2), 511);
|
|
return fxname;
|
|
}
|
|
|
|
/* a simple log to stdout. */
|
|
static int
|
|
log (char const * fmt, ...)
|
|
{
|
|
if (!be_verbose)
|
|
return 0;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
if (last_function_decl != current_function_decl)
|
|
{
|
|
last_function_decl = current_function_decl;
|
|
printf (":bbb: in '%s'\n", get_current_function_name ());
|
|
}
|
|
printf (":bbb: ");
|
|
int retval = vprintf (fmt, args);
|
|
va_end(args);
|
|
fflush (stdout);
|
|
return retval;
|
|
}
|
|
|
|
enum proepis
|
|
{
|
|
IN_CODE, IN_PROLOGUE, IN_EPILOGUE, IN_EPILOGUE_PARALLEL_POP
|
|
};
|
|
|
|
/**
|
|
* What's needed to track values?
|
|
*/
|
|
class track_var
|
|
{
|
|
/** The cached value.
|
|
* CONST_INT: if < 0x100000000: a real int value
|
|
* else: a value encoded from the line where the value was created.
|
|
* MEM: the rtx
|
|
*/
|
|
rtx value[FIRST_PSEUDO_REGISTER];
|
|
|
|
/*
|
|
* the bitmask of the used registers. needed for invalidation.
|
|
*/
|
|
unsigned used[FIRST_PSEUDO_REGISTER];
|
|
|
|
/**
|
|
* contains the bits containing a value.
|
|
*/
|
|
unsigned andMask[FIRST_PSEUDO_REGISTER];
|
|
|
|
bool
|
|
extend (rtx * z, machine_mode dstMode, rtx x)
|
|
{
|
|
switch (GET_CODE(x))
|
|
{
|
|
case CONST_INT:
|
|
case CONST_FIXED:
|
|
case CONST_DOUBLE:
|
|
case SYMBOL_REF:
|
|
case LABEL_REF:
|
|
// only use full register assignments
|
|
if (GET_MODE_SIZE(dstMode) != 4)
|
|
return false;
|
|
/* these can be used directly. */
|
|
*z = x;
|
|
return true;
|
|
|
|
case REG:
|
|
{
|
|
/* store the reg. */
|
|
if (GET_MODE(x) == dstMode)
|
|
*z = x;
|
|
else
|
|
*z = gen_rtx_REG (dstMode, REGNO(x));
|
|
return true;
|
|
}
|
|
case PLUS:
|
|
case MINUS:
|
|
// handle only in combination with const
|
|
{
|
|
rtx y = XEXP(x, 0);
|
|
if (GET_CODE(y) != SYMBOL_REF && GET_CODE(y) == LABEL_REF && amiga_is_const_pic_ref (y))
|
|
return false;
|
|
|
|
if (GET_CODE(x) == PLUS) // create an own plus to be able to modify the constant offset (later).
|
|
*z = gen_rtx_PLUS(GET_MODE(x), y, XEXP(x, 1));
|
|
else
|
|
*z = gen_rtx_MINUS(GET_MODE(x), y, XEXP(x, 1));
|
|
return true;
|
|
}
|
|
|
|
/* memory reads. */
|
|
case MEM:
|
|
{
|
|
// cache restict and stack spills
|
|
if (MEM_IN_STRUCT_P(x) || MEM_READONLY_P(x))
|
|
{
|
|
*z = x;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
track_var (track_var const * o = 0)
|
|
{
|
|
if (o)
|
|
assign (o);
|
|
else
|
|
for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
value[i] = 0;
|
|
used[i] = 0;
|
|
andMask[i] = 0xffffffff;
|
|
}
|
|
}
|
|
|
|
int
|
|
find_alias (rtx src)
|
|
{
|
|
rtx z = 0;
|
|
if (extend (&z, GET_MODE(src), src))
|
|
{
|
|
for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
// do not alias small int value from -128 ... 127
|
|
if (rtx_equal_p (z, value[i]) && (GET_CODE(z) != CONST_INT || INTVAL(z) > 127 || INTVAL(z) < -128))
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
void
|
|
invalidate_mem (rtx mem, unsigned index)
|
|
{
|
|
rtx z = 0;
|
|
if (extend (&z, GET_MODE(mem), mem))
|
|
{
|
|
rtx reg = 0;
|
|
int iv = 0;
|
|
if (MEM_IN_STRUCT_P(mem))
|
|
{
|
|
rtx x = XEXP(mem, 0);
|
|
if (GET_CODE(x) == PLUS || GET_CODE(x) == MINUS)
|
|
{
|
|
rtx creg = XEXP(x, 0);
|
|
rtx cint = XEXP(x, 1);
|
|
if (REG_P(creg) && CONST_INT_P(cint))
|
|
{
|
|
reg = creg;
|
|
iv = (int) INTVAL(cint);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iv) for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (rtx_equal_p (z, value[i]))
|
|
{
|
|
clear(SImode, i, index);
|
|
continue;
|
|
}
|
|
// check for subreg/strict_low: overlap +/-3 -> clear the cache
|
|
rtx mem = value[i];
|
|
if (reg && mem && MEM_P(mem) && MEM_IN_STRUCT_P(mem))
|
|
{
|
|
rtx pl = XEXP(mem, 0);
|
|
if (GET_CODE (pl) == PLUS || GET_CODE (pl) == MINUS)
|
|
{
|
|
rtx r = XEXP(pl, 0);
|
|
rtx v = XEXP(pl, 1);
|
|
if (rtx_equal_p (r, reg) && CONST_INT_P(v)
|
|
&& (INTVAL(v) + 3 <= iv || INTVAL(v) - 3 >= iv))
|
|
{
|
|
clear(SImode, i, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rtx
|
|
get (unsigned regno) const
|
|
{
|
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|
return 0;
|
|
|
|
return value[regno];
|
|
}
|
|
|
|
unsigned getMask(unsigned regno) const
|
|
{
|
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|
return 0xffffffff;
|
|
|
|
return andMask[regno];
|
|
}
|
|
|
|
void
|
|
set (machine_mode mode, unsigned regno, rtx src, unsigned usedhere, unsigned index)
|
|
{
|
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|
return;
|
|
|
|
if (mode == DImode && GET_CODE(src) == CONST_INT)
|
|
{
|
|
rtx hi = gen_rtx_CONST_INT(VOIDmode, INTVAL(src) >> 32);
|
|
rtx lo = gen_rtx_CONST_INT(VOIDmode, INTVAL(src) & 0xffffffff);
|
|
set(SImode, regno, hi, usedhere, index);
|
|
set(SImode, regno + 1, lo, usedhere, index);
|
|
return;
|
|
}
|
|
|
|
if (mode == SFmode && regno < 16)
|
|
mode = SImode;
|
|
|
|
if (GET_CODE(src) == CONST_INT && (mode == HImode || mode == QImode))
|
|
{
|
|
unsigned iv = UINTVAL(src);
|
|
setMask(regno, iv, mode);
|
|
value[regno] = gen_rtx_CONST_INT(mode, 0x100000000000000LL | ((long long int ) (regno) << 32) | index);
|
|
used[regno] = 1 << FIRST_PSEUDO_REGISTER;
|
|
clearRefsByMask(1<<regno, index);
|
|
}
|
|
else if (extend (&value[regno], mode, src))
|
|
{
|
|
if (GET_CODE(src) == CONST_INT)
|
|
{
|
|
unsigned iv = UINTVAL(src);
|
|
setMask(regno, iv, mode);
|
|
}
|
|
else if (REG_P(src) && REG_NREGS(src) == 1)
|
|
setMask(regno, andMask[REGNO(src)], mode);
|
|
else
|
|
setMask(regno, 0xffffffff, mode);
|
|
|
|
used[regno] = usedhere;
|
|
// convert reg value int regs value.
|
|
if (REG_P(value[regno]))
|
|
{
|
|
unsigned refregno = REGNO(value[regno]);
|
|
rtx val = value[refregno];
|
|
if (!val)
|
|
{
|
|
val = gen_rtx_CONST_INT(mode, 0x100000000000000LL | ((long long int ) (refregno) << 32) | (0xffffffff & -index));
|
|
value[refregno] = val;
|
|
}
|
|
value[regno] = val;
|
|
used[regno] = used[refregno];
|
|
}
|
|
|
|
clearRefsByMask(1<<regno, index);
|
|
}
|
|
else
|
|
{
|
|
clear (mode, regno, index);
|
|
}
|
|
}
|
|
|
|
/** set the mask and combine it with a previous mask if mode size < 4. */
|
|
void
|
|
setMask(unsigned regno, unsigned mask, machine_mode mode)
|
|
{
|
|
if (GET_MODE_SIZE(mode) == 1)
|
|
andMask[regno] = (andMask[regno] & 0xffffff00) | (mask & 0xff);
|
|
else
|
|
if (GET_MODE_SIZE(mode) == 2)
|
|
andMask[regno] = (andMask[regno] & 0xffff0000) | (mask & 0xffff);
|
|
else
|
|
andMask[regno] = mask;
|
|
}
|
|
|
|
bool
|
|
equals (machine_mode dmode, unsigned regno, rtx x)
|
|
{
|
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|
return false;
|
|
|
|
if (x == 0 || value[regno] == 0)
|
|
return false;
|
|
|
|
machine_mode mode = GET_MODE(value[regno]);
|
|
if (dmode == DImode && mode == VOIDmode && GET_CODE(x) == CONST_INT)
|
|
{
|
|
rtx hi = gen_rtx_CONST_INT(VOIDmode, INTVAL(x) >> 32);
|
|
rtx lo = gen_rtx_CONST_INT(VOIDmode, INTVAL(x) & 0xffffffff);
|
|
return equals(VOIDmode, regno, hi) && equals(VOIDmode, regno + 1, lo);
|
|
}
|
|
|
|
machine_mode xmode = GET_MODE(x);
|
|
if (REG_P(x) && REGNO(x) < 16 && xmode == SFmode)
|
|
xmode = SImode;
|
|
|
|
if (mode == SFmode && regno < 16)
|
|
mode = SImode;
|
|
|
|
if (mode != xmode)
|
|
return false;
|
|
|
|
rtx z = 0;
|
|
if (!extend (&z, GET_MODE(x), x))
|
|
return false;
|
|
|
|
return rtx_equal_p (z, value[regno]);
|
|
}
|
|
|
|
void
|
|
clear (machine_mode mode, unsigned regno, unsigned index)
|
|
{
|
|
if (regno >= FIRST_PSEUDO_REGISTER)
|
|
return;
|
|
|
|
if (mode == SFmode && regno < 16)
|
|
mode = SImode;
|
|
value[regno] = gen_rtx_CONST_INT(mode, 0x100000000000000LL | ((long long int ) (regno) << 32) | index);
|
|
used[regno] = 1 << FIRST_PSEUDO_REGISTER;
|
|
setMask(regno, 0xffffffff, mode);
|
|
|
|
clearRefsByMask(1 << regno, index);
|
|
}
|
|
|
|
void
|
|
clearRefsByMask(unsigned mask, unsigned index) {
|
|
// clear also all using this register.
|
|
for (int i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (used[i] & mask) {
|
|
clear(SImode, i, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
clear_aftercall (unsigned index)
|
|
{
|
|
for (int i = 2; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (value[i] && MEM_P(value[i]))
|
|
{
|
|
value[i] = 0;
|
|
used[i] = 0;
|
|
andMask[i] = 0xffffffff;
|
|
}
|
|
}
|
|
clear (SImode, 0, index);
|
|
clear (SImode, 1, index);
|
|
clear (SImode, 8, index);
|
|
clear (SImode, 9, index);
|
|
clear (SImode, 16, index);
|
|
clear (SImode, 17, index);
|
|
}
|
|
|
|
void
|
|
clear_for_mask (unsigned def, unsigned index)
|
|
{
|
|
if (!def)
|
|
return;
|
|
for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
|
|
{
|
|
// register changed or used somehow
|
|
if ((1 << regno) & def)
|
|
clear (SImode, regno, index);
|
|
}
|
|
}
|
|
|
|
void
|
|
assign (track_var const * o)
|
|
{
|
|
for (int i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
value[i] = o->value[i];
|
|
used[i] = o->used[i];
|
|
andMask[i] = o->andMask[i];
|
|
}
|
|
}
|
|
|
|
/* only keep common values in both sides. */
|
|
void
|
|
merge (track_var * o)
|
|
{
|
|
for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (!rtx_equal_p (value[i], o->value[i]))
|
|
{
|
|
value[i] = o->value[i] = 0;
|
|
used[i] = 0;
|
|
andMask[i] = 0xffffffff; // don't want to - but without loop detection
|
|
// this might lead to a elimination of
|
|
// a possible merge source...
|
|
}
|
|
o->andMask[i] = andMask[i] |= o->andMask[i]; // or the masks
|
|
}
|
|
}
|
|
|
|
/* true if a merge would not change anything. */
|
|
bool
|
|
no_merge_needed (track_var const * o) const
|
|
{
|
|
for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (!rtx_equal_p (value[i], o->value[i]) || andMask[i] != (andMask[i] | o->andMask[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/* Information for each insn to detect alive registers. Enough for m68k.
|
|
* Why a class? Maybe extend it for general usage.
|
|
*
|
|
* Track use & def separate to determine starting points.
|
|
*/
|
|
class insn_info
|
|
{
|
|
rtx_insn * insn; // the insn
|
|
|
|
// usage flags - 32 sets also 16,8; 16 sets also 8.
|
|
unsigned myuse8, myuse16, myuse32; // bit set if registers are used in this statement
|
|
unsigned use8, use16, use32; // bit set if registers are used in program flow
|
|
unsigned def8, def16, def32; // bit set if registers are defined here
|
|
|
|
unsigned hard; // bit set if registers can't be renamed
|
|
|
|
enum proepis proepi;
|
|
|
|
bool stack; // part of stack frame insns
|
|
|
|
// stuff to analyze insns
|
|
bool label;
|
|
bool jump;
|
|
bool call;
|
|
bool compare;
|
|
bool dst_mem;
|
|
bool src_mem;
|
|
bool src_plus;
|
|
int src_intval;
|
|
rtx_code src_op;
|
|
bool src_ee;
|
|
bool src_2nd;
|
|
bool src_const;
|
|
|
|
machine_mode mode;
|
|
|
|
rtx dst_reg;
|
|
rtx src_reg;
|
|
|
|
struct m68k_address src_addr;
|
|
int src_mem_addr;
|
|
struct m68k_address dst_addr;
|
|
int dst_mem_addr;
|
|
|
|
bool visited;
|
|
|
|
int sp_offset;
|
|
|
|
int dst_autoinc;
|
|
int src_autoinc;
|
|
|
|
unsigned multi_reg; /* bit field for register pairs. */
|
|
|
|
// values for all variables - if used
|
|
track_var * track;
|
|
|
|
public:
|
|
insn_info (rtx_insn * i = 0, enum proepis p = IN_CODE) :
|
|
insn (i), myuse8 (0), myuse16 (0), myuse32 (0),
|
|
use8 (0), use16(0), use32(0),
|
|
def8 (0), def16(0), def32(0),
|
|
hard (0), proepi (p), stack (false), label (false), jump (false), call (false), compare (false), dst_mem (false), src_mem (false),
|
|
src_plus (false), src_intval(0), src_op ((rtx_code) 0), src_ee (false), src_2nd (false), src_const (false), mode (VOIDmode),
|
|
dst_reg (0), src_reg (0), src_mem_addr(0), dst_mem_addr(0), visited (false), sp_offset (0), dst_autoinc (0), src_autoinc (0), multi_reg(0), track (0)
|
|
{
|
|
memset(&src_addr, 0, sizeof(src_addr));
|
|
memset(&dst_addr, 0, sizeof(dst_addr));
|
|
}
|
|
|
|
unsigned
|
|
get_multi_reg () const
|
|
{
|
|
return multi_reg;
|
|
}
|
|
|
|
track_var *
|
|
get_track_var ();
|
|
|
|
inline ptrdiff_t
|
|
operator < (insn_info const & o) const
|
|
{
|
|
return this - &o;
|
|
}
|
|
|
|
int
|
|
get_index () const;
|
|
|
|
void
|
|
plus_to_move (rtx_insn * newinsn);
|
|
|
|
void
|
|
swap_adds (rtx_insn * newinsn, insn_info & ii);
|
|
|
|
void
|
|
absolute2base (unsigned regno, unsigned base, rtx with_symbol);
|
|
|
|
rtx
|
|
make_absolute2base (unsigned regno, unsigned base, rtx with_symbol, bool apply);
|
|
|
|
inline bool
|
|
is_compare () const
|
|
{
|
|
return compare;
|
|
}
|
|
|
|
inline machine_mode
|
|
get_mode () const
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
inline bool
|
|
is_dst_reg () const
|
|
{
|
|
return dst_reg;
|
|
}
|
|
|
|
inline bool
|
|
is_dst_mem () const
|
|
{
|
|
return dst_mem;
|
|
}
|
|
|
|
inline bool
|
|
is_src_mem () const
|
|
{
|
|
return src_mem;
|
|
}
|
|
|
|
inline bool
|
|
is_src_mem_2nd () const
|
|
{
|
|
return src_2nd && src_mem;
|
|
}
|
|
|
|
inline bool
|
|
has_dst_memreg () const
|
|
{
|
|
return dst_addr.base_loc || dst_addr.index_loc;
|
|
}
|
|
|
|
inline bool
|
|
has_src_memreg () const
|
|
{
|
|
return src_addr.base_loc || src_addr.index_loc;
|
|
}
|
|
|
|
inline rtx_code
|
|
get_src_mem_mem () const
|
|
{
|
|
return (rtx_code)src_addr.code;
|
|
}
|
|
|
|
inline rtx_code
|
|
get_dst_mem_mem () const
|
|
{
|
|
return (rtx_code)dst_addr.code;
|
|
}
|
|
|
|
inline rtx
|
|
get_dst_symbol () const
|
|
{
|
|
return dst_addr.offset;
|
|
}
|
|
|
|
inline rtx
|
|
get_src_symbol () const
|
|
{
|
|
return src_addr.offset;
|
|
}
|
|
inline bool
|
|
has_dst_addr () const
|
|
{
|
|
return dst_mem_addr;
|
|
}
|
|
|
|
inline bool
|
|
has_src_addr () const
|
|
{
|
|
return src_mem_addr;
|
|
}
|
|
|
|
inline bool
|
|
is_label () const
|
|
{
|
|
return label;
|
|
}
|
|
|
|
inline bool
|
|
is_jump () const
|
|
{
|
|
return jump;
|
|
}
|
|
|
|
inline bool
|
|
is_call () const
|
|
{
|
|
return call;
|
|
}
|
|
|
|
inline int
|
|
get_dst_mem_addr () const
|
|
{
|
|
return dst_mem_addr;
|
|
}
|
|
|
|
inline int
|
|
get_src_mem_addr () const
|
|
{
|
|
return src_mem_addr;
|
|
}
|
|
|
|
inline bool
|
|
is_src_reg () const
|
|
{
|
|
return src_reg && !src_op;
|
|
}
|
|
|
|
inline int
|
|
get_src_op () const
|
|
{
|
|
return src_op;
|
|
}
|
|
|
|
inline bool
|
|
is_src_ee () const
|
|
{
|
|
return src_ee;
|
|
}
|
|
|
|
inline int
|
|
get_dst_regno () const
|
|
{
|
|
return dst_reg ? REGNO(dst_reg) : -1;
|
|
}
|
|
|
|
inline int
|
|
get_src_regno () const
|
|
{
|
|
return src_reg ? REGNO(src_reg) : -1;
|
|
}
|
|
|
|
inline rtx
|
|
get_src_reg () const
|
|
{
|
|
return src_reg;
|
|
}
|
|
|
|
inline rtx
|
|
get_dst_reg () const
|
|
{
|
|
return dst_reg;
|
|
}
|
|
|
|
inline int
|
|
get_src_mem_regno () const
|
|
{
|
|
return src_addr.base_loc ? REGNO (*src_addr.base_loc): -1;
|
|
}
|
|
|
|
inline int
|
|
get_src_mem2_regno () const
|
|
{
|
|
return src_addr.index_loc ? REGNO (*src_addr.index_loc): -1;
|
|
}
|
|
|
|
inline int
|
|
get_dst_mem_regno () const
|
|
{
|
|
return dst_addr.base_loc ? REGNO (*dst_addr.base_loc): -1;
|
|
}
|
|
|
|
inline int
|
|
get_dst_mem2_regno () const
|
|
{
|
|
return dst_addr.index_loc ? REGNO (*dst_addr.index_loc): -1;
|
|
}
|
|
|
|
inline rtx
|
|
get_src_mem_reg () const
|
|
{
|
|
return src_addr.base_loc ? *src_addr.base_loc: 0;
|
|
}
|
|
|
|
inline rtx
|
|
get_src_mem2_reg () const
|
|
{
|
|
return src_addr.index_loc ? *src_addr.index_loc: 0;
|
|
}
|
|
|
|
inline rtx
|
|
get_dst_mem_reg () const
|
|
{
|
|
return dst_addr.base_loc ? *dst_addr.base_loc: 0;
|
|
}
|
|
|
|
inline rtx
|
|
get_dst_mem2_reg () const
|
|
{
|
|
return dst_addr.index_loc ? *dst_addr.index_loc: 0;
|
|
}
|
|
|
|
inline int
|
|
get_src_intval () const
|
|
{
|
|
return src_intval;
|
|
}
|
|
|
|
inline bool
|
|
is_src_const () const
|
|
{
|
|
return src_const;
|
|
}
|
|
|
|
inline void
|
|
mark_jump ()
|
|
{
|
|
jump = true;
|
|
}
|
|
inline void
|
|
mark_call ()
|
|
{
|
|
call = true;
|
|
}
|
|
inline void
|
|
mark_label ()
|
|
{
|
|
label = true;
|
|
}
|
|
|
|
void
|
|
fledder (rtx set);
|
|
|
|
void
|
|
fledder_src_mem (rtx src);
|
|
|
|
void
|
|
fledder_dst_mem (rtx src);
|
|
|
|
/* update usage. */
|
|
void
|
|
update (insn_info & o)
|
|
{
|
|
myuse8 = o.myuse8;
|
|
myuse16 = o.myuse16;
|
|
myuse32 = o.myuse32;
|
|
use8 = o.use8;
|
|
use16 = o.use16;
|
|
use32 = o.use32;
|
|
def8 = o.def8;
|
|
def16 = o.def16;
|
|
def32 = o.def32;
|
|
hard = o.hard;
|
|
multi_reg = o.multi_reg;
|
|
}
|
|
|
|
inline void
|
|
reset_use ()
|
|
{
|
|
visited = false;
|
|
use8 = use16 = use32 = 0;
|
|
def8 = def16 = def32 = 0;
|
|
myuse8 = myuse16 = myuse32 = 0;
|
|
hard = 0;
|
|
multi_reg = 0;
|
|
}
|
|
|
|
|
|
inline rtx_insn *
|
|
get_insn () const
|
|
{
|
|
return insn;
|
|
}
|
|
|
|
void
|
|
mark_stack ()
|
|
{
|
|
stack = true;
|
|
}
|
|
|
|
bool
|
|
is_stack () const
|
|
{
|
|
return stack;
|
|
}
|
|
|
|
inline enum proepis
|
|
in_proepi () const
|
|
{
|
|
return proepi;
|
|
}
|
|
|
|
inline void
|
|
set_proepi (enum proepis p)
|
|
{
|
|
proepi = p;
|
|
}
|
|
|
|
inline void
|
|
reset_flags ()
|
|
{
|
|
label = false;
|
|
jump = false;
|
|
compare = false;
|
|
dst_mem = false;
|
|
src_mem = false;
|
|
src_plus = false;
|
|
src_op = (rtx_code) 0;
|
|
src_ee = false;
|
|
src_const = false;
|
|
|
|
mode = VOIDmode;
|
|
|
|
dst_reg = 0;
|
|
src_reg = 0;
|
|
|
|
src_intval = 0;
|
|
|
|
dst_autoinc = 0;
|
|
src_autoinc = 0;
|
|
|
|
memset(&src_addr, 0, sizeof(src_addr));
|
|
memset(&dst_addr, 0, sizeof(dst_addr));
|
|
}
|
|
|
|
inline int
|
|
get_src_autoinc () const
|
|
{
|
|
return src_autoinc;
|
|
}
|
|
|
|
inline int
|
|
get_dst_autoinc () const
|
|
{
|
|
return dst_autoinc;
|
|
}
|
|
|
|
inline bool
|
|
is_empty ()
|
|
{
|
|
return !def8 && !use8 && !hard;
|
|
}
|
|
|
|
/** mark usage here as 32 bit. */
|
|
inline void
|
|
mark_myuse (int regno, int sz)
|
|
{
|
|
myuse8 |= 1 << regno;
|
|
use8 |= 1 << regno;
|
|
if (sz > 1)
|
|
{
|
|
myuse16 |= 1 << regno;
|
|
use16 |= 1 << regno;
|
|
if (sz > 2)
|
|
{
|
|
myuse32 |= 1 << regno;
|
|
use32 |= 1 << regno;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** mark usage here as 32 bit. */
|
|
inline void
|
|
mark_myuse (rtx reg)
|
|
{
|
|
int regno = REGNO(reg);
|
|
int sz = GET_MODE_SIZE(GET_MODE(reg));
|
|
mark_myuse(regno, sz);
|
|
}
|
|
|
|
/** mark usage as 32 bit. */
|
|
inline void
|
|
mark_use (int regno)
|
|
{
|
|
use8 |= 1 << regno;
|
|
use16 |= 1 << regno;
|
|
use32 |= 1 << regno;
|
|
}
|
|
|
|
/** mark def as 32 bit. */
|
|
inline void
|
|
mark_def (int regno)
|
|
{
|
|
def8 |= 1 << regno;
|
|
def16 |= 1 << regno;
|
|
def32 |= 1 << regno;
|
|
}
|
|
|
|
inline void
|
|
mark_hard (int regno)
|
|
{
|
|
hard |= 1 << regno;
|
|
}
|
|
|
|
/** clear usage of full register. */
|
|
inline void
|
|
unset (int regno)
|
|
{
|
|
use8 &= ~(1 << regno);
|
|
use16 &= ~(1 << regno);
|
|
use32 &= ~(1 << regno);
|
|
def8 &= ~(1 << regno);
|
|
def16 &= ~(1 << regno);
|
|
def32 &= ~(1 << regno);
|
|
hard &= ~(1 << regno);
|
|
}
|
|
|
|
/** a register is used if at least a byte is read. */
|
|
inline unsigned
|
|
get_use () const
|
|
{
|
|
return use8 | use16 | use32;
|
|
}
|
|
|
|
inline void
|
|
copy_use (const insn_info & u)
|
|
{
|
|
use8 = u.use8;
|
|
use16 = u.use16;
|
|
use32 = u.use32;
|
|
}
|
|
|
|
/** a register is used if at least a byte is read. */
|
|
inline unsigned
|
|
get_myuse () const
|
|
{
|
|
return myuse8 | myuse16 | myuse32;
|
|
}
|
|
|
|
/** a register is defined if at least a byte is set. */
|
|
inline unsigned
|
|
get_def () const
|
|
{
|
|
return def8;
|
|
}
|
|
|
|
inline unsigned
|
|
get_hard () const
|
|
{
|
|
return hard;
|
|
}
|
|
|
|
inline bool
|
|
is_use (int regno) const
|
|
{
|
|
return ((use8 | use16 | use32) & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline unsigned
|
|
is_use_hi24 (int regno) const
|
|
{
|
|
return ((use16 | use32) & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline unsigned
|
|
is_use32 (int regno) const
|
|
{
|
|
return ((use32) & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline int
|
|
getX(int regno) const
|
|
{
|
|
return ((use8 & (1 << regno)) ? 1 : 0) + ((use16 & (1 << regno)) ? 2 : 0) + ((use32 & (1 << regno)) ? 4 : 0);
|
|
}
|
|
|
|
inline bool
|
|
is_myuse (int regno)
|
|
{
|
|
return ((myuse8 | myuse16 | myuse32) & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline bool
|
|
is_def (int regno)
|
|
{
|
|
return (def8 & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline bool
|
|
is_hard (int regno)
|
|
{
|
|
return (hard & (1 << regno)) != 0;
|
|
}
|
|
|
|
inline void
|
|
clear_hard_def ()
|
|
{
|
|
hard = 0;
|
|
def8 = 0;
|
|
def16 = 0;
|
|
def32 = 0;
|
|
}
|
|
|
|
inline void
|
|
rename_def (unsigned oldbit, unsigned newbit)
|
|
{
|
|
if (def8 & oldbit)
|
|
{
|
|
def8 |= newbit;
|
|
def8 &= ~oldbit;
|
|
}
|
|
if (def16 & oldbit)
|
|
{
|
|
def16 |= newbit;
|
|
def16 &= ~oldbit;
|
|
}
|
|
if (def32 & oldbit)
|
|
{
|
|
def32 |= newbit;
|
|
def32 &= ~oldbit;
|
|
}
|
|
}
|
|
inline void
|
|
rename (unsigned oldbit, unsigned newbit)
|
|
{
|
|
rename_def(oldbit, newbit);
|
|
if (use8 & oldbit)
|
|
{
|
|
use8 |= newbit;
|
|
use8 &= ~oldbit;
|
|
}
|
|
if (use16 & oldbit)
|
|
{
|
|
use16 |= newbit;
|
|
use16 &= ~oldbit;
|
|
}
|
|
if (use32 & oldbit)
|
|
{
|
|
use32 |= newbit;
|
|
use32 &= ~oldbit;
|
|
}
|
|
if (myuse8 & oldbit)
|
|
{
|
|
myuse8 |= newbit;
|
|
myuse8 &= ~oldbit;
|
|
}
|
|
if (myuse16 & oldbit)
|
|
{
|
|
myuse16 |= newbit;
|
|
myuse16 &= ~oldbit;
|
|
}
|
|
if (myuse32 & oldbit)
|
|
{
|
|
myuse32 |= newbit;
|
|
myuse32 &= ~oldbit;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* update for previous insn.
|
|
* - remove regs which are defined here
|
|
* - add regs which are used here
|
|
* - reset _def
|
|
* - restrain _hard to used
|
|
*/
|
|
inline void
|
|
updateWith (insn_info const & o)
|
|
{
|
|
use8 &= ~o.def8;
|
|
use8 |= o.use8;
|
|
def8 = 0;
|
|
use16 &= ~o.def16;
|
|
use16 |= o.use16;
|
|
def16 = 0;
|
|
use32 &= ~o.def32;
|
|
use32 |= o.use32;
|
|
def32 = 0;
|
|
}
|
|
|
|
inline insn_info &
|
|
merge (insn_info const & o)
|
|
{
|
|
myuse8 = o.myuse8;
|
|
myuse16 = o.myuse16;
|
|
myuse32 = o.myuse32;
|
|
use8 = (use8 & ~o.def8) | o.use8;
|
|
use16 = (use16 & ~o.def16) | o.use16;
|
|
use32 = (use32 & ~o.def32) | o.use32;
|
|
def8 |= o.def8;
|
|
def16 |= o.def16;
|
|
def32 |= o.def32;
|
|
|
|
hard |= o.hard;
|
|
multi_reg = o.multi_reg;
|
|
return *this;
|
|
}
|
|
|
|
inline insn_info &
|
|
or_def (insn_info const & o)
|
|
{
|
|
def8 |= o.def8;
|
|
def16 |= o.def16;
|
|
def32 |= o.def32;
|
|
return *this;
|
|
}
|
|
|
|
inline insn_info &
|
|
make_hard ()
|
|
{
|
|
hard |= use8 | def8;
|
|
return *this;
|
|
}
|
|
|
|
inline insn_info &
|
|
make_clobber (machine_mode mode )
|
|
{
|
|
hard = use8 = def8 = use8 | def8;
|
|
if (GET_MODE_SIZE(mode) > 1)
|
|
{
|
|
use16 = def16 = use16 | def16;
|
|
if (GET_MODE_SIZE(mode) > 2)
|
|
use32 = def32 = use32 | def32;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline bool
|
|
contains (insn_info const & o) const
|
|
{
|
|
if ((o.def8) & ~(def8))
|
|
return false;
|
|
if ((o.def16) & ~(def16))
|
|
return false;
|
|
if ((o.def32) & ~(def32))
|
|
return false;
|
|
if ((o.use8) & ~(use8))
|
|
return false;
|
|
if ((o.use16) & ~(use16))
|
|
return false;
|
|
if ((o.use32) & ~(use32))
|
|
return false;
|
|
if (o.hard & ~hard)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
inline int
|
|
get_sp_offset () const
|
|
{
|
|
return sp_offset;
|
|
}
|
|
|
|
inline void
|
|
set_sp_offset (int sp)
|
|
{
|
|
sp_offset = sp;
|
|
}
|
|
|
|
inline bool
|
|
is_visited () const
|
|
{
|
|
return visited;
|
|
}
|
|
|
|
inline void
|
|
mark_visited ()
|
|
{
|
|
visited = true;
|
|
}
|
|
|
|
inline void
|
|
clear_visited ()
|
|
{
|
|
visited = false;
|
|
}
|
|
|
|
void
|
|
scan ();
|
|
|
|
void
|
|
scan_rtx (rtx);
|
|
|
|
bool
|
|
make_post_inc (int regno, int addend);
|
|
|
|
void
|
|
auto_inc_fixup (int regno, int size, int addend);
|
|
|
|
void
|
|
patch_mem_offsets(rtx x, int size);
|
|
|
|
/* return bits for alternate free registers. */
|
|
unsigned
|
|
get_free_mask () const
|
|
{
|
|
if (def8 & hard)
|
|
return 0;
|
|
|
|
if (!def8)
|
|
return 0;
|
|
|
|
unsigned def_no_cc = def8 & ~(1 << FIRST_PSEUDO_REGISTER);
|
|
if (def_no_cc > 0x4000)
|
|
return 0;
|
|
|
|
unsigned mask = def_no_cc - 1;
|
|
/* more than one register -> don't touch. */
|
|
if ((mask & ~def8) != mask)
|
|
return 0;
|
|
|
|
if (def_no_cc > 0xff)
|
|
mask &= 0xff00;
|
|
|
|
return mask & ~(use8|use16|use32);
|
|
}
|
|
|
|
unsigned
|
|
get_regbit () const
|
|
{
|
|
if (GET_MODE_SIZE(mode) > 4)
|
|
return 0;
|
|
return (def8|def16|def32) & ~hard & ~(use8|use16|use32) & 0x7fff;
|
|
}
|
|
|
|
void
|
|
set_insn (rtx_insn * newinsn);
|
|
|
|
void
|
|
a5_to_a7 (rtx a7, int add);
|
|
};
|
|
|
|
bool
|
|
insn_info::make_post_inc (int regno, int addend)
|
|
{
|
|
rtx pattern = PATTERN (insn);
|
|
rtx_insn * new_insn = make_insn_raw (pattern);
|
|
|
|
// convert into POST_INC
|
|
rtx set0 = single_set (new_insn);
|
|
if (!set0)
|
|
return false;
|
|
|
|
rtx set = set0;
|
|
|
|
if (is_compare ())
|
|
set = SET_SRC(set);
|
|
rtx mem;
|
|
|
|
if (get_dst_mem_regno () == regno)
|
|
mem = SET_DEST(set);
|
|
else
|
|
{
|
|
mem = SET_SRC(set);
|
|
if (!MEM_P(mem))
|
|
{
|
|
const char * format = GET_RTX_FORMAT(GET_CODE(mem));
|
|
if (format[0] != 'e')
|
|
return 0;
|
|
if (format[1] == 'e')
|
|
mem = XEXP(mem, 1);
|
|
else
|
|
mem = XEXP(mem, 0);
|
|
}
|
|
}
|
|
|
|
if (!MEM_P(mem))
|
|
return 0;
|
|
|
|
rtx reg = XEXP(mem, 0);
|
|
if (addend < 0)
|
|
{
|
|
if (GET_CODE(reg) != PLUS)
|
|
return 0;
|
|
reg = XEXP(reg, 0);
|
|
}
|
|
|
|
if (!REG_P(reg))
|
|
return 0;
|
|
|
|
XEXP(mem, 0) = gen_rtx_POST_INC(SImode, reg);
|
|
|
|
if (insn_invalid_p (new_insn, 0))
|
|
{
|
|
XEXP(mem, 0) = reg;
|
|
insn_invalid_p (insn, 0);
|
|
return 0;
|
|
}
|
|
|
|
SET_INSN_DELETED(insn);
|
|
(get_dst_mem_regno () == regno ? dst_autoinc : src_autoinc) = GET_MODE_SIZE(mode);
|
|
insn = emit_insn_after (PATTERN (new_insn), insn);
|
|
add_reg_note (insn, REG_INC, reg);
|
|
insn_invalid_p (insn, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static rtx
|
|
add_clobbers (rtx_insn * oldinsn)
|
|
{
|
|
rtx pattern = PATTERN (oldinsn);
|
|
if (GET_CODE(pattern) != PARALLEL)
|
|
return pattern;
|
|
|
|
int num_clobbers = 0;
|
|
for (int j = XVECLEN (pattern, 0) - 1; j >= 0; j--)
|
|
{
|
|
rtx x = XVECEXP(pattern, 0, j);
|
|
if (GET_CODE(x) == CLOBBER)
|
|
++num_clobbers;
|
|
}
|
|
|
|
if (!num_clobbers)
|
|
return pattern;
|
|
|
|
rtx newpat = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (num_clobbers + 1));
|
|
for (int j = XVECLEN (pattern, 0) - 1; j >= 0; j--)
|
|
{
|
|
rtx x = XVECEXP(pattern, 0, j);
|
|
if (GET_CODE(x) == CLOBBER)
|
|
XVECEXP(newpat, 0, num_clobbers--) = x;
|
|
}
|
|
|
|
XVECEXP(newpat, 0, 0) = XVECEXP(pattern, 0, 0);
|
|
return newpat;
|
|
}
|
|
|
|
void insn_info::patch_mem_offsets(rtx x, int offset)
|
|
{
|
|
if (MEM_P(x))
|
|
{
|
|
rtx plus = XEXP(x, 0);
|
|
|
|
if (offset == 0)
|
|
XEXP(x, 0) = XEXP(plus, 0);
|
|
else
|
|
XEXP(plus, 1) = gen_rtx_CONST_INT (VOIDmode, offset);
|
|
|
|
return;
|
|
}
|
|
|
|
const char * format = GET_RTX_FORMAT(GET_CODE(x));
|
|
if (format[0] == 'e')
|
|
patch_mem_offsets(XEXP(x, 0), offset);
|
|
if (format[1] == 'e')
|
|
patch_mem_offsets(XEXP(x, 1), offset);
|
|
}
|
|
|
|
void
|
|
insn_info::auto_inc_fixup (int regno, int size, int addend)
|
|
{
|
|
// fprintf(stderr, ":::"); debug_rtx (insn);
|
|
rtx set0 = single_set (insn);
|
|
rtx set = set0;
|
|
if (is_compare ())
|
|
set = SET_SRC(set);
|
|
|
|
// add to register
|
|
if (get_src_op () == PLUS && !is_src_mem() && !is_dst_mem())
|
|
{
|
|
rtx src = SET_SRC(set);
|
|
if (get_src_intval () == size)
|
|
{
|
|
src_intval = 0;
|
|
src_plus = false;
|
|
src_op = (rtx_code)0;
|
|
SET_SRC(set) = XEXP(src, 0);
|
|
SET_INSN_DELETED(insn);
|
|
return;
|
|
}
|
|
|
|
XEXP(src, 1) = gen_rtx_CONST_INT (VOIDmode, src_intval -= size);
|
|
}
|
|
else if (get_src_mem_regno () == regno)
|
|
{
|
|
int offset = get_src_mem_addr() - size * addend;
|
|
patch_mem_offsets(SET_SRC(set), offset);
|
|
if (offset == 0)
|
|
src_mem_addr = 0;
|
|
else
|
|
src_mem_addr = offset;
|
|
}
|
|
if (get_dst_mem_regno () == regno)
|
|
{
|
|
int offset = get_dst_mem_addr() - size * addend;
|
|
patch_mem_offsets(SET_DEST(set), offset);
|
|
dst_mem_addr = offset;
|
|
}
|
|
|
|
rtx pattern = add_clobbers (insn);
|
|
|
|
SET_INSN_DELETED(insn);
|
|
insn = emit_insn_after (pattern, insn);
|
|
}
|
|
|
|
track_var *
|
|
insn_info::get_track_var ()
|
|
{
|
|
if (!track)
|
|
track = new track_var ();
|
|
return track;
|
|
}
|
|
|
|
void
|
|
insn_info::scan ()
|
|
{
|
|
rtx pattern = PATTERN (insn);
|
|
if (ANY_RETURN_P(pattern))
|
|
{
|
|
tree type = TYPE_SIZE(TREE_TYPE (DECL_RESULT (current_function_decl)));
|
|
int sz = type ? TREE_INT_CST_LOW(type) : 0;
|
|
// log ("return size %d\n", sz);
|
|
if (sz && sz <= 64)
|
|
{
|
|
mark_hard (0);
|
|
mark_myuse (0, sz/8);
|
|
if (sz > 32)
|
|
{
|
|
mark_hard (1);
|
|
mark_myuse (1, 4);
|
|
}
|
|
}
|
|
}
|
|
else if (CALL_P(insn))
|
|
{
|
|
/* add mregparm registers. */
|
|
for (rtx link = CALL_INSN_FUNCTION_USAGE(insn); link; link = XEXP(link, 1))
|
|
{
|
|
rtx op, reg;
|
|
|
|
if (GET_CODE (op = XEXP (link, 0)) == USE && REG_P(reg = XEXP (op, 0)))
|
|
{
|
|
if (REG_NREGS(reg) > 1)
|
|
for (unsigned r = REGNO(reg); r < END_REGNO (reg); ++r)
|
|
mark_myuse (r, 4);
|
|
else
|
|
mark_myuse (reg);
|
|
}
|
|
}
|
|
/* mark stack pointer used. there could be parameters on stack*/
|
|
mark_myuse (15, 4);
|
|
/* mark scratch registers. */
|
|
mark_def (0);
|
|
mark_def (1);
|
|
mark_def (8);
|
|
mark_def (9);
|
|
mark_def (16);
|
|
mark_def (17);
|
|
/* also mark all registers as not renamable */
|
|
hard = use8;
|
|
}
|
|
if (CALL_P(insn) || ANY_RETURN_P(pattern))
|
|
{
|
|
for (unsigned i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
if (global_regs[i])
|
|
{
|
|
mark_hard (i);
|
|
mark_myuse(i, 4);
|
|
}
|
|
}
|
|
scan_rtx (pattern);
|
|
}
|
|
|
|
/* scan rtx for registers and set the corresponding flags. */
|
|
void
|
|
insn_info::scan_rtx (rtx x)
|
|
{
|
|
if (REG_P(x) || ((GET_CODE(x) == STRICT_LOW_PART || GET_CODE(x) == SUBREG) && REG_P(XEXP(x, 0))))
|
|
{
|
|
if (!REG_P(x))
|
|
x = XEXP(x, 0);
|
|
|
|
int n0 = REG_NREGS(x);
|
|
if (n0 > 1)
|
|
{
|
|
for (int n = n0, r = REGNO(x); n > 0; --n, ++r)
|
|
{
|
|
mark_myuse (r, 4);
|
|
multi_reg |= 1<<r;
|
|
}
|
|
}
|
|
else
|
|
mark_myuse(x);
|
|
return;
|
|
}
|
|
|
|
if (x == cc0_rtx)
|
|
{
|
|
mark_myuse (FIRST_PSEUDO_REGISTER, 4);
|
|
return;
|
|
}
|
|
|
|
RTX_CODE code = GET_CODE(x);
|
|
|
|
/* handle SET and record use and def. */
|
|
if (code == SET)
|
|
{
|
|
unsigned u8 = use8;
|
|
unsigned u16 = use16;
|
|
unsigned u32 = use32;
|
|
unsigned mu8 = myuse8;
|
|
unsigned mu16 = myuse16;
|
|
unsigned mu32 = myuse32;
|
|
use8 = myuse8 = 0;
|
|
use16 = myuse16 = 0;
|
|
use32 = myuse32 = 0;
|
|
rtx dst = SET_DEST(x);
|
|
scan_rtx (dst);
|
|
if (REG_P(dst) || ((GET_CODE(dst) == STRICT_LOW_PART || GET_CODE(dst) == SUBREG) && REG_P(XEXP(dst, 0))))
|
|
{
|
|
def8 |= use8;
|
|
def16 |= use16;
|
|
def32 |= use32;
|
|
|
|
use8 = u8;
|
|
myuse8 = mu8;
|
|
use16 = u16;
|
|
myuse16 = mu16;
|
|
use32 = u32;
|
|
myuse32 = mu32;
|
|
}
|
|
|
|
// avoid side effects from myuse -> def, e.g. adding the dst reg to def by src auto inc
|
|
mu8 = myuse8;
|
|
mu16 = myuse16;
|
|
mu32 = myuse32;
|
|
|
|
myuse8 = 0;
|
|
myuse16 = 0;
|
|
myuse32 = 0;
|
|
// this updates myuse8/16/32
|
|
scan_rtx (SET_SRC(x));
|
|
myuse8 |= mu8;
|
|
myuse16 |= mu16;
|
|
myuse32 |= mu32;
|
|
|
|
int code = GET_CODE(SET_SRC(x));
|
|
if (code == ASM_OPERANDS)
|
|
hard |= def8 | use8 | use16 | use32;
|
|
return;
|
|
}
|
|
|
|
if (code == TRAP_IF)
|
|
{
|
|
/* mark all registers used. */
|
|
hard = use8 = myuse8 = use16 = myuse16 = use32 = myuse32 = (1 << FIRST_PSEUDO_REGISTER) - 1;
|
|
return;
|
|
}
|
|
|
|
unsigned prevu16 = use16;
|
|
unsigned prevu32 = use32;
|
|
|
|
const char *fmt = GET_RTX_FORMAT(code);
|
|
for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|
{
|
|
if (fmt[i] == 'e')
|
|
scan_rtx (XEXP(x, i));
|
|
else if (fmt[i] == 'E')
|
|
for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
|
|
{
|
|
unsigned u8 = use8;
|
|
unsigned u16 = use16;
|
|
unsigned u32 = use32;
|
|
unsigned mu8 = myuse8;
|
|
unsigned mu16 = myuse16;
|
|
unsigned mu32 = myuse32;
|
|
unsigned d8 = def8;
|
|
unsigned d16 = def16;
|
|
unsigned d32 = def32;
|
|
unsigned mr = multi_reg;
|
|
scan_rtx (XVECEXP(x, i, j));
|
|
use8 |= u8;
|
|
use16 |= u16;
|
|
use32 |= u32;
|
|
myuse8 |= mu8;
|
|
myuse16 |= mu16;
|
|
myuse32 |= mu32;
|
|
def8 |= d8;
|
|
def16 |= d16;
|
|
def32 |= d32;
|
|
multi_reg |= mr;
|
|
if ((def8 - 1) & def8)
|
|
multi_reg |= def8;
|
|
}
|
|
}
|
|
|
|
/* handle
|
|
* AND.W (REG 255)
|
|
* AND.L (REG 255)
|
|
* AND.L (REG 65535)
|
|
* to reduce the use
|
|
*/
|
|
if (code == AND && REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1)))
|
|
{
|
|
unsigned regno = REGNO (XEXP (x, 0));
|
|
unsigned val = INTVAL (XEXP (x, 1));
|
|
if (val <= 0xff)
|
|
{
|
|
myuse32 &= ~(1<<regno);
|
|
use32 = prevu32 | myuse32;
|
|
myuse16 &= ~(1<<regno);
|
|
use16 = prevu16 | myuse16;
|
|
}
|
|
else if (val <= 0xffff)
|
|
{
|
|
myuse32 &= ~(1<<regno);
|
|
use32 = prevu32 | myuse32;
|
|
}
|
|
}
|
|
|
|
|
|
if (code == POST_INC || code == PRE_DEC || code == CLOBBER)
|
|
{ def8 |= myuse8; def16 |= myuse16; def32 |= myuse32;}
|
|
if (code == CLOBBER)
|
|
multi_reg |= def8;
|
|
}
|
|
|
|
void
|
|
insn_info::fledder_src_mem (rtx src)
|
|
{
|
|
memset(&src_addr, 0, sizeof(dst_addr));
|
|
src_mem = true;
|
|
decompose_mem (GET_MODE_SIZE(mode), &XEXP(src, 0), &src_addr, 1);
|
|
src_autoinc = src_addr.code != 0 && src_addr.code != MEM;
|
|
|
|
if (src_addr.offset && ! SYMBOL_REF_P(src_addr.offset) && GET_CODE(src_addr.offset) != LABEL_REF && GET_CODE(src_addr.offset) != UNSPEC)
|
|
{
|
|
if (CONST_INT_P(src_addr.offset))
|
|
{
|
|
src_mem_addr = INTVAL(src_addr.offset);
|
|
src_addr.offset = 0;
|
|
}
|
|
else // PLUS
|
|
{
|
|
src_mem_addr = INTVAL(XEXP(src_addr.offset, 1));
|
|
src_addr.offset = XEXP(src_addr.offset, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
insn_info::fledder_dst_mem (rtx dst)
|
|
{
|
|
memset(&dst_addr, 0, sizeof(dst_addr));
|
|
dst_mem = true;
|
|
decompose_mem (GET_MODE_SIZE(mode), &XEXP(dst, 0), &dst_addr, 1);
|
|
dst_autoinc = dst_addr.code != 0 && dst_addr.code != MEM;
|
|
|
|
if (dst_addr.offset && ! SYMBOL_REF_P(dst_addr.offset) && GET_CODE(dst_addr.offset) != LABEL_REF && GET_CODE(dst_addr.offset) != UNSPEC)
|
|
{
|
|
if (CONST_INT_P(dst_addr.offset))
|
|
{
|
|
dst_mem_addr = INTVAL(dst_addr.offset);
|
|
dst_addr.offset = 0;
|
|
}
|
|
else // PLUS
|
|
{
|
|
dst_mem_addr = INTVAL(XEXP(dst_addr.offset, 1));
|
|
dst_addr.offset = XEXP(dst_addr.offset, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* read the set and grab infos */
|
|
void
|
|
insn_info::fledder (rtx set)
|
|
{
|
|
if (!set || GET_CODE(set) == PARALLEL)
|
|
return;
|
|
|
|
rtx dst = SET_DEST(set);
|
|
rtx src = SET_SRC(set);
|
|
|
|
if (dst == cc0_rtx)
|
|
{
|
|
compare = true;
|
|
set = src;
|
|
dst = SET_DEST(set);
|
|
src = SET_SRC(set);
|
|
}
|
|
|
|
if (GET_CODE(dst) == STRICT_LOW_PART || GET_CODE(dst) == SUBREG)
|
|
dst = XEXP(dst, 0);
|
|
|
|
if (GET_CODE(src) == FIX)
|
|
src = XEXP(src, 0);
|
|
|
|
mode = GET_MODE(dst);
|
|
if (mode == VOIDmode)
|
|
mode = GET_MODE(src);
|
|
|
|
if (REG_P(dst))
|
|
{
|
|
dst_reg = dst;
|
|
}
|
|
else if (MEM_P(dst))
|
|
{
|
|
fledder_dst_mem(dst);
|
|
}
|
|
|
|
/* It' some kind of operation, e.g. PLUS, XOR, NEG, ... */
|
|
rtx alt_src_reg = 0;
|
|
int code = GET_CODE(src);
|
|
if (!REG_P(src) && !MEM_P(src) && code != CONST_INT && code != CONST && code != CONST_WIDE_INT && code != CONST_DOUBLE
|
|
&& code != CONST_FIXED && code != CONST_STRING)
|
|
{
|
|
src_op = GET_CODE(src);
|
|
const char *fmt = GET_RTX_FORMAT(code);
|
|
if (fmt[0] == 'e' && fmt[1] == 'e')
|
|
{
|
|
src_ee = true;
|
|
rtx operand = XEXP(src, 1);
|
|
if (GET_CODE(operand) == CONST_INT || GET_CODE(operand) == CONST_WIDE_INT)
|
|
src_const = true, src_intval = INTVAL(operand);
|
|
else if (REG_P(operand))
|
|
{
|
|
alt_src_reg = operand;
|
|
}
|
|
else if (MEM_P(operand))
|
|
{
|
|
// it' something like reg = op(reg, mem(...))
|
|
src_2nd = true;
|
|
fledder_src_mem (operand);
|
|
}
|
|
}
|
|
src = XEXP(src, 0);
|
|
}
|
|
|
|
if (REG_P(src))
|
|
{
|
|
src_reg = src;
|
|
}
|
|
else if (MEM_P(src))
|
|
{
|
|
fledder_src_mem (src);
|
|
}
|
|
else if (GET_CODE(src) == CONST_INT)
|
|
{
|
|
src_const = true;
|
|
src_intval = INTVAL(src);
|
|
}
|
|
if (alt_src_reg)
|
|
src_reg = alt_src_reg;
|
|
}
|
|
|
|
/* create a copy for a reg. Optional specify a new register number. */
|
|
static rtx
|
|
copy_reg (rtx reg, int newregno)
|
|
{
|
|
if (newregno < 0)
|
|
newregno = REGNO(reg);
|
|
rtx x = gen_raw_REG (GET_MODE(reg), newregno);
|
|
x->jump = reg->jump;
|
|
x->call = reg->call;
|
|
x->unchanging = reg->unchanging;
|
|
x->volatil = reg->volatil;
|
|
x->in_struct = reg->in_struct;
|
|
x->used = reg->used;
|
|
x->frame_related = reg->frame_related;
|
|
x->return_val = reg->return_val;
|
|
|
|
x->u.reg.attrs = reg->u.reg.attrs;
|
|
return x;
|
|
}
|
|
|
|
/* Rename the register plus track all locs to undo these changes. */
|
|
static void
|
|
find_regs_by_no (rtx x, unsigned oldregno,std::set<rtx *> & regs)
|
|
{
|
|
if (!x)
|
|
return;
|
|
|
|
RTX_CODE code = GET_CODE(x);
|
|
|
|
const char *fmt = GET_RTX_FORMAT(code);
|
|
for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|
{
|
|
if (fmt[i] == 'e')
|
|
{
|
|
rtx y = XEXP(x, i);
|
|
if (REG_P(y))
|
|
{
|
|
if (REGNO(y) == oldregno)
|
|
regs.insert(&XEXP(x, i));
|
|
}
|
|
else
|
|
find_regs_by_no (y, oldregno, regs);
|
|
}
|
|
else if (fmt[i] == 'E')
|
|
for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
|
|
{
|
|
rtx z = XVECEXP(x, i, j);
|
|
find_regs_by_no (z, oldregno, regs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Collect some data.
|
|
*/
|
|
static std::vector<insn_info> *infos;
|
|
typedef std::vector<insn_info>::iterator insn_info_iterator;
|
|
|
|
// insn->u2.insn_uid -> rtx_insn *
|
|
static std::multimap<int, rtx_insn *> *label2jump;
|
|
typedef std::multimap<int, rtx_insn *>::iterator l2j_iterator;
|
|
|
|
// index -> index
|
|
static std::multimap<unsigned, unsigned> *jump2label;
|
|
typedef std::multimap<unsigned, unsigned>::iterator j2l_iterator;
|
|
|
|
static std::map<rtx_insn *, insn_info *> *insn2info;
|
|
typedef std::map<rtx_insn *, insn_info *>::iterator i2i_iterator;
|
|
|
|
static std::set<unsigned> *scan_starts;
|
|
typedef std::set<unsigned>::iterator su_iterator;
|
|
|
|
static insn_info * info0;
|
|
static unsigned usable_regs;
|
|
|
|
static void
|
|
update_insn_infos (std::set<unsigned> & todo = *scan_starts);
|
|
static unsigned
|
|
track_regs ();
|
|
|
|
static void
|
|
update_insn2index ()
|
|
{
|
|
infos->reserve (infos->size () * 8 / 7 + 2);
|
|
insn2info->clear ();
|
|
/* needs a separate pass since the insn_infos require fixed addresses for ->get_index() */
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
insn_info & ii = (*infos)[i];
|
|
insn2info->insert (std::make_pair (ii.get_insn (), &ii));
|
|
}
|
|
info0 = &(*infos)[0];
|
|
}
|
|
|
|
static void
|
|
update_label2jump ()
|
|
{
|
|
update_insn2index ();
|
|
jump2label->clear();
|
|
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.is_label ())
|
|
for (l2j_iterator i = label2jump->find (ii.get_insn ()->u2.insn_uid), k = i;
|
|
i != label2jump->end () && i->first == k->first; ++i)
|
|
jump2label->insert (std::make_pair (insn2info->find (i->second)->second->get_index (), index));
|
|
}
|
|
}
|
|
|
|
int
|
|
insn_info::get_index () const
|
|
{
|
|
insn_info * ii = &(*infos)[0];
|
|
|
|
if (ii == info0)
|
|
{
|
|
ptrdiff_t diff = ((char const *) this - (char const *) ii);
|
|
unsigned pos = diff / sizeof(insn_info);
|
|
if (pos < infos->size ())
|
|
return pos;
|
|
}
|
|
|
|
// realloc happened...
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
if ((*infos)[i].get_insn () == this->insn)
|
|
return i;
|
|
|
|
// whoops!?
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
insn_info::plus_to_move (rtx_insn * newinsn)
|
|
{
|
|
insn = newinsn;
|
|
src_op = (rtx_code) 0;
|
|
src_reg = XEXP(PATTERN (newinsn), 1);
|
|
insn2info->insert (std::make_pair (insn, this));
|
|
// usage flags did not change
|
|
}
|
|
|
|
void
|
|
insn_info::swap_adds (rtx_insn * newinsn, insn_info & ii)
|
|
{
|
|
insn = newinsn;
|
|
|
|
std::swap (*this, ii);
|
|
|
|
insn2info->insert (std::make_pair (insn, this));
|
|
insn2info->insert (std::make_pair (ii.insn, &ii));
|
|
|
|
// usage flags did not change
|
|
}
|
|
|
|
void
|
|
replace_reg (rtx x, unsigned regno, rtx newreg, int offset)
|
|
{
|
|
RTX_CODE code = GET_CODE(x);
|
|
const char *fmt = GET_RTX_FORMAT(code);
|
|
for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|
{
|
|
if (fmt[i] == 'e')
|
|
{
|
|
rtx y = XEXP(x, i);
|
|
if (REG_P(y) && REGNO(y) == regno)
|
|
{
|
|
XEXP(x, i) = newreg;
|
|
if (offset && i + 1 < GET_RTX_LENGTH(code))
|
|
{
|
|
rtx c = XEXP(x, i + 1);
|
|
if (GET_CODE(c) == CONST_INT)
|
|
XEXP(x, i + 1) = gen_rtx_CONST_INT (GET_MODE(x), INTVAL(c) + offset);
|
|
}
|
|
}
|
|
else
|
|
replace_reg (y, regno, newreg, offset);
|
|
}
|
|
else if (fmt[i] == 'E')
|
|
for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
|
|
replace_reg (XVECEXP(x, i, j), regno, newreg, offset);
|
|
}
|
|
}
|
|
|
|
void
|
|
insn_info::a5_to_a7 (rtx a7, int add)
|
|
{
|
|
if (proepi == IN_EPILOGUE && get_src_mem_regno () == FRAME_POINTER_REGNUM)
|
|
{
|
|
rtx set = single_set (insn);
|
|
if (set)
|
|
{
|
|
SET_SRC(set) = gen_rtx_MEM (mode, gen_rtx_POST_INC(SImode, a7));
|
|
return;
|
|
}
|
|
}
|
|
replace_reg (PATTERN (insn), FRAME_POINTER_REGNUM, a7, add-4);
|
|
}
|
|
|
|
void
|
|
insn_info::set_insn (rtx_insn * newinsn)
|
|
{
|
|
insn = newinsn;
|
|
|
|
reset_flags ();
|
|
|
|
fledder (single_set (insn));
|
|
}
|
|
|
|
rtx
|
|
insn_info::make_absolute2base (unsigned regno, unsigned base, rtx with_symbol, bool apply)
|
|
{
|
|
rtx set = single_set (get_insn ());
|
|
rtx src = SET_SRC(set);
|
|
rtx dst = SET_DEST(set);
|
|
rtx reg = gen_raw_REG (SImode, regno);
|
|
bool vola = src->volatil;
|
|
|
|
if (is_dst_mem () && (has_dst_addr () || get_dst_symbol ()) && !has_dst_memreg () && get_dst_mem_mem() == 0 && get_dst_symbol () == with_symbol)
|
|
{
|
|
unsigned addr = get_dst_mem_addr ();
|
|
unsigned offset = addr - base;
|
|
if (offset <= 0x7ffe)
|
|
{
|
|
if (base == addr)
|
|
dst = gen_rtx_MEM (mode, reg);
|
|
else
|
|
dst = gen_rtx_MEM (mode, gen_rtx_PLUS(SImode, reg, gen_rtx_CONST_INT (SImode, offset)));
|
|
|
|
if (apply)
|
|
fledder_dst_mem (dst);
|
|
}
|
|
}
|
|
|
|
if (is_src_mem () && (has_src_addr () || get_src_symbol ()) && !has_src_memreg () && get_src_mem_mem() == 0 && get_src_symbol () == with_symbol)
|
|
{
|
|
unsigned addr = get_src_mem_addr ();
|
|
unsigned offset = addr - base;
|
|
if (offset <= 0x7ffe)
|
|
{
|
|
if (base == addr)
|
|
src = gen_rtx_MEM (mode, reg);
|
|
else
|
|
src = gen_rtx_MEM (mode, gen_rtx_PLUS(SImode, reg, gen_rtx_CONST_INT (SImode, offset)));
|
|
|
|
/* some operation to the same value as dst. eg. eor #5,symbol+8 -> eor #5,8(ax) */
|
|
if (src_op)
|
|
{
|
|
rtx srccopy = copy_rtx(SET_SRC(set));
|
|
// find the parent of the MEM
|
|
rtx outer = srccopy;
|
|
while (!MEM_P(XEXP(outer, 0)))
|
|
outer = XEXP(outer, 0);
|
|
|
|
XEXP(outer, 0) = src;
|
|
src = srccopy;
|
|
}
|
|
|
|
if (apply)
|
|
fledder_src_mem(src);
|
|
}
|
|
}
|
|
|
|
rtx pattern = gen_rtx_SET(dst, src);
|
|
src->volatil = vola;
|
|
|
|
return pattern;
|
|
}
|
|
|
|
void
|
|
insn_info::absolute2base (unsigned regno, unsigned base, rtx with_symbol)
|
|
{
|
|
rtx pattern = make_absolute2base (regno, base, with_symbol, true);
|
|
|
|
SET_INSN_DELETED(insn);
|
|
insn = emit_insn_after (pattern, insn);
|
|
|
|
mark_myuse (regno, 4);
|
|
|
|
insn2info->insert (std::make_pair (insn, this));
|
|
}
|
|
/*
|
|
* Reset collected data.
|
|
*/
|
|
static void
|
|
clear (void)
|
|
{
|
|
label2jump->clear ();
|
|
jump2label->clear ();
|
|
insn2info->clear ();
|
|
infos->clear ();
|
|
scan_starts->clear ();
|
|
}
|
|
|
|
/*
|
|
* return true if the register is DEAD.
|
|
* Do not check at jumps.
|
|
*/
|
|
static bool
|
|
is_reg_dead (unsigned regno, unsigned _pos)
|
|
{
|
|
// skip labels.
|
|
for (unsigned pos = _pos + 1; pos < infos->size (); ++pos)
|
|
{
|
|
insn_info & ii = (*infos)[pos];
|
|
// skip entries without info
|
|
if (ii.is_empty ())
|
|
continue;
|
|
|
|
// not dead if usage is reported in the next statement
|
|
return !ii.is_use (regno) && !ii.is_hard (regno);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool dump_cycles;
|
|
bool dump_reg_track;
|
|
void
|
|
append_reg_cache (FILE * f, rtx_insn * insn)
|
|
{
|
|
i2i_iterator i = insn2info->find (insn);
|
|
if (i == insn2info->end ())
|
|
return;
|
|
|
|
insn_info & jj = *i->second;
|
|
unsigned index = jj.get_index ();
|
|
if (index + 1 < infos->size ())
|
|
++index;
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
track_var * track = ii.get_track_var ();
|
|
if (track == 0)
|
|
return;
|
|
|
|
fprintf (f, "\n");
|
|
|
|
for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
|
|
{
|
|
rtx v = track->get (regno);
|
|
unsigned mask = track->getMask(regno);
|
|
if (!v && mask == 0xffffffff)
|
|
continue;
|
|
|
|
// if (GET_CODE(v) == CONST_INT && GET_MODE(v) == VOIDmode)
|
|
// continue;
|
|
|
|
fprintf (f, "%s=", reg_names[regno]);
|
|
|
|
if (v)
|
|
print_inline_rtx (f, v, 12);
|
|
else
|
|
fprintf(f, "---");
|
|
|
|
fprintf (f, "%08x\n", mask);
|
|
}
|
|
}
|
|
|
|
/* helper stuff to enhance the asm output. */
|
|
void
|
|
append_reg_usage (FILE * f, rtx_insn * insn)
|
|
{
|
|
i2i_iterator i = insn2info->find (insn);
|
|
if (i == insn2info->end ())
|
|
return;
|
|
|
|
insn_info & ii = *i->second;
|
|
|
|
if (f != stderr)
|
|
{
|
|
int cost = -1;
|
|
rtx set = single_set(ii.get_insn());
|
|
if (set)
|
|
cost = rtx_cost(set, GET_MODE(SET_DEST(set)), INSN, 0, true);
|
|
else if (ii.is_call())
|
|
cost = rtx_cost(PATTERN(ii.get_insn()), VOIDmode, INSN, 0, true);
|
|
if (be_very_verbose)
|
|
fprintf (f, "\n\t\t\t\t#%d\t%d %d\t", ii.get_index (), cost, set ? insn_rtx_cost(set, true): 0);
|
|
else
|
|
{
|
|
fprintf (f, "\t# %d %d", cost, set ? insn_rtx_cost(set, true): 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf (f, "%c ", ii.in_proepi () == IN_PROLOGUE ? 'p' : ii.in_proepi () >= IN_EPILOGUE ? 'e' : ' ');
|
|
|
|
for (int j = 0; j < 8; ++j)
|
|
if (ii.is_use (j) || ii.is_def (j))
|
|
{
|
|
fprintf (f, ii.is_hard (j) ? "!" : " ");
|
|
fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " ");
|
|
fprintf (f, "%d", ii.getX(j));
|
|
fprintf (f, "d%d", j);
|
|
}
|
|
else
|
|
fprintf (f, " ");
|
|
|
|
for (int j = 8; j < 16; ++j)
|
|
if (ii.is_use (j) || ii.is_def (j))
|
|
{
|
|
fprintf (f, ii.is_hard (j) ? "!" : " ");
|
|
fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " ");
|
|
fprintf (f, "a%d ", j - 8);
|
|
}
|
|
else
|
|
fprintf (f, " ");
|
|
|
|
if (ii.is_use (FIRST_PSEUDO_REGISTER) || ii.is_def (FIRST_PSEUDO_REGISTER))
|
|
{
|
|
fprintf (f, ii.is_def (FIRST_PSEUDO_REGISTER) ? ii.is_use (FIRST_PSEUDO_REGISTER) ? "*" : "+" : ii.is_myuse (FIRST_PSEUDO_REGISTER) ? "." : " ");
|
|
fprintf (f, "cc ");
|
|
}
|
|
else
|
|
fprintf (f, " ");
|
|
|
|
// append fp usage info if present
|
|
if ((ii.get_use () | ii.get_def ()) & ~0xffff)
|
|
{
|
|
for (int j = 16; j < 24; ++j)
|
|
if (ii.is_use (j) || ii.is_def (j))
|
|
{
|
|
fprintf (f, ii.is_hard (j) ? "!" : " ");
|
|
fprintf (f, ii.is_def (j) ? ii.is_use (j) ? "*" : "+" : ii.is_myuse (j) ? "." : " ");
|
|
fprintf (f, "f%d ", j - 16);
|
|
}
|
|
else
|
|
fprintf (f, " ");
|
|
}
|
|
|
|
if (f == stderr)
|
|
fprintf (f, "\n");
|
|
|
|
}
|
|
|
|
/*
|
|
* Helper function to dump the code.
|
|
* Sometimes used during debugging.
|
|
*/
|
|
static void
|
|
dump_insns (char const * name, bool all)
|
|
{
|
|
fprintf (stderr, "====================================: %s\n", name);
|
|
if (all)
|
|
{
|
|
for (rtx_insn * insn = get_insns (); insn && insn != (*infos)[0].get_insn (); insn = NEXT_INSN (insn))
|
|
debug_rtx (insn);
|
|
}
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
fprintf (stderr, "%d: ", i);
|
|
|
|
rtx_insn * insn = (*infos)[i].get_insn ();
|
|
if (i < infos->size ())
|
|
append_reg_usage (stderr, insn);
|
|
|
|
fprintf (stderr, "\t");
|
|
debug_rtx (insn);
|
|
|
|
if (all)
|
|
{
|
|
rtx_insn * p = i + 1 < infos->size () ? (*infos)[i + 1].get_insn () : 0;
|
|
for (rtx_insn * q = NEXT_INSN (insn); q && q != p; q = NEXT_INSN (q))
|
|
debug_rtx (q);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This is the important function to track register usage plus hard/live state.
|
|
*
|
|
* Start at bottom and work upwards. On all labels trigger all jumps referring to this label.
|
|
* A set destination into a register is a def. All other register references are an use.
|
|
* Hard registers cann't be renamed and are mandatory for regparms and asm_operands.
|
|
*
|
|
* todo defaults to scan_starts
|
|
*/
|
|
static void
|
|
update_insn_infos (std::set<unsigned> & todo)
|
|
{
|
|
/* add all return (jump outs) and start analysis there. */
|
|
if (todo.begin () == todo.end ())
|
|
todo.insert (infos->size () - 1);
|
|
|
|
bool locka4 = flag_pic >= 3;
|
|
|
|
while (!todo.empty ())
|
|
{
|
|
int start = *todo.begin ();
|
|
todo.erase (todo.begin ());
|
|
insn_info ii = (*infos)[start];
|
|
|
|
enum proepis proepi = ii.in_proepi ();
|
|
|
|
// mark sp reg as used.
|
|
if (proepi >= IN_EPILOGUE)
|
|
{
|
|
ii.mark_use (STACK_POINTER_REGNUM);
|
|
(*infos)[start].mark_use (STACK_POINTER_REGNUM);
|
|
}
|
|
|
|
for (int pos = start; pos >= 0; --pos)
|
|
{
|
|
insn_info & pp = (*infos)[pos];
|
|
rtx_insn * insn = pp.get_insn ();
|
|
|
|
if (!insn || NOTE_P (insn))
|
|
continue;
|
|
|
|
// do not run into previous epilogue
|
|
if (pp.in_proepi () >= IN_EPILOGUE && !proepi)
|
|
break;
|
|
|
|
proepi = pp.in_proepi ();
|
|
|
|
/* no new information -> break. */
|
|
if (pos != start && pp.is_visited () && !JUMP_P(insn) && pp.contains (ii))
|
|
break;
|
|
|
|
ii.clear_hard_def ();
|
|
ii.merge (pp);
|
|
|
|
if (LABEL_P(insn))
|
|
{
|
|
/* work on all jumps referring to that label. */
|
|
l2j_iterator i = label2jump->find (insn->u2.insn_uid);
|
|
|
|
/* no jump to here -> mark all registers as hard regs.
|
|
* This label is maybe used in an exception handler.
|
|
* Marking as hard also avoids stack frame removal.
|
|
*/
|
|
if (i == label2jump->end ())
|
|
(*infos)[pos + 1].make_hard ();
|
|
else
|
|
for (l2j_iterator k = i; i != label2jump->end () && i->first == k->first; ++i)
|
|
{
|
|
i2i_iterator j = insn2info->find (i->second);
|
|
if (j != insn2info->end ())
|
|
{
|
|
unsigned index = j->second->get_index ();
|
|
insn_info & jj = (*infos)[index];
|
|
if (!jj.is_visited () || !jj.contains (ii))
|
|
{
|
|
jj.updateWith (ii);
|
|
todo.insert (index);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pos == start)
|
|
pp.mark_visited ();
|
|
|
|
/* check previous insn for jump */
|
|
if (pos > 0 && (*infos)[pos - 1].is_jump ())
|
|
{
|
|
rtx_insn * prev = (*infos)[pos - 1].get_insn ();
|
|
rtx set = single_set (prev);
|
|
/* unconditional? -> break! */
|
|
if ( (set && SET_DEST (set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE)
|
|
|| (!set && PATTERN(prev) && GET_CODE(PATTERN(prev)) == PARALLEL && GET_CODE (SET_SRC (XVECEXP (PATTERN(prev), 0, 0))) != IF_THEN_ELSE))
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
pp.mark_visited ();
|
|
|
|
rtx pattern = PATTERN (insn);
|
|
insn_info use (insn);
|
|
use.scan ();
|
|
if (locka4 && (use.get_myuse () & (1 << PIC_REG)))
|
|
use.mark_hard (PIC_REG);
|
|
|
|
/* do not mark a node as visited, if it's in epilogue and not yet visited. */
|
|
if (CALL_P(insn) || JUMP_P(insn))
|
|
{
|
|
if (pos != start && ii.in_proepi ())
|
|
{
|
|
su_iterator k = scan_starts->find (pos);
|
|
if (k != scan_starts->end ())
|
|
{
|
|
pp.clear_visited ();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (GET_CODE (pattern) == USE || GET_CODE (pattern) == CLOBBER)
|
|
{
|
|
use.make_clobber (GET_MODE(XEXP(pattern, 0)));
|
|
}
|
|
else if (single_set (insn) == 0)
|
|
use.make_hard ();
|
|
else
|
|
/* if not cc0 defined check for mod. */
|
|
if (!use.is_def (FIRST_PSEUDO_REGISTER))
|
|
{
|
|
CC_STATUS_INIT;
|
|
NOTICE_UPDATE_CC(PATTERN (insn), insn);
|
|
if (cc_status.value1 || cc_status.value2)
|
|
use.mark_def (FIRST_PSEUDO_REGISTER);
|
|
}
|
|
|
|
// TODO: use 2 bits for data regs, to indicate mode size
|
|
// // also check mode size if < 4, it's also a use for data registers.
|
|
// if (pp.get_dst_reg () && pp.get_dst_regno () < 8 && GET_MODE_SIZE(pp.get_mode()) < 4)
|
|
// use.mark_use (pp.get_dst_regno ());
|
|
|
|
/* mark not renameable in prologue/epilogue. */
|
|
if (pp.in_proepi () != IN_CODE)
|
|
use.make_hard ();
|
|
|
|
ii.merge (use);
|
|
pp.update (ii);
|
|
ii.updateWith (use);
|
|
}
|
|
}
|
|
|
|
/* fill the mask of regs which are assigned a value. */
|
|
insn_info zz;
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
insn_info & ii = (*infos)[i];
|
|
if (ii.in_proepi ())
|
|
continue;
|
|
|
|
zz.or_def (ii);
|
|
}
|
|
|
|
usable_regs = zz.get_def ();
|
|
/* always allow a0/a1, d0/d1 unless it's an interrupt handler */
|
|
if (m68k_get_function_kind (current_function_decl) != m68k_fk_interrupt_handler)
|
|
usable_regs |= 0x303;
|
|
usable_regs &= 0x7fff;
|
|
|
|
/* do not use global registers. */
|
|
for (unsigned i = 0, j = 1; i < FIRST_PSEUDO_REGISTER; ++i)
|
|
{
|
|
if (global_regs[i])
|
|
usable_regs &= ~j;
|
|
j <<= 1;
|
|
}
|
|
}
|
|
|
|
enum AbortCodes
|
|
{
|
|
E_OK, E_NO_JUMP_LABEL, E_JUMP_TABLE_MISMATCH, E_JUMP_GOTO_LABEL, E_SP_MISMATCH
|
|
};
|
|
|
|
/*
|
|
* Create a filtered view of insns - keep only those to work with.
|
|
*/
|
|
static unsigned
|
|
update_insns ()
|
|
{
|
|
rtx_insn *insn, *next;
|
|
unsigned result = 0;
|
|
|
|
clear ();
|
|
|
|
enum proepis inproepilogue = IN_PROLOGUE;
|
|
/* create a vector with relevant insn. */
|
|
for (insn = get_insns (); insn; insn = next)
|
|
{
|
|
next = NEXT_INSN (insn);
|
|
|
|
if (NONJUMP_INSN_P (insn) || LABEL_P(insn) || JUMP_P(insn) || CALL_P(insn))
|
|
{
|
|
|
|
infos->push_back (insn_info (insn, inproepilogue));
|
|
insn_info & ii = (*infos)[infos->size () - 1];
|
|
|
|
if (JUMP_P(insn))
|
|
{
|
|
if (inproepilogue || ANY_RETURN_P(PATTERN (insn)))
|
|
{
|
|
if (ANY_RETURN_P(PATTERN (insn)))
|
|
ii.set_proepi (IN_EPILOGUE);
|
|
|
|
scan_starts->insert (infos->size () - 1);
|
|
inproepilogue = IN_CODE;
|
|
rtx set = single_set (insn);
|
|
if (ANY_RETURN_P(PATTERN (insn))
|
|
|| (set && SET_DEST(set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE)
|
|
|| (!set && PATTERN(insn) && GET_CODE(PATTERN(insn)) == PARALLEL && GET_CODE (SET_SRC (XVECEXP (PATTERN(insn), 0, 0))) != IF_THEN_ELSE))
|
|
continue;
|
|
}
|
|
|
|
ii.mark_jump ();
|
|
|
|
rtx table = 0;
|
|
rtx_insn * label = (rtx_insn *) JUMP_LABEL(insn);
|
|
if (label && NEXT_INSN (label) && JUMP_TABLE_DATA_P (NEXT_INSN (label)))
|
|
table = PATTERN(NEXT_INSN (label));
|
|
if (table)
|
|
{
|
|
// -> jump_table_data
|
|
if (GET_CODE(table) == ADDR_DIFF_VEC || GET_CODE(table) == ADDR_VEC)
|
|
{
|
|
int k = GET_CODE(table) == ADDR_DIFF_VEC;
|
|
for (int j = 0; j < XVECLEN(table, k); ++j)
|
|
{
|
|
rtx ref = XVECEXP(table, k, j);
|
|
if (!LABEL_REF_NONLOCAL_P(ref))
|
|
{
|
|
rtx label = XEXP(ref, 0);
|
|
label2jump->insert (std::make_pair (label->u2.insn_uid, insn));
|
|
ii.set_proepi (IN_EPILOGUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (be_very_verbose)
|
|
{
|
|
debug_rtx (insn);
|
|
debug_rtx (table);
|
|
}
|
|
result = E_JUMP_GOTO_LABEL;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rtx_insn * label = (rtx_insn *) JUMP_LABEL(insn);
|
|
if (!label)
|
|
{
|
|
if (be_very_verbose)
|
|
debug_rtx (insn);
|
|
result = E_NO_JUMP_LABEL;
|
|
continue;
|
|
}
|
|
label2jump->insert (std::make_pair (label->u2.insn_uid, insn));
|
|
}
|
|
}
|
|
else if (LABEL_P(insn))
|
|
{
|
|
ii.mark_label ();
|
|
ii.set_proepi (inproepilogue = IN_CODE);
|
|
if (infos->size () > 1)
|
|
scan_starts->insert (infos->size () - 1);
|
|
}
|
|
else if (CALL_P(insn))
|
|
{
|
|
if (insn->jump)
|
|
{
|
|
ii.set_proepi (IN_EPILOGUE);
|
|
ii.mark_jump ();
|
|
scan_starts->insert (infos->size () - 1);
|
|
}
|
|
ii.mark_call ();
|
|
if (inproepilogue)
|
|
{
|
|
scan_starts->insert (infos->size () - 1);
|
|
inproepilogue = IN_CODE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rtx set = single_set (insn);
|
|
if (set)
|
|
ii.fledder (set);
|
|
}
|
|
}
|
|
else if (NOTE_P(insn))
|
|
{
|
|
if (NOTE_KIND(insn) == NOTE_INSN_PROLOGUE_END)
|
|
inproepilogue = IN_CODE;
|
|
else if (NOTE_KIND(insn) == NOTE_INSN_EPILOGUE_BEG)
|
|
inproepilogue = IN_EPILOGUE;
|
|
}
|
|
}
|
|
scan_starts->insert (infos->size () - 1);
|
|
update_insn2index ();
|
|
update_insn_infos ();
|
|
|
|
return result;
|
|
}
|
|
|
|
/* convert the lowest set bit into a register number. */
|
|
static int
|
|
bit2regno (unsigned bit)
|
|
{
|
|
if (!bit)
|
|
return -1;
|
|
|
|
unsigned regno = 0;
|
|
while (!(bit & 1))
|
|
{
|
|
++regno;
|
|
bit >>= 1;
|
|
}
|
|
return regno;
|
|
}
|
|
|
|
/* check if that register is touched between from and to, excluding from and to .*/
|
|
static bool
|
|
is_reg_touched_between (unsigned regno, int from, int to)
|
|
{
|
|
for (int index = from + 1; index < to; ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.is_myuse (regno) || ii.is_def (regno))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* search backward and find the initial assignment for that regno.
|
|
*/
|
|
static unsigned
|
|
find_start (unsigned start, unsigned rename_regno)
|
|
{
|
|
/* search the start. */
|
|
while (start > 0)
|
|
{
|
|
unsigned startm1 = start - 1;
|
|
|
|
insn_info & jj = (*infos)[start];
|
|
|
|
/* stop at labels. If a label is a start pos, a search is maybe started again. */
|
|
if (jj.is_label ())
|
|
break;
|
|
|
|
/* do not run over RETURNS */
|
|
insn_info & bb = (*infos)[startm1];
|
|
if (jj.in_proepi () == IN_CODE && bb.in_proepi () >= IN_EPILOGUE)
|
|
break;
|
|
|
|
/* found the definition without use. */
|
|
if (jj.is_def (rename_regno) && !jj.is_use (rename_regno))
|
|
break;
|
|
|
|
start = startm1;
|
|
}
|
|
return start;
|
|
}
|
|
|
|
/*
|
|
static void
|
|
rus(char const * title)
|
|
{
|
|
fputs(title, stderr);
|
|
fputs("\n", stderr);
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
fprintf(stderr, "%d\t", index);
|
|
append_reg_usage(stderr, (*infos)[index].get_insn());
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* Always prefer lower register numbers within the class.
|
|
*/
|
|
static unsigned
|
|
opt_reg_rename (void)
|
|
{
|
|
update_label2jump ();
|
|
|
|
if (infos->size () < 2)
|
|
return 0;
|
|
|
|
unsigned changes = 0;
|
|
|
|
// rus("opt_reg_rename");
|
|
|
|
// dump_insns ("rename", 1);
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
/* do not rename if register is hard or used in same statement. */
|
|
const unsigned rename_regbit = ii.get_regbit ();
|
|
if (!rename_regbit)
|
|
continue;
|
|
|
|
const unsigned rename_regno = bit2regno (rename_regbit);
|
|
|
|
/* part of multi registers? do not touch! */
|
|
if (ii.get_multi_reg () & rename_regbit)
|
|
continue;
|
|
|
|
/* get the mask for free registers. */
|
|
unsigned mask = ii.get_free_mask () & (rename_regbit - 1);
|
|
|
|
/* no rename from ax to dy. */
|
|
if (rename_regno > 7)
|
|
mask &= 0xff00;
|
|
|
|
/* If it's a full register assignment, add the source register.
|
|
* Add this register anyway and track it's modification too.
|
|
* But only during first pass to avoid endless renames. */
|
|
unsigned reusemask = 0;
|
|
if (pass == 1 && ii.is_src_reg() && ii.get_mode() == SImode)
|
|
mask |= reusemask = 1 << ii.get_src_regno ();
|
|
|
|
mask &= usable_regs;
|
|
|
|
/* do not use a4 if compiling baserel */
|
|
if (flag_pic >= 3)
|
|
mask &= ~(1 << PIC_REG);
|
|
|
|
if (!mask)
|
|
continue;
|
|
|
|
/* first = pos to start, second indicates to treat def as use. */
|
|
std::set<unsigned> todo;
|
|
std::set<unsigned> found;
|
|
std::set<unsigned> endings;
|
|
if (index + 1 < infos->size ())
|
|
todo.insert (index + 1);
|
|
|
|
found.insert (index);
|
|
/* a register was defined, follow all branches. */
|
|
while (mask && todo.begin () != todo.end ())
|
|
{
|
|
unsigned runpos = *todo.begin ();
|
|
todo.erase (todo.begin ());
|
|
|
|
// printf ("runpos %d \n", runpos); fflush (stdout);
|
|
for (unsigned pos = runpos; mask && pos < infos->size (); ++pos)
|
|
{
|
|
/* already searched. */
|
|
if (found.find (pos) != found.end ())
|
|
break;
|
|
|
|
insn_info & jj = (*infos)[pos];
|
|
if (jj.is_call()) {
|
|
// do not rename registers used or defined in calls
|
|
if (jj.is_myuse(rename_regno) || jj.is_def(rename_regno)) {
|
|
mask = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rtx_insn * insn = jj.get_insn ();
|
|
if (LABEL_P(insn))
|
|
{
|
|
found.insert (pos);
|
|
|
|
/* for each jump to this label:
|
|
* check if the reg was used at that jump.
|
|
* if used, find def
|
|
*/
|
|
for (l2j_iterator i = label2jump->find (insn->u2.insn_uid), k = i;
|
|
i != label2jump->end () && i->first == k->first; ++i)
|
|
{
|
|
i2i_iterator j = insn2info->find (i->second);
|
|
if (j == insn2info->end ())
|
|
{
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
unsigned startat = j->second->get_index ();
|
|
if (found.find (startat) == found.end () && (*infos)[startat].is_use (rename_regno))
|
|
{
|
|
unsigned start = find_start (startat, rename_regno);
|
|
// printf ("label %d <- jump %d : start %d\n", pos, startat, start); fflush (stdout);
|
|
todo.insert (start);
|
|
}
|
|
}
|
|
|
|
/* if this label is at a start, check if it is reachable from the previous insn,
|
|
* and if, check for use then search start. */
|
|
if (pos > 0)
|
|
{
|
|
insn_info & bb = (*infos)[pos - 1];
|
|
// rtx set = single_set (bb.get_insn ());
|
|
if (ANY_RETURN_P(bb.get_insn ())
|
|
// || (set && SET_DEST(set) == pc_rtx && GET_CODE(SET_SRC(set)) != IF_THEN_ELSE)
|
|
// || (!set && PATTERN(bb.get_insn()) && GET_CODE(PATTERN(bb.get_insn())) == PARALLEL && (GET_CODE (SET_SRC (XVECEXP (PATTERN(bb.get_insn()), 0, 0)))) != IF_THEN_ELSE)
|
|
)
|
|
|
|
continue;
|
|
|
|
// printf ("label start check %d use %d\n", pos, bb.is_use (rename_regno) || bb.is_def(rename_regno)); fflush (stdout);
|
|
|
|
// search previous insn if it's a label or the register is used or defined.
|
|
if (bb.is_label() || bb.is_use (rename_regno) || bb.is_def (rename_regno))
|
|
{
|
|
unsigned start = find_start (pos - 1, rename_regno);
|
|
todo.insert (start);
|
|
// printf ("label %d : start %d \n", pos, start); fflush (stdout);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* marked as hard reg -> invalid rename */
|
|
if ((jj.get_use () & jj.get_hard () & rename_regbit)
|
|
/* or register is used and defined - with double register usage. */
|
|
|| (jj.get_multi_reg () & rename_regbit))
|
|
{
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
// if a register is reused in a tail rename
|
|
// check for modifications
|
|
if (reusemask)
|
|
{
|
|
if (jj.get_def() & reusemask)
|
|
{
|
|
// the reused is modified - tail rename not possible
|
|
mask &= ~reusemask;
|
|
reusemask = 0;
|
|
}
|
|
else if ((jj.get_def() & rename_regbit) && (jj.get_use() & reusemask))
|
|
{
|
|
// if the register is modified, the reused reg must be dead
|
|
mask &= ~reusemask;
|
|
reusemask = 0;
|
|
}
|
|
}
|
|
|
|
/* not used. and not a def */
|
|
if (pos == runpos && (jj.get_def () & rename_regbit))
|
|
{
|
|
/* continue since this pos was added by start search. */
|
|
}
|
|
else if (!(jj.get_use () & rename_regbit))
|
|
{
|
|
// search ends here and regrename is still valid.
|
|
endings.insert (pos);
|
|
break;
|
|
}
|
|
|
|
/* abort if some insn using this reg uses more than 1 reg. */
|
|
if ((jj.get_myuse () & rename_regbit) && GET_MODE_SIZE(jj.get_mode()) > 4)
|
|
{
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
/* update free regs. */
|
|
mask &= ~jj.get_use ();
|
|
/* add the reused reg again. */
|
|
mask |= reusemask;
|
|
mask &= ~jj.get_def ();
|
|
mask &= ~jj.get_multi_reg();
|
|
if (!mask)
|
|
break;
|
|
|
|
found.insert (pos);
|
|
|
|
/* follow jump and/or next insn. */
|
|
if (JUMP_P(insn))
|
|
{
|
|
for (j2l_iterator i = jump2label->find (pos), k = i; i != jump2label->end () && i->first == k->first;
|
|
++i)
|
|
{
|
|
unsigned label_index = i->second;
|
|
|
|
/* add the label to the search list. */
|
|
int labi = 0;
|
|
|
|
// search next non-label insn
|
|
insn_info * bb;
|
|
do
|
|
bb = &(*infos)[label_index + ++labi];
|
|
while (bb->is_label());
|
|
|
|
if (found.find (label_index) == found.end () && bb->is_use (rename_regno))
|
|
{
|
|
// printf ("jump %d -> label %d \n", pos, label_index); fflush (stdout);
|
|
todo.insert (label_index);
|
|
}
|
|
}
|
|
rtx set = single_set (insn);
|
|
if (!set)
|
|
{
|
|
// it's a parallel pattern - search the set pc = ...
|
|
rtx pat = PATTERN (insn);
|
|
for (int j = XVECLEN (pat, 0) - 1; j >= 0; j--)
|
|
{
|
|
rtx x = XVECEXP(pat, 0, j);
|
|
if (XEXP(x, 0) == pc_rtx)
|
|
{
|
|
set = x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rtx jmpsrc = set ? SET_SRC(set) : 0;
|
|
if (!jmpsrc || GET_CODE(jmpsrc) != IF_THEN_ELSE)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (mask && found.size() > 1)
|
|
{
|
|
int oldregno = bit2regno (rename_regbit);
|
|
int newregno = bit2regno (mask);
|
|
|
|
/* check the renamed insns. */
|
|
std::vector<unsigned> positions;
|
|
std::set<rtx *> regs;
|
|
// bool ok = true;
|
|
for (std::set<unsigned>::iterator i = found.begin (); i != found.end (); ++i)
|
|
{
|
|
insn_info & rr = (*infos)[*i];
|
|
|
|
// // prevent lea from or to data register.
|
|
// if (rr.get_src_op() == PLUS && rr.get_src_regno() != rr.get_dst_regno()) {
|
|
// if ((oldregno < 8 && newregno >= 8) || (oldregno >= 8 && newregno < 8)) {
|
|
// ok = false;
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
rtx_insn * insn = rr.get_insn ();
|
|
|
|
/* get rename modes. */
|
|
regs.clear();
|
|
find_regs_by_no(PATTERN (insn), oldregno, regs);
|
|
if (regs.size() > 0)
|
|
{
|
|
for (std::set<rtx *>::iterator m = regs.begin(); m != regs.end(); ++m)
|
|
{
|
|
rtx *x = *m;
|
|
rtx to = gen_raw_REG (GET_MODE(*x), newregno);
|
|
|
|
validate_unshare_change(insn, x, to, true);
|
|
}
|
|
positions.push_back (*i);
|
|
}
|
|
}
|
|
|
|
if (apply_change_group ())
|
|
{
|
|
log ("(r) opt_reg_rename %s -> %s (%d locs, start at %d)\n", reg_names[oldregno], reg_names[newregno],
|
|
positions.size (), index);
|
|
|
|
if (be_verbose)
|
|
{
|
|
for (std::vector<unsigned>::iterator i = positions.begin (); i != positions.end (); ++i)
|
|
printf ("%d ", *i);
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
}
|
|
#if 1
|
|
unsigned oldbit = 1 << oldregno;
|
|
unsigned newbit = 1 << newregno;
|
|
// does not work yet: TODO: distinguish between, def, myuse and use!
|
|
|
|
// rus("quick update");
|
|
|
|
// update the analyzed info.
|
|
for (std::vector<unsigned>::iterator i = positions.begin (); i != positions.end (); ++i)
|
|
{
|
|
rtx set = single_set((*infos)[*i].get_insn());
|
|
if (set)
|
|
(*infos)[*i].fledder(set);
|
|
}
|
|
|
|
|
|
// update the insn_infos
|
|
found.erase(index);
|
|
(*infos)[index].rename_def(oldbit, newbit);
|
|
|
|
for (std::set<unsigned>::iterator i = found.begin (); i != found.end (); ++i)
|
|
(*infos)[*i].rename(oldbit, newbit);
|
|
|
|
// rus("quick update");
|
|
break;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
// if (!ok)
|
|
// cancel_changes (0);
|
|
|
|
// try next register in mask - but skip those of same kind
|
|
if (newregno < 8)
|
|
mask &= 0x00ff00;
|
|
else
|
|
mask = 0;
|
|
}
|
|
}
|
|
return changes;
|
|
}
|
|
|
|
/*
|
|
* #1 propagate a->b->a moves out of a loop.
|
|
*
|
|
* consider a loop:
|
|
*
|
|
* .L1
|
|
* ...
|
|
* move d0, a0 ; (1)
|
|
* ...
|
|
* move xy, (a0)+
|
|
* ...
|
|
* move a0, d0 ; (2)
|
|
* ...
|
|
* jxx .L1
|
|
*
|
|
* Then the statements (1) and (2) can be moved out of the loop:
|
|
*
|
|
* move d0, a0 ; (3)
|
|
* .L1
|
|
* ...
|
|
* move *, (a0)+ ; a0 is modified somehow
|
|
* ...
|
|
* jxx .L1
|
|
* move a0, d0 ; (4)
|
|
*
|
|
* if all criteria are met:
|
|
*
|
|
* a) no other jump to .L1 -> (LABEL_NUSES(insn) == 1)
|
|
* b) no other use of d0 inside the loop
|
|
* c) no other use of a0 before (1)
|
|
* d) no other use of a1 after (2)
|
|
*
|
|
* Optional:
|
|
* - omit (4) if d0 is dead
|
|
*
|
|
* this will e.g. convert
|
|
.L6:
|
|
move.l d0,a1
|
|
move.b (a1)+,d1
|
|
move.l a1,d0
|
|
move.b d1,(a0)+
|
|
cmp.b #0, d1
|
|
jne .L6
|
|
* to
|
|
move.l d0,a1
|
|
.L6:
|
|
move.b (a1)+,d1
|
|
move.b d1,(a0)+
|
|
cmp.b #0, d1
|
|
jne .L6
|
|
|
|
*
|
|
* Also allow exit jumps, if the modification of the reg is const
|
|
* and insert a correction after the exit label.
|
|
* The label must only be reachable by the exit jump.
|
|
*/
|
|
static unsigned
|
|
opt_propagate_moves ()
|
|
{
|
|
unsigned change_count = 0;
|
|
rtx_insn * current_label = 0;
|
|
unsigned current_label_index = 0;
|
|
std::vector<unsigned> reg_reg;
|
|
std::vector<rtx_insn *> jump_out;
|
|
|
|
/* start at 1 since there must be an insn before the label. */
|
|
for (unsigned index = 1; index < infos->size (); ++index)
|
|
{
|
|
rtx_insn * insn = (*infos)[index].get_insn ();
|
|
|
|
if (LABEL_P(insn))
|
|
{
|
|
if (LABEL_NUSES(insn) == 1)
|
|
{
|
|
current_label = insn;
|
|
current_label_index = index;
|
|
reg_reg.clear ();
|
|
jump_out.clear ();
|
|
}
|
|
else
|
|
current_label = 0;
|
|
}
|
|
|
|
if (current_label == 0)
|
|
continue;
|
|
|
|
if (NONJUMP_INSN_P(insn))
|
|
{
|
|
// check for set reg, reg
|
|
rtx set = single_set (insn);
|
|
if (set)
|
|
{
|
|
rtx src = SET_SRC(set);
|
|
rtx dst = SET_DEST(set);
|
|
if (REG_P(src) && REG_P(dst))
|
|
reg_reg.push_back (index);
|
|
}
|
|
else
|
|
current_label = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (JUMP_P(insn))
|
|
{
|
|
rtx_insn * label = (rtx_insn *) JUMP_LABEL(insn);
|
|
if (label != current_label)
|
|
{
|
|
/* collect the labels for a later check if a fixup is possible. */
|
|
if (LABEL_NUSES(label) == 1 && BARRIER_P(PREV_INSN (label)))
|
|
jump_out.push_back (label);
|
|
else
|
|
current_label = 0;
|
|
continue;
|
|
}
|
|
|
|
if (reg_reg.size () > 1)
|
|
{
|
|
/* Search for reg/reg pairs. */
|
|
for (std::vector<unsigned>::iterator i = reg_reg.begin (); i != reg_reg.end () && i + 1 != reg_reg.end ();
|
|
)
|
|
{
|
|
bool inc = true;
|
|
for (std::vector<unsigned>::iterator j = i + 1; j != reg_reg.end ();)
|
|
{
|
|
rtx_insn * ii = (*infos)[*i].get_insn ();
|
|
rtx seti = single_set (ii);
|
|
rtx srci = SET_SRC(seti);
|
|
rtx dsti = SET_DEST(seti);
|
|
rtx_insn * jj = (*infos)[*j].get_insn ();
|
|
rtx setj = single_set (jj);
|
|
rtx srcj = SET_SRC(setj);
|
|
rtx dstj = SET_DEST(setj);
|
|
|
|
if (rtx_equal_p (srci, dstj) && rtx_equal_p (srcj, dsti))
|
|
{
|
|
/* Ensure correct usage. */
|
|
if (is_reg_touched_between (REGNO(srci), current_label_index, *i) // label ... move src,x
|
|
|| is_reg_touched_between (REGNO(srci), *i, *j) // move src,x ... move x,src
|
|
|| is_reg_touched_between (REGNO(srci), *j, index) // move x,src ... jcc
|
|
|| is_reg_touched_between (REGNO(dsti), current_label_index, *i) // label ... move src,x
|
|
|| is_reg_touched_between (REGNO(dsti), *j, index) // move x,src ... jcc
|
|
)
|
|
{
|
|
++j;
|
|
continue;
|
|
}
|
|
|
|
std::vector<int> fixups;
|
|
|
|
/* if there are jumps out of the loop,
|
|
* check if the modification occurs before the jump,
|
|
* and if, that it's a plus const.
|
|
*/
|
|
if (jump_out.size ())
|
|
{
|
|
std::vector<rtx_insn *>::iterator label_iter = jump_out.begin ();
|
|
int fixup = 0;
|
|
|
|
for (unsigned k = *i + 1; k != *j; ++k)
|
|
{
|
|
rtx_insn * check = (*infos)[k].get_insn ();
|
|
if (JUMP_P(check))
|
|
{
|
|
fixups.push_back (fixup);
|
|
if (++label_iter == jump_out.end ())
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
if (reg_overlap_mentioned_p (dsti, PATTERN (check)))
|
|
{
|
|
/* right now only support auto_incs. */
|
|
rtx set = single_set (check);
|
|
rtx src = SET_SRC(set);
|
|
rtx dst = SET_DEST(set);
|
|
|
|
if (reg_overlap_mentioned_p (dsti, dst))
|
|
{
|
|
if (REG_P(dst))
|
|
break;
|
|
if (!MEM_P(dst))
|
|
break;
|
|
|
|
rtx x = XEXP(dst, 0);
|
|
if (GET_CODE(x) == REG)
|
|
fixup += 0; // direct use
|
|
else if (GET_CODE(x) == PRE_INC ||
|
|
GET_CODE(x) == POST_INC)
|
|
fixup -= GET_MODE_SIZE(GET_MODE(dst));
|
|
else if (GET_CODE(dst) == PRE_DEC ||
|
|
GET_CODE(dst) == POST_DEC)
|
|
fixup += GET_MODE_SIZE(GET_MODE(dst));
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (reg_overlap_mentioned_p (dsti, src))
|
|
{
|
|
if (REG_P(src))
|
|
fixup += 0;
|
|
else
|
|
{
|
|
if (!MEM_P(src))
|
|
break;
|
|
|
|
rtx x = XEXP(src, 0);
|
|
if (GET_CODE(x) == REG)
|
|
fixup += 0; // direct use
|
|
else if (GET_CODE(x) == PRE_INC ||
|
|
GET_CODE(x) == POST_INC)
|
|
fixup -= GET_MODE_SIZE(GET_MODE(dst));
|
|
else if (GET_CODE(dst) == PRE_DEC ||
|
|
GET_CODE(dst) == POST_DEC)
|
|
fixup += GET_MODE_SIZE(GET_MODE(dst));
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* got a fixup for all jump_outs? */
|
|
if (fixups.size () == jump_out.size ())
|
|
{
|
|
rtx_insn * before = (*infos)[current_label_index - 1].get_insn ();
|
|
rtx_insn * after = (*infos)[index + 1].get_insn ();
|
|
// rtx bset = single_set (before);
|
|
|
|
log ("(p) propagate_moves condition met, moving regs %s, %s\n",
|
|
reg_names[REGNO(srci)],
|
|
reg_names[REGNO(dsti)]);
|
|
|
|
/* Move in front of loop and mark as dead. */
|
|
rtx_insn * newii = make_insn_raw (PATTERN (ii));
|
|
SET_INSN_DELETED(ii);
|
|
|
|
// /* Plus check if the reg was just loaded. */
|
|
// if (bset)
|
|
// {
|
|
// rtx bdst = SET_DEST(bset);
|
|
// if (REG_P(bdst) && REGNO(bdst) == REGNO(srci))
|
|
// {
|
|
// SET_SRC(PATTERN(newii)) = SET_SRC(bset);
|
|
//// SET_INSN_DELETED(ii);
|
|
// }
|
|
// }
|
|
// else
|
|
add_reg_note (newii, REG_DEAD, srci);
|
|
|
|
add_insn_after (newii, before, 0);
|
|
|
|
/* Move behind loop - into next BB. */
|
|
rtx_insn * newjj = make_insn_raw (PATTERN (jj));
|
|
add_insn_before (newjj, after, 0);
|
|
SET_INSN_DELETED(jj);
|
|
|
|
reg_reg.erase (j);
|
|
reg_reg.erase (i);
|
|
j = reg_reg.end ();
|
|
inc = false;
|
|
|
|
/* add fixes if there were jumps out of the loop. */
|
|
if (jump_out.size ())
|
|
{
|
|
log ("(p) propagate_moves fixing %d jump outs\n", jump_out.size ());
|
|
|
|
for (unsigned k = 0; k < jump_out.size (); ++k)
|
|
{
|
|
rtx_def * lbl = jump_out[k];
|
|
// search next no debug insn
|
|
rtx_def * before = next_nonnote_nondebug_insn (lbl);
|
|
//debug(before);
|
|
if ((REGNO(dsti) < 8 || REGNO(dstj) < 8) && REGNO(dsti) != REGNO(dstj))
|
|
{
|
|
rtx move = gen_rtx_SET(dstj, dsti);
|
|
emit_insn_before (move, before);
|
|
if (fixups[k])
|
|
{
|
|
rtx neu = gen_rtx_SET(
|
|
dstj, gen_rtx_PLUS (Pmode, dstj, gen_rtx_CONST_INT (Pmode, fixups[k])));
|
|
emit_insn_before (neu, before);
|
|
}
|
|
}
|
|
else if (fixups[k])
|
|
{
|
|
rtx neu = gen_rtx_SET(
|
|
dstj, gen_rtx_PLUS (Pmode, dsti, gen_rtx_CONST_INT (Pmode, fixups[k])));
|
|
emit_insn_before (neu, before);
|
|
}
|
|
}
|
|
}
|
|
++change_count;
|
|
}
|
|
}
|
|
if (inc)
|
|
++j;
|
|
}
|
|
if (inc)
|
|
++i;
|
|
}
|
|
}
|
|
current_label = 0;
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
/**
|
|
* Search for
|
|
*
|
|
* mov x,reg
|
|
* mov reg,x
|
|
* cmp #0, reg
|
|
* jxx
|
|
*
|
|
* patterns.
|
|
*
|
|
* Use a simple state machine to find the patterns.
|
|
*/
|
|
static unsigned
|
|
opt_strcpy ()
|
|
{
|
|
unsigned change_count = 0;
|
|
#if HAVE_cc0
|
|
rtx_insn * x2reg = 0;
|
|
rtx_insn * reg2x = 0;
|
|
rtx theReg = 0;
|
|
unsigned int regno = FIRST_PSEUDO_REGISTER;
|
|
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
rtx_insn * insn = ii.get_insn ();
|
|
|
|
if (!NONJUMP_INSN_P(insn))
|
|
{
|
|
x2reg = 0;
|
|
continue;
|
|
}
|
|
|
|
rtx set = single_set (insn);
|
|
if (!set)
|
|
{
|
|
x2reg = 0;
|
|
continue;
|
|
}
|
|
|
|
if (x2reg && reg2x)
|
|
{
|
|
rtx src = SET_SRC(set);
|
|
if (GET_CODE(src) == COMPARE)
|
|
{
|
|
rtx dst = XEXP(src, 0);
|
|
src = XEXP(src, 1);
|
|
|
|
// if (CONST_INT_P(src) && INTVAL(src) == 0 && find_reg_note (insn, REG_DEAD, dst))
|
|
if (REG_P(dst) && REGNO(dst) == REGNO(theReg) && CONST_INT_P(src) && INTVAL(src) == 0 && is_reg_dead (REGNO(dst), index))
|
|
{
|
|
/* now check via NOTICE_UPDATE_CC*/
|
|
NOTICE_UPDATE_CC(PATTERN (reg2x), reg2x);
|
|
if (cc_status.flags == 0 && rtx_equal_p (dst, cc_status.value2))
|
|
{
|
|
rtx pattern = gen_rtx_SET(SET_DEST(single_set (reg2x)), SET_SRC(single_set (x2reg)));
|
|
rtx_insn * newinsn = make_insn_raw (pattern);
|
|
|
|
if (!insn_invalid_p (newinsn, 0))
|
|
{
|
|
log ("(s) opt_strcpy condition met, removing compare and joining insns - omit reg %s\n",
|
|
reg_names[REGNO(dst)]);
|
|
|
|
SET_INSN_DELETED(x2reg);
|
|
SET_INSN_DELETED(reg2x);
|
|
SET_INSN_DELETED(insn);
|
|
|
|
insn = emit_insn_after (pattern, reg2x);
|
|
insn_invalid_p (insn, 0);
|
|
|
|
++change_count;
|
|
}
|
|
}
|
|
}
|
|
x2reg = 0;
|
|
continue;
|
|
}
|
|
reg2x = 0;
|
|
}
|
|
|
|
/* check for reg2x first, maybe fallback to x2reg. */
|
|
if (x2reg && reg2x == 0)
|
|
{
|
|
if (REG_P(SET_SRC(set)) && REGNO(SET_SRC(set)) == regno)
|
|
{
|
|
reg2x = insn;
|
|
theReg = SET_SRC(set);
|
|
continue;
|
|
}
|
|
x2reg = 0;
|
|
}
|
|
|
|
/* check for a match for x2reg. */
|
|
if (x2reg == 0)
|
|
{
|
|
if (ii.get_dst_reg() && ii.get_src_op() != ASM_OPERANDS)
|
|
{
|
|
x2reg = insn;
|
|
reg2x = 0;
|
|
regno = ii.get_dst_regno();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return change_count;
|
|
}
|
|
|
|
/*
|
|
* convert
|
|
*
|
|
* set reg1, plus (reg2, const)
|
|
* set mem(reg2), y
|
|
*
|
|
* ->
|
|
* set reg1, reg2
|
|
* set mem(reg1+), y
|
|
*
|
|
* if size of postinc == const
|
|
*
|
|
(insn 33 32 35 4 (set (reg/v/f:SI 8 a0 [orig:47 s ] [47])
|
|
(plus:SI (reg/v/f:SI 9 a1 [orig:46 s ] [46])
|
|
(const_int 1 [0x1]))) sn.c:5 141 {*addsi3_internal}
|
|
(nil))
|
|
(insn 36 35 37 4 (set (mem:QI (reg/v/f:SI 9 a1 [orig:46 s ] [46]) [0 MEM[base: s_17, offset: 4294967295B]+0 S1 A8])
|
|
(mem:QI (post_inc:SI (reg/v/f:SI 10 a2 [orig:53 s2 ] [53])) [0 MEM[base: s2_19, offset: 4294967295B]+0 S1 A8])) sn.c:5 46 {*m68k.md:1083}
|
|
(expr_list:REG_INC (reg/v/f:SI 10 a2 [orig:53 s2 ] [53])
|
|
(nil)))
|
|
*/
|
|
static unsigned
|
|
opt_commute_add_move (void)
|
|
{
|
|
unsigned change_count = 0;
|
|
|
|
for (unsigned index = 0; index + 1 < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.get_dst_regno () < 8 || ii.get_dst_regno () > 15 || ii.get_src_op () != PLUS
|
|
|| ii.get_src_regno () == ii.get_dst_regno () || !ii.get_src_intval ())
|
|
continue;
|
|
|
|
insn_info & jj = (*infos)[index + 1];
|
|
|
|
if (!jj.get_dst_mem_reg () || jj.get_dst_mem_regno () != ii.get_src_regno () || jj.get_dst_mem_addr() || jj.get_dst_autoinc()
|
|
|| jj.get_src_regno () == ii.get_dst_regno () || GET_MODE_SIZE(jj.get_mode()) != ii.get_src_intval () || jj.get_dst_mem_mem() != 0)
|
|
continue;
|
|
|
|
rtx_insn * insn = ii.get_insn ();
|
|
|
|
rtx_insn * next = jj.get_insn ();
|
|
rtx set2 = single_set (next);
|
|
rtx dst = SET_DEST(set2);
|
|
if (!MEM_P(dst))
|
|
continue;
|
|
|
|
rtx pinc = gen_rtx_POST_INC(GET_MODE(dst), ii.get_dst_reg ());
|
|
rtx newmem = replace_equiv_address_nv (dst, pinc);
|
|
|
|
rtx_insn * newinsn = make_insn_raw (gen_rtx_SET(ii.get_dst_reg (), ii.get_src_reg ()));
|
|
|
|
if (!insn_invalid_p (newinsn, 1) && validate_change (next, &SET_DEST(set2), newmem, 1) && apply_change_group ())
|
|
{
|
|
log ("(a) commute_add_move found\n");
|
|
|
|
SET_INSN_DELETED(insn);
|
|
|
|
insn = emit_insn_before (PATTERN(newinsn), next);
|
|
|
|
add_reg_note (next, REG_INC, ii.get_dst_reg ());
|
|
|
|
++change_count;
|
|
}
|
|
else
|
|
cancel_changes (0);
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
/*
|
|
* Replace
|
|
*
|
|
* move x,dx
|
|
* cmp dx,dy
|
|
*
|
|
* if dx and dy are both dead after compare.
|
|
*
|
|
* with
|
|
*
|
|
* sub #n,dx
|
|
*
|
|
d0 d1 d2 a0 a1 a7 (insn 99 59 41 7 (set (reg:SI 2 d2)
|
|
(const_int 1 [0x1])) sn.c:8 38 {*movsi_m68k}
|
|
(nil))
|
|
d0 d1 d2 a0 a1 a7 (insn 41 99 42 7 (set (cc0)
|
|
(compare (reg/v:SI 1 d1 [orig:54 n ] [54])
|
|
(reg:SI 2 d2))) sn.c:8 16 {*m68k.md:499}
|
|
(expr_list:REG_DEAD (reg:SI 2 d2)
|
|
(expr_list:REG_DEAD (reg/v:SI 1 d1 [orig:54 n ] [54])
|
|
(nil))))
|
|
*
|
|
*/
|
|
static unsigned
|
|
opt_const_cmp_to_sub (void)
|
|
{
|
|
unsigned change_count = 0;
|
|
#if HAVE_cc0
|
|
if (infos->size () < 2)
|
|
return change_count;
|
|
|
|
unsigned lastsub = 0;
|
|
for (unsigned index = infos->size () - 2; index > 0; --index)
|
|
{
|
|
insn_info & i1 = (*infos)[index];
|
|
/* we wan't a compare or tst insn, */
|
|
if (!i1.is_compare ())
|
|
continue;
|
|
|
|
if (GET_MODE_SIZE(i1.get_mode()) > 4 || !i1.is_dst_reg () || REGNO(i1.get_dst_reg()) > 7)
|
|
continue;
|
|
|
|
/* src must be a reg dead register with a constant - or a #0 */
|
|
if (!i1.get_src_reg () && (!i1.is_src_const () || i1.get_src_op () == PLUS))
|
|
continue;
|
|
|
|
/* allow an alive reg, if life ends at previous handled sub. */
|
|
int lastsubval = 0;
|
|
if (lastsub == index + 3)
|
|
{
|
|
insn_info & pp = (*infos)[lastsub];
|
|
if (pp.get_dst_regno () != i1.get_dst_regno ())
|
|
continue;
|
|
lastsubval = pp.get_src_intval ();
|
|
|
|
// but still check for usage after this jump
|
|
j2l_iterator l = jump2label->find (index + 2);
|
|
if (l == jump2label->end ())
|
|
continue;
|
|
|
|
insn_info & label = (*infos)[l->second + 1];
|
|
if (label.is_use (i1.get_dst_regno ()))
|
|
continue;
|
|
}
|
|
else if (!is_reg_dead (i1.get_dst_regno (), index))
|
|
continue;
|
|
|
|
insn_info & i0 = (*infos)[index - 1];
|
|
int intval = 0;
|
|
/* compare with register - check previous insn for load with constant. */
|
|
if (i1.is_src_reg ())
|
|
{
|
|
if (!is_reg_dead (i1.get_src_regno (), index))
|
|
continue;
|
|
|
|
if (GET_MODE_SIZE(i0.get_mode()) > 4)
|
|
continue;
|
|
|
|
if (!i0.is_dst_reg () || !i0.is_src_const () || i0.get_src_op ())
|
|
continue;
|
|
|
|
if (i0.get_dst_regno () != i1.get_src_regno ())
|
|
continue;
|
|
|
|
intval = -i0.get_src_intval ();
|
|
if (intval < -8 || intval > 7)
|
|
continue;
|
|
|
|
/* is the next sub value in range? */
|
|
if (lastsub == index + 3 && (lastsubval - intval < -8 || lastsubval - intval > 7))
|
|
continue;
|
|
}
|
|
|
|
/* next insn must be the jump. */
|
|
insn_info & i2 = (*infos)[index + 1];
|
|
if (!i2.is_jump ())
|
|
continue;
|
|
|
|
rtx jmppattern = single_set (i2.get_insn ());
|
|
if (!jmppattern)
|
|
continue;
|
|
|
|
rtx jmpsrc = XEXP(jmppattern, 1);
|
|
if (GET_CODE(jmpsrc) != IF_THEN_ELSE)
|
|
continue;
|
|
|
|
rtx condition = XEXP(jmpsrc, 0);
|
|
RTX_CODE code = GET_CODE(condition);
|
|
if (code != EQ && code != NE)
|
|
continue;
|
|
|
|
if (intval)
|
|
{
|
|
rtx copyreg = copy_reg (i1.get_dst_reg (), -1);
|
|
/* create the sub statement. */
|
|
rtx sub = gen_rtx_PLUS(i1.get_mode (), copyreg, gen_rtx_CONST_INT (i1.get_mode (), intval));
|
|
|
|
rtx_insn * subinsn = make_insn_raw (gen_rtx_SET(copyreg, sub));
|
|
|
|
if (insn_invalid_p (subinsn, 0))
|
|
continue;
|
|
|
|
/* delete move #x,dy. */
|
|
SET_INSN_DELETED(i0.get_insn ())
|
|
/* delete cmp dx,dy */
|
|
SET_INSN_DELETED(i1.get_insn ());
|
|
/* add a cmp #0 - to be removed in final() */
|
|
|
|
/* convert cmp/tst into sub */
|
|
subinsn = emit_insn_before (PATTERN (subinsn), i1.get_insn ());
|
|
i1.set_insn (subinsn);
|
|
|
|
rtx neu = gen_rtx_SET(cc0_rtx,
|
|
gen_rtx_COMPARE (i1.get_mode (), copyreg, gen_rtx_CONST_INT (i1.get_mode (), 0)));
|
|
|
|
emit_insn_before (neu, i2.get_insn ());
|
|
|
|
log ("(c) const_cmp_to_sub replaced %s == %s (%d) with sub %d,%s\n", reg_names[i1.get_dst_regno ()],
|
|
reg_names[i0.get_dst_regno ()],
|
|
-intval, -intval, reg_names[i1.get_dst_regno ()]);
|
|
|
|
if (index + 3 == lastsub)
|
|
{
|
|
/* patch previous sub - or even a compare. */
|
|
insn_info & pp = (*infos)[lastsub];
|
|
|
|
int diff = lastsubval - intval;
|
|
rtx c = gen_rtx_CONST_INT (i1.get_mode (), diff);
|
|
|
|
if (pp.is_compare ())
|
|
{
|
|
/* still a compare with 0 -> insert the sub. */
|
|
rtx copyreg = copy_reg (i1.get_dst_reg (), -1);
|
|
/* create the sub statement. */
|
|
rtx sub = gen_rtx_PLUS(i1.get_mode (), copyreg, c);
|
|
rtx set = gen_rtx_SET(copyreg, sub);
|
|
emit_insn_before (set, pp.get_insn ());
|
|
}
|
|
else
|
|
{
|
|
/* modify the sub. */
|
|
XEXP(SET_SRC(PATTERN(pp.get_insn())), 1) = c;
|
|
}
|
|
}
|
|
|
|
lastsub = index;
|
|
++change_count;
|
|
}
|
|
}
|
|
#endif
|
|
return change_count;
|
|
}
|
|
|
|
/*
|
|
* rare and only little gain - but :-)
|
|
lea (-1,a0),a1
|
|
add.l d1,a1
|
|
subq.l #1,d1
|
|
->
|
|
move.l a0,a1
|
|
subq.l #1,d1
|
|
add.l d1,a1
|
|
*/
|
|
static unsigned
|
|
opt_merge_add (void)
|
|
{
|
|
unsigned change_count = 0;
|
|
for (unsigned index = 0; index + 2 < infos->size (); ++index)
|
|
{
|
|
insn_info & ii0 = (*infos)[index];
|
|
insn_info & ii1 = (*infos)[index + 1];
|
|
insn_info & ii2 = (*infos)[index + 2];
|
|
|
|
if (!ii2.is_dst_reg () || ii2.is_src_mem() || ii2.get_src_op () != PLUS)
|
|
{
|
|
index += 2;
|
|
continue;
|
|
}
|
|
|
|
if (!ii1.is_dst_reg () || ii1.is_src_mem() || ii1.get_src_op () != PLUS || ii1.get_dst_reg() != ii2.get_src_reg())
|
|
{
|
|
++index;
|
|
continue;
|
|
}
|
|
|
|
if (!ii0.is_dst_reg () || ii0.is_src_mem() || ii0.get_src_op () != PLUS)
|
|
continue;
|
|
|
|
if (!ii0.is_src_const () || !ii2.is_src_const () || ii0.get_src_intval () != ii2.get_src_intval ())
|
|
continue;
|
|
|
|
if (ii0.get_dst_regno () != ii1.get_dst_regno () || ii1.get_src_regno () != ii2.get_dst_regno ())
|
|
continue;
|
|
|
|
rtx_insn * insn1 = ii1.get_insn ();
|
|
|
|
CC_STATUS_INIT;
|
|
NOTICE_UPDATE_CC(PATTERN (insn1), insn1);
|
|
if (cc_status.value1 || cc_status.value2)
|
|
continue;
|
|
|
|
log ("(m) %d: merge_add applied\n", index);
|
|
// debug_rtx(ii0.get_insn());
|
|
// debug_rtx(ii1.get_insn());
|
|
// debug_rtx(ii2.get_insn());
|
|
|
|
rtx_insn * insn0 = ii0.get_insn ();
|
|
rtx set = PATTERN (insn0);
|
|
|
|
// convert lea (-1,a0),a1 into move.l a0,a1
|
|
rtx_insn * newins0 = make_insn_raw (gen_rtx_SET(XEXP(set, 0), XEXP(XEXP(set, 1), 0)));
|
|
add_insn_after (newins0, insn0, 0);
|
|
SET_INSN_DELETED(insn0);
|
|
// update infos accordingly
|
|
ii0.plus_to_move (newins0);
|
|
|
|
rtx_insn * insn2 = ii2.get_insn ();
|
|
rtx_insn * newins1 = make_insn_raw (PATTERN (insn1));
|
|
add_insn_after (newins1, insn2, 0);
|
|
SET_INSN_DELETED(insn1);
|
|
ii1.swap_adds (newins1, ii2);
|
|
|
|
++change_count;
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
/* Update the insn_infos to 'know' the sp offset. */
|
|
static unsigned
|
|
track_sp (int & a5_touched)
|
|
{
|
|
// reset visited flags - also check if sp is used as REG src.
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
ii.clear_visited ();
|
|
ii.set_sp_offset (0);
|
|
|
|
if (ii.in_proepi() == IN_CODE)
|
|
a5_touched |= ii.get_myuse() & 0x2000;
|
|
|
|
// if sp is used as source, we cannot shrink the stack yet
|
|
// too complicated - well, could be done^^
|
|
if (ii.get_src_regno () == STACK_POINTER_REGNUM)
|
|
return -1;
|
|
}
|
|
|
|
// add entry point
|
|
std::set<unsigned> todo;
|
|
todo.insert (0);
|
|
|
|
while (todo.begin () != todo.end ())
|
|
{
|
|
unsigned startpos = *todo.begin ();
|
|
todo.erase (todo.begin ());
|
|
|
|
int sp_offset = (*infos)[startpos].get_sp_offset ();
|
|
|
|
for (unsigned index = startpos; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.in_proepi () != IN_CODE)
|
|
{
|
|
ii.set_sp_offset (sp_offset);
|
|
continue;
|
|
}
|
|
|
|
// already visited? sp_offset must match
|
|
if (ii.is_visited ())
|
|
{
|
|
if (ii.get_sp_offset () != sp_offset)
|
|
return E_SP_MISMATCH;
|
|
|
|
// check all jumps to this label
|
|
if (ii.is_label())
|
|
{
|
|
for (l2j_iterator j = label2jump->find (ii.get_insn ()->u2.insn_uid), k = j;
|
|
j != label2jump->end () && j->first == k->first; ++j)
|
|
{
|
|
insn_info & ll = *insn2info->find(j->second)->second;
|
|
if (ll.is_visited () && ll.get_sp_offset () != sp_offset)
|
|
return E_SP_MISMATCH;
|
|
}
|
|
}
|
|
|
|
// check the label(s) for this jump
|
|
if (ii.is_jump ())
|
|
{
|
|
for (j2l_iterator i = jump2label->find (index), k = i; i != jump2label->end () && i->first == k->first; ++i)
|
|
{
|
|
insn_info & ll = (*infos)[i->second];
|
|
if (ll.is_visited () && ll.get_sp_offset () != sp_offset)
|
|
return E_SP_MISMATCH;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// mark current insn_info and set sp_offset
|
|
ii.mark_visited ();
|
|
ii.set_sp_offset (sp_offset);
|
|
|
|
// add all referencing jumps
|
|
if (ii.is_label())
|
|
{
|
|
for (l2j_iterator j = label2jump->find (ii.get_insn ()->u2.insn_uid), k = j;
|
|
j != label2jump->end () && j->first == k->first; ++j)
|
|
{
|
|
insn_info & ll = *insn2info->find(j->second)->second;
|
|
if (!ll.is_visited())
|
|
{
|
|
ll.set_sp_offset(sp_offset);
|
|
todo.insert (ll.get_index());
|
|
}
|
|
else if (ll.get_sp_offset() != sp_offset)
|
|
return E_SP_MISMATCH;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
|
|
// add all referred labels
|
|
if (ii.is_jump ())
|
|
{
|
|
for (j2l_iterator i = jump2label->find (index), k = i; i != jump2label->end () && i->first == k->first; ++i)
|
|
{
|
|
insn_info & ll = (*infos)[i->second];
|
|
if (!ll.is_visited())
|
|
{
|
|
ll.set_sp_offset(sp_offset);
|
|
todo.insert (i->second);
|
|
}
|
|
else if (ll.get_sp_offset() != sp_offset)
|
|
return E_SP_MISMATCH;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// is sp modified directly
|
|
if (ii.is_dst_reg () && ii.get_dst_regno () == STACK_POINTER_REGNUM)
|
|
{
|
|
// handle sp = sp + const_int
|
|
if (!ii.get_src_reg () || ii.get_src_regno () != STACK_POINTER_REGNUM || ii.get_src_op () != PLUS)
|
|
return E_SP_MISMATCH;
|
|
|
|
sp_offset = sp_offset + ii.get_src_intval ();
|
|
continue;
|
|
}
|
|
|
|
// handle dst mem autoinc
|
|
if (ii.is_dst_mem () && ii.get_dst_mem_regno () == STACK_POINTER_REGNUM && ii.get_dst_autoinc ())
|
|
sp_offset += GET_MODE_SIZE(ii.get_mode()) * ii.get_dst_autoinc ();
|
|
|
|
// handle src mem autoinc
|
|
if (ii.is_src_mem () && ii.get_src_mem_regno () == STACK_POINTER_REGNUM && ii.get_src_autoinc ())
|
|
sp_offset += GET_MODE_SIZE(ii.get_mode()) * ii.get_src_autoinc ();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* recursive function to patch stack pointer offsets. */
|
|
void
|
|
patch_sp (rtx x, int adjust, int spoffset)
|
|
{
|
|
int code = GET_CODE(x);
|
|
if (code == PLUS)
|
|
{
|
|
rtx a = XEXP(x, 0);
|
|
rtx b = XEXP(x, 1);
|
|
if (REG_P(a) && REGNO(a) == STACK_POINTER_REGNUM && GET_CODE(b) == CONST_INT)
|
|
{
|
|
if (INTVAL(b) > -spoffset)
|
|
XEXP(x, 1) = gen_rtx_CONST_INT (GET_MODE(b), INTVAL(b) - adjust);
|
|
return;
|
|
}
|
|
}
|
|
const char *fmt = GET_RTX_FORMAT(code);
|
|
for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
|
{
|
|
if (fmt[i] == 'e')
|
|
patch_sp (XEXP(x, i), adjust, spoffset);
|
|
else if (fmt[i] == 'E')
|
|
for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
|
|
patch_sp (XVECEXP(x, i, j), adjust, spoffset);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 1. scan for all used registers.
|
|
* 2. scan the stack from for omittable push/pop
|
|
* 3. adjust stack frame + insns referring to stack pointer
|
|
* typical code:
|
|
subq.l #4,sp
|
|
movem.l #16190,-(sp)
|
|
move.l 52(sp),d2
|
|
move.l 56(sp),d3
|
|
|
|
* or
|
|
link a5,#4
|
|
movem.l #16190,-(sp)
|
|
move.l 8(a5),d2
|
|
move.l 12(a5),d3
|
|
*
|
|
* => with a5 check only prolog/epilog
|
|
* => without a5 adjust insns referring sp if offset > startoffset + current sp diff
|
|
*
|
|
* startvalue count(pushes)*4
|
|
* newstartvalue = startvalue - omitted pushes
|
|
*/
|
|
static unsigned
|
|
opt_shrink_stack_frame (void)
|
|
{
|
|
/* nothing to do. */
|
|
if (!infos->size ())
|
|
return 0;
|
|
|
|
/* needed to track sp correctly. */
|
|
update_label2jump ();
|
|
int a5_touched = 0;
|
|
if (track_sp (a5_touched))
|
|
return 0; // do nothing on stack errors
|
|
|
|
std::vector<int> a5pos;
|
|
|
|
unsigned pos = 0;
|
|
rtx_insn * insn = (*infos)[pos].get_insn ();
|
|
if (JUMP_P(insn)) /* return -> empty function*/
|
|
return 0;
|
|
|
|
bool usea5 = false;
|
|
int paramstart = 4;
|
|
int a5offset = 0;
|
|
|
|
/*
|
|
* Move prologue to temp.
|
|
* Only register push and parallel insn unless its a link a5 are moved.
|
|
*/
|
|
unsigned num_push = 0;
|
|
for (; pos < infos->size ();)
|
|
{
|
|
insn_info & ii = (*infos)[pos];
|
|
insn = ii.get_insn ();
|
|
|
|
if (ii.in_proepi () != IN_PROLOGUE)
|
|
break;
|
|
|
|
rtx pattern = PATTERN (insn);
|
|
if (GET_CODE(pattern) == PARALLEL)
|
|
{
|
|
rtx set = XVECEXP(pattern, 0, 0);
|
|
rtx dst = SET_DEST(set);
|
|
ii.mark_stack ();
|
|
/* ignore link a5 */
|
|
if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM)
|
|
{
|
|
a5pos.push_back (pos);
|
|
usea5 = true;
|
|
set = XVECEXP(pattern, 0, 2);
|
|
a5offset = INTVAL(XEXP(SET_SRC(set), 1));
|
|
}
|
|
else
|
|
++num_push;
|
|
++pos;
|
|
continue;
|
|
}
|
|
if (GET_CODE(pattern) != SET)
|
|
{
|
|
/* (set (mem:BLK (scratch) [0 A8]) (unspec:BLK [ ...)) */
|
|
if (MEM_P(SET_DEST(pattern)) && GET_CODE(SET_SRC(pattern)) == UNSPEC)
|
|
a5pos.push_back (pos);
|
|
++pos;
|
|
continue;
|
|
}
|
|
|
|
/* move only the push statements. */
|
|
rtx src = SET_SRC(pattern);
|
|
rtx dest = SET_DEST(pattern);
|
|
if (REG_P(src))
|
|
{
|
|
if (MEM_P(dest))
|
|
{
|
|
rtx predec = XEXP(dest, 0);
|
|
if (GET_CODE(predec) == PRE_DEC)
|
|
{
|
|
rtx reg = XEXP(predec, 0);
|
|
if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM)
|
|
{
|
|
ii.mark_stack ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (GET_CODE(src) == PLUS && REG_P(dest) && REGNO(dest) == STACK_POINTER_REGNUM)
|
|
{
|
|
/* check for stack variables. */
|
|
rtx reg = XEXP(src, 0);
|
|
rtx cx = XEXP(src, 1);
|
|
if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM && CONST_INT_P(cx))
|
|
paramstart -= INTVAL(cx);
|
|
}
|
|
|
|
if (++pos >= infos->size ())
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (pos == 0 || num_push >= 2)
|
|
return 0;
|
|
|
|
unsigned prologueend = pos;
|
|
|
|
/* search epilogues - there can be multiple epilogues. */
|
|
while (pos < infos->size ())
|
|
{
|
|
while (pos < infos->size ())
|
|
{
|
|
if ((*infos)[pos].in_proepi () != IN_CODE)
|
|
break;
|
|
++pos;
|
|
}
|
|
|
|
/* move epilogues away. */
|
|
for (; pos < infos->size (); ++pos)
|
|
{
|
|
insn_info & ii = (*infos)[pos];
|
|
insn = ii.get_insn ();
|
|
if (JUMP_P(insn) || LABEL_P(insn) || ii.in_proepi () == IN_CODE)
|
|
break;
|
|
|
|
/* omit the frame pointer a5. */
|
|
rtx pattern = PATTERN (insn);
|
|
if (GET_CODE(pattern) == PARALLEL)
|
|
{
|
|
rtx set = XVECEXP(pattern, 0, 0);
|
|
rtx dst = SET_DEST(set);
|
|
ii.mark_stack ();
|
|
/* unlink is last. */
|
|
if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM)
|
|
{
|
|
a5pos.push_back (pos);
|
|
break;
|
|
}
|
|
|
|
}
|
|
else if (GET_CODE(pattern) == SET)
|
|
{
|
|
/* check for move (a7+), x */
|
|
rtx src = SET_SRC(pattern);
|
|
rtx dst = SET_DEST(pattern);
|
|
if (REG_P(dst))
|
|
{
|
|
if (MEM_P(src))
|
|
{
|
|
rtx postinc = XEXP(src, 0);
|
|
if (GET_CODE(postinc) == POST_INC)
|
|
{
|
|
rtx reg = XEXP(postinc, 0);
|
|
if (REG_P(reg) && REGNO(reg) == STACK_POINTER_REGNUM)
|
|
ii.mark_stack ();
|
|
}
|
|
else if (GET_CODE(postinc) == PLUS)
|
|
{
|
|
rtx a5 = XEXP(postinc, 0);
|
|
if (REG_P(a5) && REGNO(a5) == FRAME_POINTER_REGNUM)
|
|
ii.mark_stack ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++pos;
|
|
}
|
|
|
|
unsigned freemask = 0x7fff & ~usable_regs;
|
|
|
|
/* do not remove a4 push/pop in baserel modes, if __saveds or commandline demands it. */
|
|
if (flag_pic > 2)
|
|
{
|
|
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
|
|
tree attr = lookup_attribute ("saveds", attrs);
|
|
if (attr || TARGET_RESTORE_A4 || TARGET_ALWAYS_RESTORE_A4)
|
|
freemask &= ~(1 << PIC_REG);
|
|
}
|
|
|
|
rtx a7 = gen_raw_REG (SImode, STACK_POINTER_REGNUM);
|
|
rtx a5 = gen_raw_REG (SImode, FRAME_POINTER_REGNUM);
|
|
|
|
unsigned changed = 0;
|
|
unsigned adjust = 0;
|
|
unsigned regs_seen = 0;
|
|
unsigned regs_total_size = 0;
|
|
/* now all push/pop insns are in temp. */
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
insn_info & ii = (*infos)[i];
|
|
if (!ii.is_stack ())
|
|
continue;
|
|
|
|
insn = ii.get_insn ();
|
|
rtx pattern = PATTERN (insn);
|
|
/* check the pushed regs, either a vector or single statements */
|
|
if (GET_CODE(pattern) == PARALLEL)
|
|
{
|
|
// do not touch the frame pointer parallel insn.
|
|
rtx set = XVECEXP(pattern, 0, 0);
|
|
rtx dst = SET_DEST(set);
|
|
if (REG_P(dst) && REGNO(dst) == FRAME_POINTER_REGNUM)
|
|
continue;
|
|
|
|
if (ii.in_proepi () == IN_EPILOGUE)
|
|
ii.set_proepi (IN_EPILOGUE_PARALLEL_POP);
|
|
|
|
regs_seen = 0;
|
|
regs_total_size = 0;
|
|
std::vector<rtx> regs;
|
|
std::vector<rtx> clobbers;
|
|
for (int j = 0; j < XVECLEN(pattern, 0); ++j)
|
|
{
|
|
rtx set = XVECEXP(pattern, 0, j);
|
|
if (GET_CODE(set) == CLOBBER)
|
|
{
|
|
clobbers.push_back (set);
|
|
continue;
|
|
}
|
|
rtx dst = SET_DEST(set);
|
|
rtx src = SET_SRC(set);
|
|
rtx reg;
|
|
if (MEM_P(src))
|
|
reg = dst;
|
|
else if (MEM_P(dst))
|
|
reg = src;
|
|
else
|
|
continue;
|
|
|
|
if (i < prologueend)
|
|
paramstart += 4;
|
|
unsigned regbit = 1 << REGNO(reg);
|
|
|
|
++regs_seen;
|
|
if (freemask & regbit)
|
|
{
|
|
log (i < prologueend ? "(f) remove push for %s\n" : "(f) remove pop for %s\n",
|
|
reg_names[REGNO(reg)]);
|
|
if (i < prologueend)
|
|
adjust += GET_MODE_SIZE(GET_MODE(reg));
|
|
}
|
|
else
|
|
{
|
|
regs_total_size += GET_MODE_SIZE(GET_MODE(reg));
|
|
regs.push_back (copy_reg (reg, -1));
|
|
}
|
|
}
|
|
|
|
/* add room for add.
|
|
* push is always using -(a7) addressing.
|
|
* If a5 is used a movem offset(a5) is generated to pop saved registers..
|
|
* Otherwise a7 is used and with (a7)+ addressing.
|
|
*/
|
|
int add1 = i < prologueend || !(ii.get_myuse () & (1 << FRAME_POINTER_REGNUM)) ? 1 : 0;
|
|
if (regs.size () < regs_seen)
|
|
{
|
|
log ("(f) shrinking stack frame from %d to %d\n", regs_seen, regs.size ());
|
|
if (regs.size () <= 2)
|
|
{
|
|
int x = 0;
|
|
for (unsigned k = 0; k < regs.size (); ++k)
|
|
x -= REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
changed = 1;
|
|
for (unsigned k = 0; k < regs.size (); ++k)
|
|
{
|
|
rtx reg = regs[k];
|
|
if (i < prologueend)
|
|
{
|
|
/* push */
|
|
rtx dec = gen_rtx_PRE_DEC(REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, a7);
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, dec);
|
|
rtx set = gen_rtx_SET(mem, reg);
|
|
emit_insn_after (set, insn);
|
|
}
|
|
else
|
|
{
|
|
if ((ii.get_myuse () & (1 << FRAME_POINTER_REGNUM))
|
|
&& (a5offset != -4 || a5_touched)) // no conversion to sp)
|
|
{
|
|
/* pop via a5 + offset*/
|
|
x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
rtx plus = gen_rtx_PLUS(SImode, a5, gen_rtx_CONST_INT (SImode, a5offset + x));
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus);
|
|
rtx set = gen_rtx_SET(reg, mem);
|
|
emit_insn_before (set, insn);
|
|
}
|
|
else
|
|
{
|
|
/* pop */
|
|
rtx dec = gen_rtx_POST_INC(REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, a7);
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, dec);
|
|
rtx set = gen_rtx_SET(reg, mem);
|
|
emit_insn_before (set, insn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rtx parallel = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (regs.size () + add1 + clobbers.size ()));
|
|
rtx plus;
|
|
|
|
int x = 0;
|
|
for (unsigned k = 0; k < regs.size (); ++k)
|
|
x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
|
|
unsigned l = 0;
|
|
/* no add if a5 is used with pop */
|
|
if (add1)
|
|
{
|
|
plus = gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, i < prologueend ? -x : x));
|
|
XVECEXP(parallel, 0, l) = gen_rtx_SET(a7, plus);
|
|
++l;
|
|
}
|
|
|
|
if (i >= prologueend)
|
|
x = (ii.get_myuse () & (1 << FRAME_POINTER_REGNUM)) ? -x : 0;
|
|
|
|
|
|
for (unsigned k = 0; k < regs.size (); ++k, ++l)
|
|
{
|
|
if (i < prologueend)
|
|
{
|
|
/* push */
|
|
plus = gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, -x));
|
|
x -= REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus);
|
|
rtx set = gen_rtx_SET(mem, regs[k]);
|
|
XVECEXP(parallel, 0, l) = set;
|
|
}
|
|
else
|
|
{
|
|
/* pop - use the same register as in the existing insn*/
|
|
if (ii.get_myuse () & (1 << FRAME_POINTER_REGNUM))
|
|
{
|
|
x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
plus = gen_rtx_PLUS(SImode, a5, gen_rtx_CONST_INT (SImode, a5offset + x));
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus);
|
|
rtx set = gen_rtx_SET(regs[k], mem);
|
|
XVECEXP(parallel, 0, l) = set;
|
|
}
|
|
else
|
|
{
|
|
plus = x ? gen_rtx_PLUS(SImode, a7, gen_rtx_CONST_INT (SImode, x)) : a7;
|
|
x += REGNO(regs[k]) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
rtx mem = gen_rtx_MEM (REGNO(regs[k]) > STACK_POINTER_REGNUM ? XFmode : SImode, plus);
|
|
rtx set = gen_rtx_SET(regs[k], mem);
|
|
XVECEXP(parallel, 0, l) = set;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned k = 0; k < clobbers.size (); ++k, ++l)
|
|
{
|
|
rtx clobber = clobbers[k];
|
|
XVECEXP(parallel, 0, l) = clobber;
|
|
}
|
|
|
|
rtx_insn * neu;
|
|
if (i < prologueend)
|
|
neu = emit_insn_after (parallel, insn);
|
|
else
|
|
neu = emit_insn_before (parallel, insn);
|
|
ii.set_insn (neu);
|
|
}
|
|
SET_INSN_DELETED(insn);
|
|
changed = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// shrink from 2/1 to 1/0
|
|
|
|
rtx set = PATTERN (insn);
|
|
|
|
if (i < prologueend)
|
|
{
|
|
/* move x,-(a7). */
|
|
rtx src = SET_SRC(set);
|
|
paramstart += REGNO(src) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
unsigned regbit = 1 << REGNO(src);
|
|
if (freemask & regbit)
|
|
{
|
|
adjust += REGNO(src) > STACK_POINTER_REGNUM ? 12 : 4;
|
|
log ("(f) remove push for %s\n", reg_names[REGNO(src)]);
|
|
SET_INSN_DELETED(insn);
|
|
++changed;
|
|
}
|
|
else
|
|
{
|
|
regs_total_size += GET_MODE_SIZE(GET_MODE(src));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* move (a7)+,x */
|
|
rtx dst = SET_DEST(set);
|
|
unsigned regbit = 1 << REGNO(dst);
|
|
if (freemask & regbit)
|
|
{
|
|
log ("(f) remove pop for %s\n", reg_names[REGNO(dst)]);
|
|
SET_INSN_DELETED(insn);
|
|
++changed;
|
|
|
|
if ((ii.get_myuse () & (1 << FRAME_POINTER_REGNUM)))
|
|
{
|
|
rtx x;
|
|
// the previous offset needs a fix if it's also a a5 pop
|
|
insn_info & pp = (*infos)[i - 1];
|
|
if (pp.is_stack() && (set = single_set(pp.get_insn())) && MEM_P(x = SET_SRC(set))
|
|
&& GET_CODE(x = XEXP(x, 0)) == PLUS && REG_P(XEXP(x, 0)) && REGNO(XEXP(x, 0)) == FRAME_POINTER_REGNUM)
|
|
{
|
|
XEXP(x, 1) = GEN_INT(INTVAL(XEXP(x, 1)) + GET_MODE_SIZE(GET_MODE(SET_SRC(set))));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fix sp offsets. */
|
|
if (!usea5 && adjust)
|
|
{
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.in_proepi () != IN_CODE)
|
|
continue;
|
|
|
|
rtx pattern = PATTERN (ii.get_insn ());
|
|
if (pattern)
|
|
patch_sp (pattern, adjust, ii.get_sp_offset ());
|
|
}
|
|
}
|
|
|
|
if (usea5 && a5offset == -4)
|
|
{
|
|
/* for now only drop the frame pointer if it's not used.
|
|
* Needs tracking of the sp to adjust the offsets.
|
|
*/
|
|
if (!a5_touched)
|
|
{
|
|
log ("(f) dropping unused frame pointer\n");
|
|
for (std::vector<int>::reverse_iterator i = a5pos.rbegin (); i != a5pos.rend (); ++i)
|
|
{
|
|
int index = *i;
|
|
SET_INSN_DELETED((*infos)[index].get_insn ());
|
|
|
|
// move to last insn in epilogue
|
|
while (index - 1 > 0 && (*infos)[index - 1].in_proepi () >= IN_EPILOGUE)
|
|
--index;
|
|
|
|
insn_info & ii = (*infos)[index];
|
|
if (ii.in_proepi () >= IN_EPILOGUE && ii.get_sp_offset () != 0)
|
|
{
|
|
log ("(f) adjusting exit sp\n");
|
|
rtx pattern = gen_rtx_SET(
|
|
a7, gen_rtx_PLUS (SImode, a7, gen_rtx_CONST_INT (SImode, ii.get_sp_offset ())));
|
|
emit_insn_before (pattern, ii.get_insn ());
|
|
}
|
|
}
|
|
|
|
/* convert all parameter accesses via a5 into a7. */
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
insn_info & ii = (*infos)[i];
|
|
|
|
//skip already deleted insns.
|
|
if (GET_CODE(ii.get_insn()) == NOTE)
|
|
continue;
|
|
if (ii.get_myuse () & (1 << FRAME_POINTER_REGNUM))
|
|
{
|
|
ii.a5_to_a7 (a7, regs_total_size - ii.get_sp_offset());
|
|
if (regs_total_size && regs_seen && ii.in_proepi () == IN_EPILOGUE_PARALLEL_POP)
|
|
{
|
|
// exit sp insn needs an +
|
|
rtx pattern = PATTERN (ii.get_insn ());
|
|
unsigned sz = XVECLEN(pattern, 0);
|
|
|
|
rtx parallel = gen_rtx_PARALLEL(VOIDmode, rtvec_alloc (sz + 1));
|
|
unsigned n = 0;
|
|
for (unsigned j = 0; j < sz; ++j)
|
|
{
|
|
rtx set = XVECEXP(pattern, 0, j);
|
|
rtx reg = SET_DEST(set);
|
|
rtx mem = SET_SRC(set);
|
|
rtx plus = XEXP(mem, 0);
|
|
if (n)
|
|
{
|
|
XEXP(plus, 1) = gen_rtx_CONST_INT (SImode, n);
|
|
}
|
|
else
|
|
{
|
|
XEXP(mem, 0) = XEXP(plus, 0);
|
|
}
|
|
n += GET_MODE_SIZE(GET_MODE(reg));
|
|
XVECEXP(parallel, 0, j + 1) = set;
|
|
}
|
|
|
|
rtx a = copy_reg (a7, -1);
|
|
a->frame_related = 1;
|
|
rtx plus = gen_rtx_PLUS(SImode, a, gen_rtx_CONST_INT (SImode, regs_total_size));
|
|
rtx set = gen_rtx_SET(a, plus);
|
|
XVECEXP(parallel, 0, 0) = set;
|
|
SET_INSN_DELETED(ii.get_insn ());
|
|
ii.set_insn (emit_insn_after (parallel, ii.get_insn ()));
|
|
}
|
|
}
|
|
|
|
ii.unset (FRAME_POINTER_REGNUM);
|
|
}
|
|
|
|
update_insn2index ();
|
|
++changed;
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
/* Update the insn_infos to 'know' the value for each register.
|
|
*
|
|
* assignments to registers are optimized by knowing the value. If the same value is assigned, omit that insn.
|
|
*
|
|
* I'm tracking
|
|
*
|
|
* rtx - the value
|
|
*
|
|
* mask - the referenced registers in the value, 0 means that rtx is const, with baserel a4 is not tracked
|
|
*
|
|
* if there is a value for the referenced register(s), the value is extended
|
|
*
|
|
* e.g.
|
|
*
|
|
* ; line 2
|
|
* move.l 12(a7),a0
|
|
*
|
|
* -> rtx = mem(plus(a7, 12)); 0x8000
|
|
*
|
|
* ; line 10
|
|
* move.l 4(a0),d0
|
|
*
|
|
* -> rtx = mem(plus(mem(plus(a7, 12)), 4)); 0x8000; extended with value from a0, thus a7 is used only
|
|
*
|
|
* ;15
|
|
* lea _label,a1
|
|
*
|
|
* -> rtx = symbol_ref(_label) ; 0x0000 == const
|
|
*
|
|
* on jumps the current state is duplicated and merged at the given label
|
|
*
|
|
* on merge only identical info is kept, rest is discarded
|
|
*
|
|
* for each insn for all defined regs the value and mask is discarded before a new value is set.
|
|
*
|
|
* for each insn which is writing to memory, all non const values are discarded.
|
|
*
|
|
*
|
|
* after the track info is complete, each insn setting a register is evaluated against the track info.
|
|
*
|
|
* now redundant loads are found and eliminated
|
|
*
|
|
* Also the used bits per register are tracked.
|
|
* This info is used to eliminate superfluous AND insns.
|
|
*/
|
|
|
|
static unsigned
|
|
track_regs ()
|
|
{
|
|
// reset visited flags
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
ii.clear_visited ();
|
|
ii.set_sp_offset (0);
|
|
}
|
|
|
|
update_label2jump ();
|
|
|
|
// add entry point
|
|
std::multimap<unsigned, track_var *> todo;
|
|
todo.insert (std::make_pair (0, new track_var ()));
|
|
|
|
while (todo.begin () != todo.end ())
|
|
{
|
|
unsigned startpos = todo.begin ()->first;
|
|
track_var * const track = todo.begin ()->second;
|
|
todo.erase (todo.begin ());
|
|
|
|
for (unsigned index = startpos; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
// already visited?
|
|
if (index != startpos && ii.is_visited () && ii.get_track_var ()->no_merge_needed (track))
|
|
break;
|
|
|
|
// only keep common values at labels
|
|
if (ii.is_label ())
|
|
{
|
|
if (ii.is_visited ())
|
|
{
|
|
ii.get_track_var ()->merge (track);
|
|
}
|
|
else
|
|
{
|
|
ii.get_track_var ()->assign (track);
|
|
ii.mark_visited ();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// mark current insn_info and set sp_offset
|
|
ii.mark_visited ();
|
|
ii.get_track_var ()->assign (track);
|
|
|
|
unsigned def = ii.get_def () & 0xffffff;
|
|
if (def)
|
|
{
|
|
// more than one register set? or mask from clobber?
|
|
if (((def - 1) & def) || !ii.get_dst_reg ())
|
|
{
|
|
// exception for autoinc
|
|
if (!ii.get_dst_reg() || !ii.get_src_autoinc())
|
|
track->clear_for_mask (def, index);
|
|
else
|
|
// mark only the auto inc register as unknown
|
|
track->clear(SImode, ii.get_src_mem_regno(), index);
|
|
}
|
|
}
|
|
|
|
// compare must not clear
|
|
if (ii.is_compare ())
|
|
continue;
|
|
|
|
// do not clear if self assigned unless there is an operator
|
|
int dregno = ii.get_dst_regno ();
|
|
unsigned dmask = track->getMask(dregno);
|
|
if (dregno != ii.get_src_regno () || ii.get_src_op ())
|
|
track->clear (ii.get_mode (), dregno, index);
|
|
|
|
|
|
if (ii.is_call ())
|
|
{
|
|
track->clear_aftercall (index);
|
|
continue;
|
|
}
|
|
|
|
rtx set = single_set (ii.get_insn ());
|
|
|
|
// add all referred labels
|
|
if (ii.is_jump ())
|
|
{
|
|
if (ANY_RETURN_P(ii.get_insn ()))
|
|
break;
|
|
|
|
for (j2l_iterator i = jump2label->find (index), k = i; i != jump2label->end () && i->first == k->first; ++i)
|
|
todo.insert (std::make_pair (i->second, new track_var (track)));
|
|
|
|
if (set && GET_CODE(SET_SRC(set)) == IF_THEN_ELSE)
|
|
continue;
|
|
|
|
// dbra == parallel with IF_THEN_ELSE
|
|
if (!set && PATTERN(ii.get_insn()) && GET_CODE(PATTERN(ii.get_insn ())) == PARALLEL && GET_CODE(SET_SRC( XVECEXP (PATTERN(ii.get_insn ()), 0, 0) )) == IF_THEN_ELSE)
|
|
continue;
|
|
|
|
// unconditional jump
|
|
break;
|
|
}
|
|
|
|
if (!set || !ii.get_def ())
|
|
continue;
|
|
|
|
if (dregno < 0)
|
|
{
|
|
rtx dst = SET_DEST(set);
|
|
track->invalidate_mem (dst, index);
|
|
if (ii.get_def())
|
|
track->clear_for_mask(ii.get_def(), index);
|
|
|
|
// reverse assignment
|
|
if (MEM_P(dst) && (MEM_IN_STRUCT_P(dst) || MEM_READONLY_P(dst)) && ii.get_src_reg() && !ii.get_src_op())
|
|
track->set (ii.get_mode (), ii.get_src_regno(), dst, ii.get_myuse(), index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (ii.get_src_op ())
|
|
{
|
|
if (ii.get_src_op() == AND || ii.get_src_op() == IOR || ii.get_src_op() == XOR || ii.get_src_op() == PLUS)
|
|
{
|
|
rtx op = XEXP(SET_SRC(set), 1);
|
|
unsigned mask;
|
|
if (GET_CODE(op) == CONST_INT)
|
|
mask = INTVAL(op);
|
|
else if (REG_P(op))
|
|
mask = track->getMask(REGNO(op));
|
|
else
|
|
op = 0;
|
|
|
|
if (op)
|
|
{
|
|
if (ii.get_src_op() == AND) {
|
|
track->setMask(dregno, mask & dmask, ii.get_mode());
|
|
}
|
|
else if (ii.get_src_op() == IOR) {
|
|
track->setMask(dregno, mask | dmask, ii.get_mode());
|
|
}
|
|
else if (ii.get_src_op() == XOR) {
|
|
track->setMask(dregno, mask | dmask, ii.get_mode());
|
|
}
|
|
else if (ii.get_src_op() == PLUS) {
|
|
track->setMask(dregno, ((mask | dmask) << 1) | 1, ii.get_mode());
|
|
}
|
|
}
|
|
}
|
|
else if (ii.get_src_op() == ZERO_EXTRACT) {
|
|
unsigned mask = (1 << INTVAL(XEXP(SET_SRC(set), 1))) - 1;
|
|
track->setMask(dregno, mask, ii.get_mode());
|
|
}
|
|
else if (ii.get_src_op() == LSHIFTRT || ii.get_src_op() == ASHIFT)
|
|
{
|
|
rtx op = XEXP(SET_SRC(set), 1);
|
|
if (GET_CODE(op) != CONST_INT)
|
|
{
|
|
if (REG_P(op))
|
|
{
|
|
rtx val = track->get(REGNO(op));
|
|
if (val && GET_CODE(val) == CONST_INT && INTVAL(val) == (0xffff & INTVAL(val)))
|
|
op = val;
|
|
else
|
|
op = 0;
|
|
}
|
|
else
|
|
op = 0;
|
|
}
|
|
if (op)
|
|
{
|
|
if (GET_MODE_SIZE(ii.get_mode()) == 2)
|
|
dmask &= 0xffff;
|
|
else
|
|
if (GET_MODE_SIZE(ii.get_mode()) == 1)
|
|
dmask &= 0xff;
|
|
if (ii.get_src_op() == LSHIFTRT)
|
|
track->setMask(dregno, dmask >> INTVAL(op), ii.get_mode());
|
|
else if (ii.get_src_op() == ASHIFT)
|
|
track->setMask(dregno, dmask << INTVAL(op), ii.get_mode());
|
|
}
|
|
}
|
|
else
|
|
track->clear (ii.get_mode (), dregno, index);
|
|
continue;
|
|
}
|
|
|
|
// WHY? or more than one register used: can't cache
|
|
// if (((ii.get_myuse () - 1) & ii.get_myuse ()))
|
|
if (ii.get_src_autoinc () || ((ii.get_myuse () - 1) & ii.get_myuse ()))
|
|
continue;
|
|
|
|
rtx src = SET_SRC(set);
|
|
if (ii.is_src_mem () && src->volatil)
|
|
continue;
|
|
|
|
track->set (ii.get_mode (), dregno, src, ii.get_myuse(), index);
|
|
}
|
|
delete track;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
delete_and_update (unsigned index)
|
|
{
|
|
SET_INSN_DELETED((*infos)[index].get_insn());
|
|
int start = index + 1;
|
|
while ((*infos)[start].is_label())
|
|
++start;
|
|
scan_starts->insert(start);
|
|
(*infos)[index + 1].clear_visited();
|
|
update_insn_infos ();
|
|
}
|
|
|
|
/*
|
|
* Some optimizations (e.g. propagate_moves) might result into an unused assignment behind the loop.
|
|
* delete those insns.
|
|
*/
|
|
static unsigned
|
|
opt_elim_dead_assign (int blocked_regno)
|
|
{
|
|
track_regs ();
|
|
|
|
// unsigned mask = 0;
|
|
|
|
unsigned change_count = 0;
|
|
for (int index = infos->size () - 1; index >= 0; --index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
if (ii.is_compare ())
|
|
{
|
|
if (blocked_regno == FIRST_PSEUDO_REGISTER && ii.get_dst_reg())
|
|
{
|
|
unsigned lmask = ii.get_track_var()->getMask(ii.get_dst_regno());
|
|
if (lmask != 0xffffffff)
|
|
add_reg_note(ii.get_insn(), REG_BIT_MASK, gen_rtx_CONST_INT (SImode, lmask));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (ii.in_proepi () || !ii.get_dst_reg ())
|
|
continue;
|
|
|
|
rtx_insn * insn = ii.get_insn ();
|
|
rtx set = single_set (insn);
|
|
if (!set)
|
|
continue;
|
|
|
|
if (!ii.is_dst_reg() || ii.get_dst_regno () == blocked_regno)
|
|
continue;
|
|
|
|
// more than one register set? e.g. side effect move.l (a0)+,d0
|
|
unsigned def = ii.get_def () & 0xffffff;
|
|
if ((def - 1) & def)
|
|
continue;
|
|
|
|
rtx ssrc = SET_SRC(set);
|
|
rtx_code opcode = GET_CODE(ssrc);
|
|
|
|
// look for superfluous ZERO_EXTEND or SIGN_EXTEND
|
|
if (opcode == ASHIFT || opcode == ASHIFTRT || opcode == LSHIFTRT)
|
|
{
|
|
rtx op = XEXP(ssrc, 1);
|
|
if (REG_P (op))
|
|
{
|
|
int opreg = REGNO (op);
|
|
for (int jndex = index - 1; jndex > 0; --jndex)
|
|
{
|
|
insn_info & jj = (*infos)[jndex];
|
|
if (jj.is_label())
|
|
break;
|
|
|
|
// ZERO_EXTEND and SIGN_EXTEND use and def that register.
|
|
if (jj.is_myuse(opreg))
|
|
{
|
|
if (jj.get_dst_regno() == opreg &&
|
|
(jj.get_src_op() == ZERO_EXTEND || jj.get_src_op() == SIGN_EXTEND))
|
|
{
|
|
delete_and_update(jndex);
|
|
++change_count;
|
|
}
|
|
break;
|
|
}
|
|
// if the reg is set by something different break
|
|
if (jj.is_def(opreg))
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if (opcode == AND)
|
|
{
|
|
track_var * tv = ii.get_track_var();
|
|
unsigned lmask = tv->getMask(ii.get_dst_regno());
|
|
rtx andval = XEXP(SET_SRC(set), 1);
|
|
|
|
if (REG_P(andval))
|
|
{
|
|
rtx val = tv->get(REGNO(andval));
|
|
if (val && GET_CODE(val) == CONST_INT)
|
|
{
|
|
andval = 0;
|
|
long long int lli = INTVAL(val);
|
|
if (lli < 0x100000000LL)
|
|
andval = val;
|
|
}
|
|
}
|
|
if (andval && GET_CODE(andval) == CONST_INT)
|
|
{
|
|
long long int lli = UINTVAL(andval);
|
|
if (lli < 0x100000000LL)
|
|
{
|
|
unsigned nmask = lli;
|
|
if (GET_MODE_SIZE(ii.get_mode()) == 1)
|
|
{
|
|
nmask &= 0xff;
|
|
lmask &= 0xff;
|
|
}
|
|
else if (GET_MODE_SIZE(ii.get_mode()) < 4)
|
|
{
|
|
nmask &= 0xffff;
|
|
lmask &= 0xffff;
|
|
}
|
|
//printf("%d: and 0x%x, %s : 0x%x\n", index, nmask, reg_names[ii.get_dst_regno ()], lmask);
|
|
//debug(ii.get_insn());
|
|
if ((lmask & nmask) == lmask)
|
|
{
|
|
log ("(e) %d: eliminate superfluous 'and' to %s %08x->%08x\n",
|
|
index, reg_names[ii.get_dst_regno ()], lmask, nmask);
|
|
delete_and_update(index);
|
|
++change_count;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (REG_NREGS(ii.get_dst_reg ()) == 1
|
|
&& is_reg_dead (ii.get_dst_regno (), index)
|
|
&& SET_SRC(set)->volatil == 0) // keep reading volatil stuff.
|
|
{
|
|
log ("(e) %d: eliminate dead assign to %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
delete_and_update (index);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
|
|
track_var * track = ii.get_track_var ();
|
|
rtx src = SET_SRC(set);
|
|
// check for redundant load
|
|
if (ii.get_src_op () == 0 && ii.get_dst_reg ())
|
|
{
|
|
if(!ii.is_myuse (ii.get_dst_regno ()) || ii.get_dst_regno () == ii.get_src_regno ())
|
|
{
|
|
if (track->equals (ii.get_mode(), ii.get_dst_regno (), src)
|
|
|| (ii.get_dst_regno () == ii.get_src_regno () && (*infos)[index +1].is_def(FIRST_PSEUDO_REGISTER)))
|
|
{
|
|
log ("(e) %d: eliminate redundant load to %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
delete_and_update (index);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
|
|
if (ii.get_dst_regno () == ii.get_src_regno () && GET_MODE(ii.get_src_reg()) == GET_MODE(ii.get_dst_reg()) && is_reg_dead(FIRST_PSEUDO_REGISTER, index))
|
|
{
|
|
log ("(e) %d: eliminate self load of %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
delete_and_update (index);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
|
|
if (ii.get_src_reg () && track->equals (ii.get_mode(), ii.get_src_regno (), SET_DEST(set)))
|
|
{
|
|
log ("(e) %d: eliminate redundant reverse load to %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
delete_and_update (index);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
if (!ii.get_src_reg ())
|
|
{
|
|
// is there a register holding that value?
|
|
int aliasRegno = track->find_alias (src);
|
|
if (aliasRegno >= 0 && aliasRegno != ii.get_dst_regno ())
|
|
{
|
|
log ("(e) %d: replace load with %s\n", index, reg_names[aliasRegno]);
|
|
if (validate_change (ii.get_insn (), &SET_SRC(set), gen_rtx_REG (ii.get_mode (), aliasRegno), 0))
|
|
++change_count;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// eliminate add dx,dy with dx/dy ==0
|
|
if (ii.get_src_op () == PLUS && ii.get_dst_reg () && ii.get_src_reg() && ii.get_src_regno() <= 7)
|
|
{
|
|
rtx dx = XEXP (src, 0);
|
|
rtx dy = XEXP (src, 1);
|
|
if (REG_P(dx) && REG_P(dy))
|
|
{
|
|
// dx == 0
|
|
rtx v = track->get(REGNO(dx));
|
|
if (v && CONST_INT_P(v) && INTVAL(v) == 0)
|
|
{
|
|
// convert into move dy,dy
|
|
validate_change (ii.get_insn (), &SET_SRC(set), dy, 0);
|
|
log ("(e) %d: convert left add zero into move %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
|
|
// dy == 0
|
|
v = track->get(REGNO(dy));
|
|
if (v && CONST_INT_P(v) && INTVAL(v) == 0)
|
|
{
|
|
// convert into move
|
|
validate_change (ii.get_insn (), &SET_SRC(set), dx, 0);
|
|
|
|
log ("(e) %d: convert right add zero into move %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
static unsigned
|
|
opt_elim_dead_assign2 (int blocked_regno)
|
|
{
|
|
track_regs ();
|
|
|
|
unsigned change_count = 0;
|
|
|
|
// same but top->down
|
|
for (unsigned index = 0; index < infos->size () - 1; ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
if (ii.is_compare ())
|
|
continue;
|
|
|
|
if (ii.in_proepi () || !ii.get_dst_reg ())
|
|
continue;
|
|
|
|
rtx_insn * insn = ii.get_insn ();
|
|
rtx set = single_set (insn);
|
|
if (!set)
|
|
continue;
|
|
|
|
if (!ii.is_dst_reg() || ii.get_dst_regno () == blocked_regno)
|
|
continue;
|
|
|
|
// more than one register set? e.g. side effect move.l (a0)+,d0
|
|
unsigned def = ii.get_def () & 0xffffff;
|
|
if ((def - 1) & def)
|
|
continue;
|
|
|
|
if (ii.get_src_op () == 0 && ii.get_dst_reg ())
|
|
{
|
|
if(!ii.is_myuse (ii.get_dst_regno ()))
|
|
{
|
|
if (!ii.get_src_reg ())
|
|
{
|
|
|
|
insn_info & jj = (*infos)[index + 1];
|
|
// eliminate clr if the next insn makes it obsolete.
|
|
if (GET_MODE_SIZE(ii.get_mode()) == 4 && CONST_INT_P (SET_SRC (set)) && jj.get_dst_regno() == ii.get_dst_regno())
|
|
{
|
|
unsigned val = INTVAL (SET_SRC (set));
|
|
rtx jset = single_set(jj.get_insn());
|
|
if (!jset )
|
|
continue;
|
|
|
|
track_var * track = (*infos)[index].get_track_var ();
|
|
if ((jj.get_mode() == QImode && val <= 0xff && (!jj.is_use_hi24(ii.get_dst_regno()) || track->getMask(ii.get_dst_regno()) <= 0xff))
|
|
|| (jj.get_mode() == HImode && val <= 0xffff && (!jj.is_use32(ii.get_dst_regno()) || track->getMask(ii.get_dst_regno()) <= 0xffff)))
|
|
{
|
|
//printf("%d: use24=%d, use32=%d mask=%d\n", index, jj.is_use_hi24(ii.get_dst_regno()), jj.is_use32(ii.get_dst_regno()), track->getMask(ii.get_dst_regno()));
|
|
//debug(jj.get_insn());
|
|
|
|
log ("(e0) %d: eliminate superfluous clear of %s\n", index, reg_names[ii.get_dst_regno ()]);
|
|
delete_and_update (index);
|
|
++change_count;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return change_count;
|
|
}
|
|
|
|
/*
|
|
* Convert a series of move into absolute address into register based moves.
|
|
*/
|
|
static unsigned
|
|
opt_absolute (void)
|
|
{
|
|
unsigned change_count = 0;
|
|
|
|
for (unsigned i = 0; i < infos->size (); ++i)
|
|
{
|
|
insn_info & ii = (*infos)[i];
|
|
|
|
if (ii.is_compare ())
|
|
continue;
|
|
|
|
if (ii.get_src_op () && ii.is_src_ee () && !ii.get_src_intval ())
|
|
continue;
|
|
|
|
bool is_dst = ii.is_dst_mem () && (ii.has_dst_addr () || ii.get_dst_symbol ()) && !ii.has_dst_memreg ();
|
|
bool is_src = ii.is_src_mem () && (ii.has_src_addr () || ii.get_src_symbol ()) && !ii.has_src_memreg ();
|
|
|
|
if (!is_dst && !is_src)
|
|
continue;
|
|
|
|
if (ii.get_mode () == VOIDmode)
|
|
continue;
|
|
|
|
unsigned freemask = ~(ii.get_use () | ii.get_def ()) & 0x7f00 & usable_regs;
|
|
if (!freemask)
|
|
continue;
|
|
|
|
rtx with_symbol = is_dst ? ii.get_dst_symbol () : ii.get_src_symbol ();
|
|
|
|
std::vector<unsigned> found;
|
|
found.push_back (i);
|
|
int base = ii.get_dst_mem_addr ();
|
|
|
|
int max = base;
|
|
unsigned j = i + 1;
|
|
for (; j < infos->size (); ++j)
|
|
{
|
|
insn_info & jj = (*infos)[j];
|
|
/* TODO: continue also at jump target */
|
|
if (jj.is_jump ())
|
|
continue;
|
|
/* TODO: check if label is visited only from jump targets from herein. then the label is ok. */
|
|
if (jj.is_label ())
|
|
break;
|
|
|
|
unsigned tempmask = freemask & ~(jj.get_use () | jj.get_def ());
|
|
if (!tempmask)
|
|
break;
|
|
freemask = tempmask;
|
|
|
|
if (jj.get_mode () == VOIDmode || jj.is_compare ())
|
|
continue;
|
|
|
|
// ???
|
|
if (jj.get_src_op () && jj.is_src_ee () && !jj.get_src_intval ())
|
|
continue;
|
|
|
|
bool j_dst = jj.is_dst_mem () && jj.get_dst_mem_mem() == 0 && (jj.has_dst_addr () || jj.get_dst_symbol ()) && !jj.has_dst_memreg ()
|
|
&& jj.get_dst_symbol () == with_symbol;
|
|
bool j_src = jj.is_src_mem () && jj.get_src_mem_mem() == 0 && (jj.has_src_addr () || jj.get_src_symbol ()) && !jj.has_src_memreg ()
|
|
&& jj.get_src_symbol () == with_symbol;
|
|
|
|
/* exclude operations on that symbol. */
|
|
|
|
if (j_dst)
|
|
{
|
|
int addr = jj.get_dst_mem_addr ();
|
|
if (addr < base)
|
|
{
|
|
if (max - addr <= 0x7ffe)
|
|
{
|
|
base = addr;
|
|
found.push_back (j);
|
|
continue;
|
|
}
|
|
}
|
|
else if (addr - base <= 0x7ffe)
|
|
{
|
|
if (addr > max)
|
|
max = addr;
|
|
found.push_back (j);
|
|
continue;
|
|
}
|
|
}
|
|
if (j_src)
|
|
{
|
|
int addr = jj.get_src_mem_addr ();
|
|
if (addr < base)
|
|
{
|
|
if (max - addr <= 0x7ffe)
|
|
{
|
|
base = addr;
|
|
found.push_back (j);
|
|
continue;
|
|
}
|
|
}
|
|
else if (addr - base <= 0x7ffe)
|
|
{
|
|
if (addr > max)
|
|
max = addr;
|
|
found.push_back (j);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
unsigned needed = (TUNE_68040_60 || TUNE_68080) ? 10 : 2;
|
|
if (freemask && found.size () > needed)
|
|
{
|
|
unsigned regno = bit2regno (freemask);
|
|
/* check again. */
|
|
for (std::vector<unsigned>::iterator k = found.begin (); k != found.end ();)
|
|
{
|
|
insn_info & kk = (*infos)[*k];
|
|
bool k_dst = kk.is_dst_mem () && (kk.has_dst_addr () || kk.get_dst_symbol ()) && !kk.has_dst_memreg ()
|
|
&& kk.get_dst_symbol () == with_symbol;
|
|
bool k_src = kk.is_src_mem () && (kk.has_src_addr () || kk.get_src_symbol ()) && !kk.has_src_memreg ()
|
|
&& kk.get_src_symbol () == with_symbol;
|
|
if (k_dst && kk.get_dst_mem_addr () - base > 0x7ffc)
|
|
k = found.erase (k);
|
|
else if (k_src && kk.get_src_mem_addr () - base > 0x7ffc)
|
|
k = found.erase (k);
|
|
else if (insn_invalid_p (make_insn_raw (kk.make_absolute2base (regno, base, with_symbol, false)), 0))
|
|
k = found.erase (k);
|
|
else
|
|
++k;
|
|
}
|
|
}
|
|
if (freemask && found.size () > needed)
|
|
{
|
|
unsigned regno = bit2regno (freemask);
|
|
if (with_symbol)
|
|
log ("(b) modifying %d symbol addresses for %s using %s\n", found.size (),
|
|
with_symbol->u.block_sym.fld[0].rt_str, reg_names[regno]);
|
|
else
|
|
log ("(b) modifying %d absolute addresses using %s\n", found.size (), reg_names[regno]);
|
|
|
|
|
|
for (std::vector<unsigned>::iterator k = found.begin (); k != found.end (); ++k)
|
|
{
|
|
insn_info & kk = (*infos)[*k];
|
|
kk.absolute2base (regno, base, with_symbol);
|
|
insn_invalid_p (kk.get_insn (), 0);
|
|
}
|
|
|
|
// load base into reg
|
|
rtx lea;
|
|
|
|
if (with_symbol)
|
|
{
|
|
if (base)
|
|
lea = gen_rtx_SET(
|
|
gen_raw_REG (SImode, regno),
|
|
gen_rtx_CONST (SImode, gen_rtx_PLUS (SImode, with_symbol, gen_rtx_CONST_INT (SImode, base))));
|
|
else
|
|
lea = gen_rtx_SET(gen_raw_REG (SImode, regno), with_symbol);
|
|
}
|
|
else
|
|
lea = gen_rtx_SET(gen_raw_REG (SImode, regno), gen_rtx_CONST_INT (SImode, base));
|
|
rtx_insn * insn = emit_insn_before (lea, ii.get_insn ());
|
|
insn_info nn (insn);
|
|
nn.copy_use (ii);
|
|
nn.scan ();
|
|
nn.fledder (lea);
|
|
nn.mark_def (regno);
|
|
infos->insert (infos->begin () + i, nn);
|
|
|
|
/* mark until last hit is found. */
|
|
for (unsigned k = i + 1; k < infos->size (); ++k)
|
|
{
|
|
(*infos)[k].mark_use (regno);
|
|
if (k == *found.rbegin ())
|
|
break;
|
|
}
|
|
++change_count;
|
|
--i;
|
|
}
|
|
}
|
|
|
|
if (change_count)
|
|
update_insn2index ();
|
|
|
|
return change_count;
|
|
}
|
|
|
|
/**
|
|
* @param index the insn index to start with.
|
|
* @param ii the insn_info of the auto_inc candidate.
|
|
* @param size the size of the increment: 1,2,4,8
|
|
* @param addend the value used in the loop: -1 to search backwards, +1 to search forwards.
|
|
*/
|
|
static int
|
|
try_auto_inc (unsigned index, insn_info & ii, rtx reg, int size, int addend)
|
|
{
|
|
int const regno = REGNO(reg);
|
|
|
|
// log ("starting auto_inc search for %s at %d\n", reg_names[regno], index);
|
|
|
|
// track all fixups to modify
|
|
std::set<unsigned> fixups;
|
|
|
|
// all paths to check
|
|
std::vector<unsigned> todo;
|
|
|
|
todo.push_back (index + addend);
|
|
|
|
std::set<unsigned> visited;
|
|
|
|
bool last_is_add = false;
|
|
while (todo.size () > 0)
|
|
{
|
|
unsigned pos = todo[todo.size () - 1];
|
|
todo.pop_back ();
|
|
|
|
if (pos == index)
|
|
return 0;
|
|
|
|
// prevent double handling
|
|
if (visited.find (pos) != visited.end ())
|
|
continue;
|
|
|
|
for (; (int)pos >= 0 && pos < infos->size (); pos = pos + addend)
|
|
{
|
|
visited.insert (pos);
|
|
insn_info & jj = (*infos)[pos];
|
|
|
|
// do not auto_inc over calls for d0/d1/a0/a1/a7
|
|
if (jj.is_call() && ((1 << regno) & ( (1<<15)|(1<<9)|(1<<8)|(1<<1)|(1<<0) )) != 0)
|
|
return 0;
|
|
|
|
// check all jumps labels for register usage
|
|
if (jj.is_label ())
|
|
{
|
|
if (addend < 0)
|
|
return 0; // no labels backwards
|
|
|
|
if (pos > 0)
|
|
{
|
|
// check previous insn, if the reg is used or defined there
|
|
// that label must been seen before
|
|
insn_info & pp = (*infos)[pos - 1];
|
|
// fprintf(stderr, "regno=%d, pos=%d, used=%d, def=%d, visited=%d", regno, pos - 1, pp.is_use (regno), pp.is_def (regno), visited.find (pos - 1) != visited.end ());
|
|
// debug(pp.get_insn());
|
|
if ((pp.is_use (regno) || pp.is_def (regno)) && visited.find (pos - 1) == visited.end ())
|
|
return 0;
|
|
}
|
|
|
|
// jumps to here which use that register must be already visited.
|
|
// jumps to here which do not use that register are ok.
|
|
for (l2j_iterator j = label2jump->find (jj.get_insn ()->u2.insn_uid), k = j;
|
|
j != label2jump->end () && j->first == k->first; ++j)
|
|
{
|
|
insn_info * ll = insn2info->find (j->second)->second;
|
|
if (ll->is_use (regno)
|
|
// jump from an already visited pos is ok
|
|
&& visited.find (ll->get_index()) == visited.end ())
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// break if no longer used
|
|
if (!jj.is_use (regno)) {
|
|
if (jj.is_def (regno))
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
// abort if a parallel insn is touched.
|
|
if (GET_CODE(PATTERN(jj.get_insn())) == PARALLEL && (jj.is_def(regno) || jj.is_myuse(regno)))
|
|
return 0;
|
|
|
|
if (jj.in_proepi ())
|
|
return 0;
|
|
|
|
// add all labels
|
|
if (jj.is_jump ())
|
|
{
|
|
if (addend < 0)
|
|
return 0; // no jumps backwards
|
|
|
|
for (j2l_iterator j = jump2label->find (pos), k = j; j != jump2label->end () && j->first == k->first; ++j)
|
|
todo.push_back (j->second);
|
|
continue;
|
|
}
|
|
|
|
// not used directly
|
|
if (!jj.is_myuse (regno))
|
|
continue;
|
|
|
|
// can't fixup such kind of insn (yet)
|
|
if (single_set (jj.get_insn ()) == 0)
|
|
return 0;
|
|
|
|
// if reg is src reg, op must be add and addend must be large enough
|
|
bool fix = false;
|
|
if (jj.get_src_mem_regno () == regno)
|
|
{
|
|
if (jj.get_dst_regno () == regno)
|
|
return 0;
|
|
|
|
// no index register
|
|
if (jj.get_src_mem2_reg())
|
|
return 0;
|
|
|
|
if (jj.get_src_mem_addr () * addend < size)
|
|
return 0;
|
|
|
|
// no inc, no double indirect
|
|
if (jj.get_src_mem_mem() != 0)
|
|
return 0;
|
|
|
|
fix = true;
|
|
}
|
|
if (jj.get_dst_mem_regno () == regno)
|
|
{
|
|
if (jj.get_src_regno () == regno)
|
|
return 0;
|
|
|
|
// no index register
|
|
if (jj.get_dst_mem2_reg())
|
|
return 0;
|
|
|
|
if (jj.get_dst_mem_addr () * addend < size)
|
|
return 0;
|
|
|
|
// no inc, no double indirect
|
|
if (jj.get_dst_mem_mem () != 0)
|
|
return 0;
|
|
|
|
fix = true;
|
|
}
|
|
|
|
// direct add
|
|
if (jj.get_src_regno() == jj.get_dst_regno() && jj.get_src_op() == PLUS && jj.get_src_intval() >= size)
|
|
fix = true;
|
|
|
|
if (!fix)
|
|
return 0;
|
|
|
|
fixups.insert (pos);
|
|
|
|
// done if this is an add
|
|
if (jj.is_def (regno))
|
|
{
|
|
// new load ->invalid!
|
|
if (jj.get_src_op() != PLUS)
|
|
return 0;
|
|
|
|
last_is_add = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!last_is_add && addend < 0)
|
|
return 0;
|
|
|
|
if (!fixups.size ())
|
|
return 0;
|
|
|
|
if (!ii.make_post_inc (regno, addend))
|
|
return 0;
|
|
|
|
log ("(i) auto_inc for %s at %d - %d fixups\n", reg_names[regno], index, fixups.size ());
|
|
|
|
// fix all offsets / adds
|
|
for (std::set<unsigned>::iterator k = fixups.begin (); k != fixups.end (); ++k)
|
|
{
|
|
insn_info & kk = (*infos)[*k];
|
|
// log ("(i) fixup at %d\n", *k);
|
|
// debug_rtx(kk.get_insn());
|
|
kk.auto_inc_fixup (regno, size, addend);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Convert a series of reg with offset ( (ax), 4(ax), 8(ax), ...) into autoincx ( (ax+), (ax+), (ax+), ...)
|
|
*
|
|
* 1. search a mem(reg) without offset and either src or dst is using that reg
|
|
* 2. follow paths until reg is dead
|
|
* 3. if there is another mem(reg) with offset check that
|
|
* a) offset fits last mode size
|
|
* b) all remaining insn using that reg can be updated by
|
|
* i) decrement the offset
|
|
* ii) decrement the add value
|
|
*/
|
|
static unsigned
|
|
opt_autoinc ()
|
|
{
|
|
unsigned change_count = 0;
|
|
|
|
update_label2jump ();
|
|
|
|
for (unsigned index = 0; index < infos->size (); ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
if (ii.in_proepi ())
|
|
continue;
|
|
|
|
if (!INSN_P(ii.get_insn ()))
|
|
continue;
|
|
|
|
if (GET_CODE(PATTERN(ii.get_insn())) == PARALLEL)
|
|
continue;
|
|
|
|
rtx set = single_set(ii.get_insn());
|
|
if (!set)
|
|
continue;
|
|
|
|
// move.w (a0)+,a1 reads a word but writes a long...
|
|
int dsize = GET_MODE_SIZE(ii.get_mode());
|
|
if (dsize > 4 && !((TARGET_68881 && ii.get_mode() == DFmode) || ii.get_mode() == DImode))
|
|
return 0;
|
|
|
|
int ssize = dsize;
|
|
if (ii.is_src_mem()) {
|
|
if (GET_CODE(SET_SRC(set)) == SIGN_EXTEND)
|
|
ssize /= 2;
|
|
else if (GET_CODE(SET_SRC(set)) == FLOAT_TRUNCATE)
|
|
ssize *= 2;
|
|
}
|
|
|
|
// neither INC or nested MEM is allowed
|
|
// check if src is a mem which can be converted into an auto inc
|
|
if (ii.is_src_mem() && ii.get_src_mem_regno () >= 8 && ii.get_src_mem_mem () == 0
|
|
&& ii.get_src_mem_regno () != ii.get_dst_regno ()
|
|
&& ii.get_src_mem_regno () != ii.get_dst_mem_regno ()
|
|
&& ii.get_src_mem_regno () != ii.get_dst_mem2_regno ()
|
|
&& ii.get_src_mem_regno () != ii.get_src_mem2_regno ())
|
|
{
|
|
if (!ii.get_src_mem_addr ())
|
|
change_count += try_auto_inc (index, ii, ii.get_src_mem_reg (), ssize, 1);
|
|
else
|
|
if (ii.get_src_mem_addr () == -ssize)
|
|
change_count += try_auto_inc (index, ii, ii.get_src_mem_reg (), ssize, -1);
|
|
}
|
|
// neither INC or nested MEM is allowed
|
|
// check if dst is a mem which can be converted into an auto inc
|
|
if (ii.is_dst_mem() && ii.get_dst_mem_regno () >= 8 && ii.get_dst_mem_mem () == 0
|
|
&& ii.get_src_regno () != ii.get_dst_mem_regno ()
|
|
&& ii.get_src_mem_regno () != ii.get_dst_mem_regno ()
|
|
&& ii.get_src_mem2_regno () != ii.get_dst_mem_regno ()
|
|
&& ii.get_dst_mem2_regno () != ii.get_dst_mem_regno ())
|
|
{
|
|
if (!ii.get_dst_mem_addr())
|
|
change_count += try_auto_inc (index, ii, ii.get_dst_mem_reg (), dsize, 1);
|
|
else
|
|
if (ii.get_dst_mem_addr () == -dsize)
|
|
change_count += try_auto_inc (index, ii, ii.get_dst_mem_reg (), dsize, -1);
|
|
}
|
|
}
|
|
|
|
return change_count;
|
|
}
|
|
|
|
|
|
/*
|
|
* A final pass, with these optimizations:
|
|
*
|
|
* - convert cmp #0,ax int move.l ax,dy if there is a free dy
|
|
*/
|
|
static unsigned
|
|
opt_final()
|
|
{
|
|
unsigned change_count = 0;
|
|
for (unsigned index = 0; index < infos->size(); ++index)
|
|
{
|
|
insn_info &ii = (*infos)[index];
|
|
|
|
// cmp #0,ax
|
|
if (ii.is_compare() && ii.get_dst_reg() && ii.get_dst_regno() >= 8 && ii.get_dst_regno() <= 15 && !ii.is_src_mem() && ii.is_src_const() && ii.get_src_intval() == 0)
|
|
{
|
|
// if there is a free dx register use a move
|
|
unsigned avail_dx = (usable_regs & ~ii.get_use()) & 0xff;
|
|
if (avail_dx)
|
|
{
|
|
unsigned regno = 0;
|
|
while ((avail_dx & 1) == 0)
|
|
{
|
|
++regno;
|
|
avail_dx >>= 1;
|
|
}
|
|
rtx dx = gen_raw_REG(SImode, regno);
|
|
rtx ax = gen_raw_REG(SImode, ii.get_dst_regno());
|
|
rtx set = gen_rtx_SET(dx, ax);
|
|
emit_insn_before (set, ii.get_insn());
|
|
|
|
|
|
rtx cmp = gen_rtx_SET(cc0_rtx,
|
|
gen_rtx_COMPARE (SImode, dx, gen_rtx_CONST_INT (SImode, 0)));
|
|
//rtx_insn * neu =
|
|
emit_insn_before (cmp, ii.get_insn());
|
|
SET_INSN_DELETED(ii.get_insn());
|
|
|
|
log ("(z) cmp.w #0,%s -> move.l %s,%s\n", reg_names[ii.get_dst_regno()], reg_names[ii.get_dst_regno()], reg_names[regno]);
|
|
|
|
++ change_count;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
static unsigned
|
|
opt_declear()
|
|
{
|
|
unsigned change_count = 0;
|
|
for (unsigned index = 0; index < infos->size(); ++index)
|
|
{
|
|
insn_info &ii = (*infos)[index];
|
|
// search moveq #0,dx
|
|
if (ii.is_dst_reg() && ii.get_dst_regno() < 8 && index + 1 < infos->size())
|
|
{
|
|
rtx set = single_set(ii.get_insn());
|
|
if (set)
|
|
{
|
|
rtx src = SET_SRC(set);
|
|
if (GET_CODE(src) == CONST_INT && INTVAL(src) == 0)
|
|
{
|
|
// used in next insn as src and dead?
|
|
insn_info & jj = (*infos)[index + 1];
|
|
rtx set1 = single_set(jj.get_insn());
|
|
if (set1 && jj.get_src_op() == 0 && !jj.is_compare() && !jj.get_multi_reg() && jj.get_src_reg() && jj.get_src_regno() == ii.get_dst_regno()
|
|
&& is_reg_dead(ii.get_dst_regno(), index + 1))
|
|
{
|
|
if (validate_change(jj.get_insn(), &SET_SRC(set1), src, 0))
|
|
{
|
|
SET_INSN_DELETED(ii.get_insn());
|
|
log("(z) %d: use clear instead of reg %s with #0\n", index, reg_names[jj.get_src_regno()]);
|
|
++ change_count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert
|
|
* lea <y>,ax
|
|
* move (ax),...
|
|
* into
|
|
* move (<y>),...
|
|
*
|
|
* if ax is reag dead
|
|
*/
|
|
static unsigned
|
|
opt_lea_mem()
|
|
{
|
|
unsigned change_count = 0;
|
|
for (unsigned index = 1; index< infos->size(); ++index)
|
|
{
|
|
insn_info &ii = (*infos)[index - 1];
|
|
|
|
// load into register and no self load?
|
|
if (!ii.get_dst_reg() || ii.get_src_regno() == ii.get_dst_regno())
|
|
continue;
|
|
|
|
rtx set0 = single_set(ii.get_insn());
|
|
if (!set0)
|
|
continue;
|
|
|
|
insn_info &jj = (*infos)[index ];
|
|
rtx set = single_set(jj.get_insn());
|
|
if (!set)
|
|
continue;
|
|
|
|
rtx src = SET_SRC(set);
|
|
if (!MEM_P(src))
|
|
continue;
|
|
|
|
rtx reg = XEXP(src, 0);
|
|
if (!REG_P(reg))
|
|
continue;
|
|
|
|
if (REGNO(reg) != (unsigned)ii.get_dst_regno() || REGNO(reg) == (unsigned)jj.get_dst_mem_regno() || !is_reg_dead(REGNO(reg), index))
|
|
continue;
|
|
|
|
// try the conversion
|
|
if (validate_change(jj.get_insn(), &XEXP(src, 0), SET_SRC(set0), 0))
|
|
{
|
|
SET_INSN_DELETED(ii.get_insn());
|
|
log("(l) lea removed at %d\n", index - 1);
|
|
++change_count;
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
|
|
/**
|
|
* Expand "clr mem" into "moveq #0,dx; move dx,mem", if possible.
|
|
* Perform the cleanup in opt_final().
|
|
*/
|
|
static unsigned
|
|
opt_clear()
|
|
{
|
|
unsigned change_count = 0;
|
|
if (TUNE_68000)
|
|
for (unsigned index = 0; index< infos->size(); ++index)
|
|
{
|
|
insn_info &ii = (*infos)[index];
|
|
|
|
if (GET_MODE_SIZE(ii.get_mode()) > 4)
|
|
continue;
|
|
|
|
rtx set0 = single_set(ii.get_insn());
|
|
if (!set0)
|
|
continue;
|
|
|
|
rtx src = SET_SRC(set0);
|
|
if (GET_CODE(src) != CONST_INT || INTVAL(src))
|
|
continue;
|
|
|
|
if (!MEM_P(SET_DEST(set0)))
|
|
continue;
|
|
|
|
unsigned regs = ~ii.get_use() & usable_regs & 0xff;
|
|
if (!regs)
|
|
continue;
|
|
|
|
unsigned regno = 0;
|
|
while (!(regs & 1))
|
|
{
|
|
++regno;
|
|
regs >>= 1;
|
|
}
|
|
|
|
if (validate_change(ii.get_insn(), &SET_SRC(set0), gen_rtx_REG(GET_MODE(SET_DEST(set0)), regno), 0))
|
|
{
|
|
rtx set = gen_rtx_SET(gen_rtx_REG(SImode, regno), gen_rtx_CONST_INT(SImode, 0));
|
|
emit_insn_before (set, ii.get_insn());
|
|
++change_count;
|
|
|
|
log("(z) %d: use reg %s with #0 instead of clear\n", index, reg_names[regno]);
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
/**
|
|
* Exchange insns if dst operand of an insn is used in the next insn as src operand.
|
|
*/
|
|
static void
|
|
opt_pipeline_insns()
|
|
{
|
|
// up
|
|
for (unsigned index = 1; index < infos->size() - 1; ++index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
|
|
// only check if not in prolog and a register is set
|
|
rtx iiset = single_set(ii.get_insn());
|
|
if (ii.in_proepi() || ii.is_compare() || !ii.get_dst_reg() || ii.get_hard() || !iiset || ii.is_myuse (FIRST_PSEUDO_REGISTER))
|
|
continue;
|
|
|
|
insn_info & jj = (*infos)[index + 1];
|
|
// don't touch compares and check for register overlap and CC use
|
|
if (jj.is_compare() || !(jj.get_myuse() & ii.get_def()))
|
|
continue;
|
|
|
|
// check previous insn
|
|
insn_info & hh = (*infos)[index - 1];
|
|
rtx hhset = single_set(hh.get_insn());
|
|
if (hh.is_call() || hh.is_label() || hh.is_jump() || hh.in_proepi() || hh.is_compare() || hh.get_hard() || !hhset || !hh.get_dst_reg() || hh.is_myuse (FIRST_PSEUDO_REGISTER))
|
|
continue;
|
|
|
|
// overlap with current insn
|
|
if (((ii.get_myuse() | ii.get_def()) & (hh.get_myuse() | hh.get_def()) & ~(1<<(FIRST_PSEUDO_REGISTER+1))) != 0
|
|
|| rtx_equal_p(SET_SRC(iiset), SET_DEST(hhset)))
|
|
continue;
|
|
|
|
rtx pat = PATTERN(hh.get_insn());
|
|
|
|
// don't move volatil insns
|
|
if (pat->volatil)
|
|
continue;
|
|
|
|
// fprintf(stderr, "reorder insns: ");
|
|
// debug_rtx(hh.get_insn());
|
|
// debug_rtx(ii.get_insn());
|
|
|
|
// swap da insns
|
|
rtx_insn * head = PREV_INSN(hh.get_insn());
|
|
rtx_insn * tail = NEXT_INSN(ii.get_insn());
|
|
if (BLOCK_FOR_INSN(head) == BLOCK_FOR_INSN(tail))
|
|
{
|
|
rtx_insn * i = ii.get_insn();
|
|
rtx_insn * h = hh.get_insn();
|
|
|
|
remove_insn(i);
|
|
remove_insn(h);
|
|
|
|
add_insn_after(i, head, BLOCK_FOR_INSN(head));
|
|
add_insn_before(h, tail, BLOCK_FOR_INSN(tail));
|
|
|
|
std::swap((*infos)[index], (*infos)[index - 1]);
|
|
log("(n) reordered insn %d<->%d\n", index - 1, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* insert a moveq #0, dx .
|
|
*/
|
|
static unsigned
|
|
opt_insert_move0()
|
|
{
|
|
std::set<unsigned> positions;
|
|
|
|
unsigned label = 0;
|
|
unsigned change_count = 0;
|
|
for (unsigned index = 0, pos = 0; index< infos->size(); ++index, ++pos)
|
|
{
|
|
insn_info &ii = (*infos)[index];
|
|
|
|
if (ii.is_label())
|
|
{
|
|
label = index;
|
|
continue;
|
|
}
|
|
|
|
|
|
if (GET_MODE_SIZE(ii.get_mode()) >= 4)
|
|
continue;
|
|
|
|
int regno = ii.get_dst_regno();
|
|
if (regno < 0 || regno > 7)
|
|
continue;
|
|
|
|
if (ii.is_use(regno))
|
|
continue;
|
|
|
|
rtx set = single_set(ii.get_insn());
|
|
if (!set)
|
|
continue;
|
|
rtx src = SET_SRC (set);
|
|
if (!MEM_P(src))
|
|
continue;
|
|
|
|
// this is a candidate - check if there is a AND before the next LABEL.
|
|
// a jump to the last label exits the search.
|
|
bool ok = true;
|
|
insn_info * found = 0;
|
|
int val = GET_MODE_SIZE(ii.get_mode()) == 1 ? 0xff : 0xffff;
|
|
unsigned to = MIN(pos + 10, infos->size());
|
|
for (unsigned pos = index + 1; ok && pos < to; ++pos)
|
|
{
|
|
insn_info & pp = (*infos)[pos];
|
|
if (pp.is_label() || ANY_RETURN_P(PATTERN (pp.get_insn())))
|
|
break;
|
|
|
|
if (pp.is_jump())
|
|
{
|
|
for (j2l_iterator i = jump2label->find (pos), k = i; i != jump2label->end () && i->first == k->first; ++i)
|
|
if (label == i->second)
|
|
{
|
|
ok = false;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!pp.is_compare() && pp.get_dst_regno() == regno)
|
|
{
|
|
if (pp.get_src_op() == AND && pp.get_src_intval() == val)
|
|
found = &pp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
continue;
|
|
|
|
rtx dst = SET_DEST (set);
|
|
|
|
// fprintf(stderr, "modesize=%d ", GET_MODE_SIZE(ii.get_mode()));
|
|
// debug(ii.get_insn());
|
|
|
|
if (REG_P (dst))
|
|
{
|
|
// convert to strict_low_part
|
|
rtx slp = gen_rtx_STRICT_LOW_PART (
|
|
//GET_MODE (dst)
|
|
VOIDmode
|
|
, dst);
|
|
if (!validate_unshare_change(ii.get_insn(), &SET_DEST (set), slp, false))
|
|
continue;
|
|
log("(0) %d: to strict_low_part,%s\n", index, reg_names[regno]);
|
|
}
|
|
|
|
rtx nset = gen_rtx_SET(gen_rtx_REG(found->get_mode(), regno), gen_rtx_CONST_INT(found->get_mode(), 0));
|
|
emit_insn_before (nset, ii.get_insn());
|
|
++change_count;
|
|
|
|
log("(0) %d: prepend moveq #0,%s\n", index, reg_names[regno]);
|
|
positions.insert(pos++);
|
|
}
|
|
|
|
return change_count;
|
|
}
|
|
|
|
|
|
void print_inline_info()
|
|
{
|
|
unsigned count = 0;
|
|
for (unsigned index = 0; index < infos->size(); ++index)
|
|
{
|
|
if (!(*infos)[index].in_proepi())
|
|
++count;
|
|
}
|
|
|
|
for (unsigned m = usable_regs >> 2; m; m>>=1)
|
|
{
|
|
if (count && (m&1))
|
|
--count;
|
|
}
|
|
|
|
printf(":bbb: inline weight = %4d\t%s\n", count, get_current_function_name ());
|
|
}
|
|
|
|
static unsigned
|
|
opt_shift (void)
|
|
{
|
|
unsigned change_count = 0;
|
|
for (int index = infos->size () - 2; index > 0; --index)
|
|
{
|
|
insn_info & ii = (*infos)[index];
|
|
// it's a shift ?
|
|
if (ii.get_mode() != SImode || !ii.get_dst_reg() || !(ii.get_src_op() == ASHIFT || ii.get_src_op() == ASHIFTRT || ii.get_src_op() == LSHIFTRT))
|
|
continue;
|
|
|
|
int dy = ii.get_dst_regno();
|
|
|
|
// check the next insn if only a word/byte is used.
|
|
insn_info & next = (*infos)[index + 1];
|
|
int usedSize = next.getX(dy);
|
|
if (usedSize >= 4)
|
|
continue;
|
|
|
|
// debug(ii.get_insn());
|
|
machine_mode mode = usedSize == 1 ? QImode : HImode;
|
|
int srcop = ii.get_src_op(); // can be changed
|
|
|
|
// are there insns like move.l dx,dy in front of that can be changed too?
|
|
bool reduce = false;
|
|
for (int jndex = index - 1; jndex > 0; --jndex)
|
|
{
|
|
insn_info * jj = &(*infos)[jndex];
|
|
if (jj->is_label())
|
|
break;
|
|
|
|
// skip unrelated insns
|
|
if (!jj->is_def(dy) && !jj->is_myuse(dy))
|
|
continue;
|
|
|
|
// we want sign_extend or set
|
|
if (!jj->is_def(dy))
|
|
break;
|
|
|
|
// debug(jj->get_insn());
|
|
|
|
// there might be the use of a temp register:
|
|
// moveq #0,dx
|
|
// move.w dy,dx
|
|
// move.l dx,dy <--- this one
|
|
if (jj->get_mode() == SImode && !jj->get_src_op() && jj->get_src_reg())
|
|
{
|
|
int dx = jj->get_src_regno();
|
|
// search the assignment for dx
|
|
|
|
for (int kndex = jndex - 1; kndex > 0; --kndex)
|
|
{
|
|
insn_info * kk = &(*infos)[kndex];
|
|
if (kk->is_label())
|
|
break;
|
|
|
|
// skip unrelated insns
|
|
if (!kk->is_def(dx) && !kk->is_myuse(dx))
|
|
continue;
|
|
|
|
// we want sign_extend or set
|
|
if (!kk->is_def(dx))
|
|
break;
|
|
|
|
if (kk->get_mode() == mode && !kk->get_src_op())
|
|
{
|
|
if (srcop == ASHIFTRT)
|
|
{
|
|
// we need a clr.l dx
|
|
insn_info * ll = &(*infos)[kndex - 1];
|
|
if (ll->get_dst_reg() && ll->get_dst_regno() == dx && ll->get_mode() == SImode
|
|
&& !ll->get_src_op() && !ll->is_src_mem()
|
|
&& ll->get_src_intval() == 0)
|
|
{
|
|
srcop = LSHIFTRT;
|
|
reduce = true;
|
|
}
|
|
}
|
|
else
|
|
reduce = true;
|
|
|
|
// if dx is dead after assignment to dy
|
|
if (reduce && is_reg_dead(dx, jndex))
|
|
{
|
|
// assign src to dy unless src == dy
|
|
if (!kk->get_src_reg() || kk->get_src_regno() != dy)
|
|
{
|
|
rtx set = single_set(kk->get_insn());
|
|
rtx x = gen_rtx_SET(gen_rtx_REG (mode, dy), SET_SRC (set));
|
|
rtx notes = REG_NOTES (kk->get_insn());
|
|
rtx_insn * neu = emit_insn_before (x, kk->get_insn ());
|
|
REG_NOTES(neu) = notes;
|
|
}
|
|
SET_INSN_DELETED(kk->get_insn());
|
|
SET_INSN_DELETED(jj->get_insn());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
// there might be one or two sign extends
|
|
// ext.w dy
|
|
// ext.l dy
|
|
else if (jj->get_mode() == SImode && jj->get_src_op() == SIGN_EXTEND)
|
|
{
|
|
if (GET_MODE(jj->get_src_reg()) == mode)
|
|
{
|
|
SET_INSN_DELETED(jj->get_insn());
|
|
reduce = true;
|
|
}
|
|
else if (mode == QImode && GET_MODE(jj->get_src_reg()) == HImode)
|
|
{
|
|
// check previous insn for 2nd SIGN_EXTEND
|
|
insn_info * ll = &(*infos)[jndex - 1];
|
|
if (ll->get_dst_reg() && ll->get_dst_regno() == dy
|
|
&& ll->get_src_op() == SIGN_EXTEND
|
|
&& GET_MODE(ll->get_src_reg()) == QImode)
|
|
{
|
|
SET_INSN_DELETED(ll->get_insn());
|
|
SET_INSN_DELETED(jj->get_insn());
|
|
reduce = true;
|
|
}
|
|
}
|
|
}
|
|
// there might be an AND
|
|
// and.l #255,dy
|
|
else if (jj->get_mode() == SImode && jj->get_src_op() == AND
|
|
&& !jj->is_src_mem())
|
|
{
|
|
int val = jj->get_src_intval();
|
|
if ((val == 255 && mode == QImode) || (val == 65535 && mode == HImode))
|
|
{
|
|
reduce = true;
|
|
if (srcop == ASHIFTRT)
|
|
srcop = LSHIFTRT;
|
|
SET_INSN_DELETED(jj->get_insn());
|
|
}
|
|
}
|
|
// move.b ..,dy or move.w ...,dy
|
|
else if (jj->get_mode() == mode)
|
|
{
|
|
if (srcop == ASHIFT)
|
|
reduce = true;
|
|
else if (srcop == ASHIFTRT)
|
|
{
|
|
// we need a clr.l dy
|
|
insn_info * ll = &(*infos)[jndex - 1];
|
|
if (ll->get_dst_reg() && ll->get_dst_regno() == dy && ll->get_mode() == SImode
|
|
&& !ll->get_src_op() && !ll->is_src_mem()
|
|
&& ll->get_src_intval() == 0)
|
|
{
|
|
srcop = LSHIFTRT;
|
|
reduce = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (reduce)
|
|
{
|
|
rtx op1 = XEXP(SET_SRC(PATTERN(ii.get_insn())), 1);
|
|
rtx r = gen_rtx_REG (mode, dy);
|
|
rtx shift;
|
|
if (srcop == ASHIFT)
|
|
shift = gen_rtx_ASHIFT (mode, r, op1);
|
|
else if (srcop == ASHIFTRT)
|
|
shift = gen_rtx_ASHIFTRT (mode, r, op1);
|
|
else
|
|
shift = gen_rtx_LSHIFTRT (mode, r, op1);
|
|
rtx x = gen_rtx_SET(r, shift);
|
|
rtx notes = REG_NOTES (ii.get_insn());
|
|
SET_INSN_DELETED(ii.get_insn());
|
|
|
|
rtx_insn * neu = emit_insn_before (x, ii.get_insn ());
|
|
REG_NOTES(neu) = notes;
|
|
++change_count;
|
|
|
|
log ("(h) long shift replaced with shorter variant for %s\n", reg_names[ii.get_dst_regno ()]);
|
|
}
|
|
}
|
|
return change_count;
|
|
}
|
|
|
|
|
|
namespace
|
|
{
|
|
|
|
const pass_data pass_data_bbb_optimizations =
|
|
{ RTL_PASS, /* type */
|
|
"bebbo's-optimizers", /* name */
|
|
OPTGROUP_NONE, /* optinfo_flags */
|
|
TV_NONE, /* tv_id */
|
|
0, /* properties_required */
|
|
0, /* properties_provided */
|
|
0, /* properties_destroyed */
|
|
0, /* todo_flags_start */
|
|
0, //( TODO_df_finish | TODO_df_verify), /* todo_flags_finish */
|
|
};
|
|
|
|
class pass_bbb_optimizations : public rtl_opt_pass
|
|
{
|
|
public:
|
|
pass_bbb_optimizations (gcc::context *ctxt) :
|
|
rtl_opt_pass (pass_data_bbb_optimizations, ctxt), pp (0)
|
|
{
|
|
}
|
|
|
|
/* opt_pass methods: */
|
|
virtual bool
|
|
gate (function * fun)
|
|
{
|
|
::optimize_this_for_speed_p = optimize_function_for_speed_p (fun);
|
|
|
|
if (!string_bbb_opts)
|
|
string_bbb_opts = "+";
|
|
|
|
return TARGET_M68K && optimize > 0 && string_bbb_opts && !strchr (string_bbb_opts, '-');
|
|
}
|
|
|
|
virtual unsigned int
|
|
execute (function *)
|
|
{
|
|
if (infos == NULL)
|
|
{
|
|
infos = new std::vector<insn_info>();
|
|
label2jump = new std::multimap<int, rtx_insn *>();
|
|
jump2label = new std::multimap<unsigned, unsigned>();
|
|
insn2info = new std::map<rtx_insn *, insn_info *>();
|
|
scan_starts = new std::set<unsigned>();
|
|
}
|
|
return execute_bbb_optimizations ();
|
|
}
|
|
|
|
opt_pass *
|
|
clone ()
|
|
{
|
|
pass_bbb_optimizations * bbb = new pass_bbb_optimizations (m_ctxt);
|
|
bbb->pp = pp + 1;
|
|
return bbb;
|
|
}
|
|
|
|
unsigned int pp;
|
|
|
|
unsigned
|
|
execute_bbb_optimizations (void);
|
|
};
|
|
// class pass_bbb_optimizations
|
|
|
|
/* Main entry point to the pass. */
|
|
unsigned
|
|
pass_bbb_optimizations::execute_bbb_optimizations (void)
|
|
{
|
|
int done;
|
|
dump_cycles = strchr (string_bbb_opts, 'C') != 0;
|
|
dump_reg_track = strchr (string_bbb_opts, 'R') != 0;
|
|
be_very_verbose = strchr (string_bbb_opts, 'V') != 0;
|
|
be_verbose = strchr (string_bbb_opts, 'v') != 0;
|
|
if (be_verbose && be_very_verbose)
|
|
++be_very_verbose;
|
|
if (be_very_verbose)
|
|
be_verbose = true;
|
|
|
|
bool do_commute_add_move = strchr (string_bbb_opts, 'a') || strchr (string_bbb_opts, '+');
|
|
bool do_absolute = strchr (string_bbb_opts, 'b') || strchr (string_bbb_opts, '+');
|
|
bool do_const_cmp_to_sub = strchr (string_bbb_opts, 'c') || strchr (string_bbb_opts, '+');
|
|
bool do_elim_dead_assign = strchr (string_bbb_opts, 'e') || strchr (string_bbb_opts, '+');
|
|
bool do_shrink_stack_frame = strchr (string_bbb_opts, 'f') || strchr (string_bbb_opts, '+');
|
|
bool do_handle_shift = strchr (string_bbb_opts, 'h') || strchr (string_bbb_opts, '+');
|
|
bool do_autoinc = strchr (string_bbb_opts, 'i') || strchr (string_bbb_opts, '+');
|
|
bool do_lea_mem = strchr (string_bbb_opts, 'l') || strchr (string_bbb_opts, '+');
|
|
bool do_merge_add = strchr (string_bbb_opts, 'm') || strchr (string_bbb_opts, '+');
|
|
bool do_pipeline = strchr (string_bbb_opts, 'n') || strchr (string_bbb_opts, '+');
|
|
bool do_propagate_moves = strchr (string_bbb_opts, 'p') || strchr (string_bbb_opts, '+');
|
|
bool do_bb_reg_rename = strchr (string_bbb_opts, 'r') || strchr (string_bbb_opts, '+');
|
|
bool do_opt_strcpy = strchr (string_bbb_opts, 's') || strchr (string_bbb_opts, '+');
|
|
bool do_opt_0 = strchr (string_bbb_opts, '0') || strchr (string_bbb_opts, '+');
|
|
bool do_opt_final = strchr (string_bbb_opts, 'z') || strchr (string_bbb_opts, '+');
|
|
|
|
if (be_very_verbose)
|
|
log ("ENTER\n");
|
|
|
|
unsigned r = update_insns ();
|
|
if (!r)
|
|
{
|
|
if (do_handle_shift && opt_shift())
|
|
update_insns ();
|
|
|
|
if (do_lea_mem && opt_lea_mem())
|
|
update_insns ();
|
|
|
|
if (do_opt_final && opt_clear())
|
|
update_insns ();
|
|
|
|
if (do_opt_0 && opt_insert_move0())
|
|
{XUSE('0'); update_insns(); }
|
|
|
|
pass = 0;
|
|
for (;;)
|
|
{
|
|
XUSE('+');
|
|
++pass;
|
|
done = 1;
|
|
if (be_very_verbose)
|
|
log ("pass %d\n", pass);
|
|
|
|
if (do_opt_strcpy && opt_strcpy ())
|
|
{XUSE('s'); done = 0; update_insns (); }
|
|
|
|
if (do_commute_add_move && opt_commute_add_move ())
|
|
{XUSE('a'); done = 0; update_insns (); }
|
|
|
|
if (do_propagate_moves && opt_propagate_moves ())
|
|
{XUSE('p'); done = 0; update_insns (); }
|
|
|
|
if (do_const_cmp_to_sub && opt_const_cmp_to_sub ())
|
|
{XUSE('c'); done = 0; update_insns (); }
|
|
|
|
if (do_merge_add && opt_merge_add ())
|
|
{XUSE('m'); done = 0; update_insns(); }
|
|
|
|
if (do_absolute && opt_absolute ())
|
|
{XUSE('b'); done = 0; update_insns (); }
|
|
|
|
if (do_bb_reg_rename)
|
|
if (opt_reg_rename ())
|
|
{
|
|
XUSE('r');
|
|
update_insns ();
|
|
done = 0;
|
|
}
|
|
|
|
/* convert back to clear before fixing the stack frame plus before tracking registers! */
|
|
if (do_opt_final && opt_declear())
|
|
{ XUSE('f'); update_insns(); }
|
|
|
|
if (do_elim_dead_assign) while(opt_elim_dead_assign (STACK_POINTER_REGNUM))
|
|
{
|
|
XUSE('e');
|
|
done = 0;
|
|
update_insns ();
|
|
}
|
|
|
|
if (do_elim_dead_assign) while(opt_elim_dead_assign2 (STACK_POINTER_REGNUM))
|
|
{
|
|
XUSE('E');
|
|
update_insns ();
|
|
}
|
|
|
|
if (do_autoinc && opt_autoinc ())
|
|
{XUSE('i'); done = 0; update_insns (); }
|
|
|
|
if (done)
|
|
break;
|
|
}
|
|
|
|
/* convert back to clear before fixing the stack frame */
|
|
if (do_opt_final && opt_final())
|
|
{ XUSE('z'); update_insns(); }
|
|
|
|
if (do_shrink_stack_frame && opt_shrink_stack_frame ())
|
|
{ XUSE('f'); update_insns (); }
|
|
|
|
/* elim assignments to the stack pointer last. */
|
|
if (do_elim_dead_assign) while(opt_elim_dead_assign (FIRST_PSEUDO_REGISTER))
|
|
{ XUSE('e'); update_insns (); }
|
|
|
|
if (do_elim_dead_assign) while(opt_elim_dead_assign2 (FIRST_PSEUDO_REGISTER))
|
|
{
|
|
XUSE('E');
|
|
update_insns ();
|
|
}
|
|
|
|
if (do_pipeline)
|
|
opt_pipeline_insns ();
|
|
}
|
|
if (r && be_verbose)
|
|
log ("no bbb optimization code %d\n", r);
|
|
|
|
if (dump_reg_track || dump_cycles || be_very_verbose || strchr (string_bbb_opts, 'X') || strchr (string_bbb_opts, 'x'))
|
|
{
|
|
update_insns ();
|
|
if (dump_reg_track)
|
|
track_regs ();
|
|
|
|
if (strchr (string_bbb_opts, 'X') || strchr (string_bbb_opts, 'x'))
|
|
dump_insns ("bbb", strchr (string_bbb_opts, 'X'));
|
|
}
|
|
|
|
if (be_verbose)
|
|
print_inline_info();
|
|
|
|
XUSE('.');
|
|
XUSE('\n');
|
|
|
|
/*
|
|
std::vector<insn_info>().swap(infos);
|
|
std::multimap<int, rtx_insn *>().swap(label2jump);
|
|
std::multimap<unsigned, unsigned>().swap(jump2label);
|
|
std::map<rtx_insn *, insn_info *>().swap(insn2info);
|
|
std::set<unsigned>().swap(scan_starts);
|
|
std::vector<rtx>().swap(dstregs);
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
} // anon namespace
|
|
|
|
rtl_opt_pass *
|
|
make_pass_bbb_optimizations (gcc::context * ctxt)
|
|
{
|
|
return new pass_bbb_optimizations (ctxt);
|
|
}
|
|
|
|
#define TEST(t) {int x = t(); if (!x) printf(#t " failed\n"); r &= x; }
|
|
|
|
static int
|
|
valid_address_1()
|
|
{
|
|
rtx ad = gen_rtx_MEM(SImode, gen_rtx_REG(SImode, 6));
|
|
return !targetm.legitimate_address_p(SImode, ad, true);
|
|
}
|
|
|
|
static int
|
|
detect_cc_use_1()
|
|
{
|
|
rtx gt = gen_rtx_GT(QImode, cc0_rtx, GEN_INT(0));
|
|
|
|
insn_info ii;
|
|
ii.scan_rtx(gt);
|
|
return ii.is_use(FIRST_PSEUDO_REGISTER);
|
|
}
|
|
|
|
int run_tests()
|
|
{
|
|
printf("gcc-test: START\n");
|
|
int r = 1;
|
|
|
|
TEST(valid_address_1);
|
|
TEST(detect_cc_use_1);
|
|
|
|
printf("gcc-test: STOP with rc=%d\n", r);
|
|
exit(!r);
|
|
}
|