/* 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 . */ /** * 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 #include #include #include //#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, ""); 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<= 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< 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<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 & 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 *infos; typedef std::vector::iterator insn_info_iterator; // insn->u2.insn_uid -> rtx_insn * static std::multimap *label2jump; typedef std::multimap::iterator l2j_iterator; // index -> index static std::multimap *jump2label; typedef std::multimap::iterator j2l_iterator; static std::map *insn2info; typedef std::map::iterator i2i_iterator; static std::set *scan_starts; typedef std::set::iterator su_iterator; static insn_info * info0; static unsigned usable_regs; static void update_insn_infos (std::set & 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 & 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 todo; std::set found; std::set 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 positions; std::set regs; // bool ok = true; for (std::set::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::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::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::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::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 reg_reg; std::vector 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::iterator i = reg_reg.begin (); i != reg_reg.end () && i + 1 != reg_reg.end (); ) { bool inc = true; for (std::vector::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 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::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 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 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 regs; std::vector 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::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 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 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::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::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 fixups; // all paths to check std::vector todo; todo.push_back (index + addend); std::set 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::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 ,ax * move (ax),... * into * move (),... * * 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 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(); label2jump = new std::multimap(); jump2label = new std::multimap(); insn2info = new std::map(); scan_starts = new std::set(); } 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().swap(infos); std::multimap().swap(label2jump); std::multimap().swap(jump2label); std::map().swap(insn2info); std::set().swap(scan_starts); std::vector().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); }