aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/sh/sh.cc
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2022-01-14 16:56:44 +0100
committerMartin Liska <mliska@suse.cz>2022-01-17 22:12:04 +0100
commit5c69acb32329d49e58c26fa41ae74229a52b9106 (patch)
treeddb05f9d73afb6f998457d2ac4b720e3b3b60483 /gcc/config/sh/sh.cc
parent490e23032baaece71f2ec09fa1805064b150fbc2 (diff)
downloadgcc-5c69acb32329d49e58c26fa41ae74229a52b9106.zip
gcc-5c69acb32329d49e58c26fa41ae74229a52b9106.tar.gz
gcc-5c69acb32329d49e58c26fa41ae74229a52b9106.tar.bz2
Rename .c files to .cc files.
gcc/ada/ChangeLog: * adadecode.c: Moved to... * adadecode.cc: ...here. * affinity.c: Moved to... * affinity.cc: ...here. * argv-lynxos178-raven-cert.c: Moved to... * argv-lynxos178-raven-cert.cc: ...here. * argv.c: Moved to... * argv.cc: ...here. * aux-io.c: Moved to... * aux-io.cc: ...here. * cio.c: Moved to... * cio.cc: ...here. * cstreams.c: Moved to... * cstreams.cc: ...here. * env.c: Moved to... * env.cc: ...here. * exit.c: Moved to... * exit.cc: ...here. * expect.c: Moved to... * expect.cc: ...here. * final.c: Moved to... * final.cc: ...here. * gcc-interface/cuintp.c: Moved to... * gcc-interface/cuintp.cc: ...here. * gcc-interface/decl.c: Moved to... * gcc-interface/decl.cc: ...here. * gcc-interface/misc.c: Moved to... * gcc-interface/misc.cc: ...here. * gcc-interface/targtyps.c: Moved to... * gcc-interface/targtyps.cc: ...here. * gcc-interface/trans.c: Moved to... * gcc-interface/trans.cc: ...here. * gcc-interface/utils.c: Moved to... * gcc-interface/utils.cc: ...here. * gcc-interface/utils2.c: Moved to... * gcc-interface/utils2.cc: ...here. * init.c: Moved to... * init.cc: ...here. * initialize.c: Moved to... * initialize.cc: ...here. * libgnarl/thread.c: Moved to... * libgnarl/thread.cc: ...here. * link.c: Moved to... * link.cc: ...here. * locales.c: Moved to... * locales.cc: ...here. * mkdir.c: Moved to... * mkdir.cc: ...here. * raise.c: Moved to... * raise.cc: ...here. * rtfinal.c: Moved to... * rtfinal.cc: ...here. * rtinit.c: Moved to... * rtinit.cc: ...here. * seh_init.c: Moved to... * seh_init.cc: ...here. * sigtramp-armdroid.c: Moved to... * sigtramp-armdroid.cc: ...here. * sigtramp-ios.c: Moved to... * sigtramp-ios.cc: ...here. * sigtramp-qnx.c: Moved to... * sigtramp-qnx.cc: ...here. * sigtramp-vxworks.c: Moved to... * sigtramp-vxworks.cc: ...here. * socket.c: Moved to... * socket.cc: ...here. * tracebak.c: Moved to... * tracebak.cc: ...here. * version.c: Moved to... * version.cc: ...here. * vx_stack_info.c: Moved to... * vx_stack_info.cc: ...here. gcc/ChangeLog: * adjust-alignment.c: Moved to... * adjust-alignment.cc: ...here. * alias.c: Moved to... * alias.cc: ...here. * alloc-pool.c: Moved to... * alloc-pool.cc: ...here. * asan.c: Moved to... * asan.cc: ...here. * attribs.c: Moved to... * attribs.cc: ...here. * auto-inc-dec.c: Moved to... * auto-inc-dec.cc: ...here. * auto-profile.c: Moved to... * auto-profile.cc: ...here. * bb-reorder.c: Moved to... * bb-reorder.cc: ...here. * bitmap.c: Moved to... * bitmap.cc: ...here. * btfout.c: Moved to... * btfout.cc: ...here. * builtins.c: Moved to... * builtins.cc: ...here. * caller-save.c: Moved to... * caller-save.cc: ...here. * calls.c: Moved to... * calls.cc: ...here. * ccmp.c: Moved to... * ccmp.cc: ...here. * cfg.c: Moved to... * cfg.cc: ...here. * cfganal.c: Moved to... * cfganal.cc: ...here. * cfgbuild.c: Moved to... * cfgbuild.cc: ...here. * cfgcleanup.c: Moved to... * cfgcleanup.cc: ...here. * cfgexpand.c: Moved to... * cfgexpand.cc: ...here. * cfghooks.c: Moved to... * cfghooks.cc: ...here. * cfgloop.c: Moved to... * cfgloop.cc: ...here. * cfgloopanal.c: Moved to... * cfgloopanal.cc: ...here. * cfgloopmanip.c: Moved to... * cfgloopmanip.cc: ...here. * cfgrtl.c: Moved to... * cfgrtl.cc: ...here. * cgraph.c: Moved to... * cgraph.cc: ...here. * cgraphbuild.c: Moved to... * cgraphbuild.cc: ...here. * cgraphclones.c: Moved to... * cgraphclones.cc: ...here. * cgraphunit.c: Moved to... * cgraphunit.cc: ...here. * collect-utils.c: Moved to... * collect-utils.cc: ...here. * collect2-aix.c: Moved to... * collect2-aix.cc: ...here. * collect2.c: Moved to... * collect2.cc: ...here. * combine-stack-adj.c: Moved to... * combine-stack-adj.cc: ...here. * combine.c: Moved to... * combine.cc: ...here. * common/common-targhooks.c: Moved to... * common/common-targhooks.cc: ...here. * common/config/aarch64/aarch64-common.c: Moved to... * common/config/aarch64/aarch64-common.cc: ...here. * common/config/alpha/alpha-common.c: Moved to... * common/config/alpha/alpha-common.cc: ...here. * common/config/arc/arc-common.c: Moved to... * common/config/arc/arc-common.cc: ...here. * common/config/arm/arm-common.c: Moved to... * common/config/arm/arm-common.cc: ...here. * common/config/avr/avr-common.c: Moved to... * common/config/avr/avr-common.cc: ...here. * common/config/bfin/bfin-common.c: Moved to... * common/config/bfin/bfin-common.cc: ...here. * common/config/bpf/bpf-common.c: Moved to... * common/config/bpf/bpf-common.cc: ...here. * common/config/c6x/c6x-common.c: Moved to... * common/config/c6x/c6x-common.cc: ...here. * common/config/cr16/cr16-common.c: Moved to... * common/config/cr16/cr16-common.cc: ...here. * common/config/cris/cris-common.c: Moved to... * common/config/cris/cris-common.cc: ...here. * common/config/csky/csky-common.c: Moved to... * common/config/csky/csky-common.cc: ...here. * common/config/default-common.c: Moved to... * common/config/default-common.cc: ...here. * common/config/epiphany/epiphany-common.c: Moved to... * common/config/epiphany/epiphany-common.cc: ...here. * common/config/fr30/fr30-common.c: Moved to... * common/config/fr30/fr30-common.cc: ...here. * common/config/frv/frv-common.c: Moved to... * common/config/frv/frv-common.cc: ...here. * common/config/gcn/gcn-common.c: Moved to... * common/config/gcn/gcn-common.cc: ...here. * common/config/h8300/h8300-common.c: Moved to... * common/config/h8300/h8300-common.cc: ...here. * common/config/i386/i386-common.c: Moved to... * common/config/i386/i386-common.cc: ...here. * common/config/ia64/ia64-common.c: Moved to... * common/config/ia64/ia64-common.cc: ...here. * common/config/iq2000/iq2000-common.c: Moved to... * common/config/iq2000/iq2000-common.cc: ...here. * common/config/lm32/lm32-common.c: Moved to... * common/config/lm32/lm32-common.cc: ...here. * common/config/m32r/m32r-common.c: Moved to... * common/config/m32r/m32r-common.cc: ...here. * common/config/m68k/m68k-common.c: Moved to... * common/config/m68k/m68k-common.cc: ...here. * common/config/mcore/mcore-common.c: Moved to... * common/config/mcore/mcore-common.cc: ...here. * common/config/microblaze/microblaze-common.c: Moved to... * common/config/microblaze/microblaze-common.cc: ...here. * common/config/mips/mips-common.c: Moved to... * common/config/mips/mips-common.cc: ...here. * common/config/mmix/mmix-common.c: Moved to... * common/config/mmix/mmix-common.cc: ...here. * common/config/mn10300/mn10300-common.c: Moved to... * common/config/mn10300/mn10300-common.cc: ...here. * common/config/msp430/msp430-common.c: Moved to... * common/config/msp430/msp430-common.cc: ...here. * common/config/nds32/nds32-common.c: Moved to... * common/config/nds32/nds32-common.cc: ...here. * common/config/nios2/nios2-common.c: Moved to... * common/config/nios2/nios2-common.cc: ...here. * common/config/nvptx/nvptx-common.c: Moved to... * common/config/nvptx/nvptx-common.cc: ...here. * common/config/or1k/or1k-common.c: Moved to... * common/config/or1k/or1k-common.cc: ...here. * common/config/pa/pa-common.c: Moved to... * common/config/pa/pa-common.cc: ...here. * common/config/pdp11/pdp11-common.c: Moved to... * common/config/pdp11/pdp11-common.cc: ...here. * common/config/pru/pru-common.c: Moved to... * common/config/pru/pru-common.cc: ...here. * common/config/riscv/riscv-common.c: Moved to... * common/config/riscv/riscv-common.cc: ...here. * common/config/rs6000/rs6000-common.c: Moved to... * common/config/rs6000/rs6000-common.cc: ...here. * common/config/rx/rx-common.c: Moved to... * common/config/rx/rx-common.cc: ...here. * common/config/s390/s390-common.c: Moved to... * common/config/s390/s390-common.cc: ...here. * common/config/sh/sh-common.c: Moved to... * common/config/sh/sh-common.cc: ...here. * common/config/sparc/sparc-common.c: Moved to... * common/config/sparc/sparc-common.cc: ...here. * common/config/tilegx/tilegx-common.c: Moved to... * common/config/tilegx/tilegx-common.cc: ...here. * common/config/tilepro/tilepro-common.c: Moved to... * common/config/tilepro/tilepro-common.cc: ...here. * common/config/v850/v850-common.c: Moved to... * common/config/v850/v850-common.cc: ...here. * common/config/vax/vax-common.c: Moved to... * common/config/vax/vax-common.cc: ...here. * common/config/visium/visium-common.c: Moved to... * common/config/visium/visium-common.cc: ...here. * common/config/xstormy16/xstormy16-common.c: Moved to... * common/config/xstormy16/xstormy16-common.cc: ...here. * common/config/xtensa/xtensa-common.c: Moved to... * common/config/xtensa/xtensa-common.cc: ...here. * compare-elim.c: Moved to... * compare-elim.cc: ...here. * config/aarch64/aarch64-bti-insert.c: Moved to... * config/aarch64/aarch64-bti-insert.cc: ...here. * config/aarch64/aarch64-builtins.c: Moved to... * config/aarch64/aarch64-builtins.cc: ...here. * config/aarch64/aarch64-c.c: Moved to... * config/aarch64/aarch64-c.cc: ...here. * config/aarch64/aarch64-d.c: Moved to... * config/aarch64/aarch64-d.cc: ...here. * config/aarch64/aarch64.c: Moved to... * config/aarch64/aarch64.cc: ...here. * config/aarch64/cortex-a57-fma-steering.c: Moved to... * config/aarch64/cortex-a57-fma-steering.cc: ...here. * config/aarch64/driver-aarch64.c: Moved to... * config/aarch64/driver-aarch64.cc: ...here. * config/aarch64/falkor-tag-collision-avoidance.c: Moved to... * config/aarch64/falkor-tag-collision-avoidance.cc: ...here. * config/aarch64/host-aarch64-darwin.c: Moved to... * config/aarch64/host-aarch64-darwin.cc: ...here. * config/alpha/alpha.c: Moved to... * config/alpha/alpha.cc: ...here. * config/alpha/driver-alpha.c: Moved to... * config/alpha/driver-alpha.cc: ...here. * config/arc/arc-c.c: Moved to... * config/arc/arc-c.cc: ...here. * config/arc/arc.c: Moved to... * config/arc/arc.cc: ...here. * config/arc/driver-arc.c: Moved to... * config/arc/driver-arc.cc: ...here. * config/arm/aarch-common.c: Moved to... * config/arm/aarch-common.cc: ...here. * config/arm/arm-builtins.c: Moved to... * config/arm/arm-builtins.cc: ...here. * config/arm/arm-c.c: Moved to... * config/arm/arm-c.cc: ...here. * config/arm/arm-d.c: Moved to... * config/arm/arm-d.cc: ...here. * config/arm/arm.c: Moved to... * config/arm/arm.cc: ...here. * config/arm/driver-arm.c: Moved to... * config/arm/driver-arm.cc: ...here. * config/avr/avr-c.c: Moved to... * config/avr/avr-c.cc: ...here. * config/avr/avr-devices.c: Moved to... * config/avr/avr-devices.cc: ...here. * config/avr/avr-log.c: Moved to... * config/avr/avr-log.cc: ...here. * config/avr/avr.c: Moved to... * config/avr/avr.cc: ...here. * config/avr/driver-avr.c: Moved to... * config/avr/driver-avr.cc: ...here. * config/avr/gen-avr-mmcu-specs.c: Moved to... * config/avr/gen-avr-mmcu-specs.cc: ...here. * config/avr/gen-avr-mmcu-texi.c: Moved to... * config/avr/gen-avr-mmcu-texi.cc: ...here. * config/bfin/bfin.c: Moved to... * config/bfin/bfin.cc: ...here. * config/bpf/bpf.c: Moved to... * config/bpf/bpf.cc: ...here. * config/bpf/coreout.c: Moved to... * config/bpf/coreout.cc: ...here. * config/c6x/c6x.c: Moved to... * config/c6x/c6x.cc: ...here. * config/cr16/cr16.c: Moved to... * config/cr16/cr16.cc: ...here. * config/cris/cris.c: Moved to... * config/cris/cris.cc: ...here. * config/csky/csky.c: Moved to... * config/csky/csky.cc: ...here. * config/darwin-c.c: Moved to... * config/darwin-c.cc: ...here. * config/darwin-d.c: Moved to... * config/darwin-d.cc: ...here. * config/darwin-driver.c: Moved to... * config/darwin-driver.cc: ...here. * config/darwin-f.c: Moved to... * config/darwin-f.cc: ...here. * config/darwin.c: Moved to... * config/darwin.cc: ...here. * config/default-c.c: Moved to... * config/default-c.cc: ...here. * config/default-d.c: Moved to... * config/default-d.cc: ...here. * config/dragonfly-d.c: Moved to... * config/dragonfly-d.cc: ...here. * config/epiphany/epiphany.c: Moved to... * config/epiphany/epiphany.cc: ...here. * config/epiphany/mode-switch-use.c: Moved to... * config/epiphany/mode-switch-use.cc: ...here. * config/epiphany/resolve-sw-modes.c: Moved to... * config/epiphany/resolve-sw-modes.cc: ...here. * config/fr30/fr30.c: Moved to... * config/fr30/fr30.cc: ...here. * config/freebsd-d.c: Moved to... * config/freebsd-d.cc: ...here. * config/frv/frv.c: Moved to... * config/frv/frv.cc: ...here. * config/ft32/ft32.c: Moved to... * config/ft32/ft32.cc: ...here. * config/gcn/driver-gcn.c: Moved to... * config/gcn/driver-gcn.cc: ...here. * config/gcn/gcn-run.c: Moved to... * config/gcn/gcn-run.cc: ...here. * config/gcn/gcn-tree.c: Moved to... * config/gcn/gcn-tree.cc: ...here. * config/gcn/gcn.c: Moved to... * config/gcn/gcn.cc: ...here. * config/gcn/mkoffload.c: Moved to... * config/gcn/mkoffload.cc: ...here. * config/glibc-c.c: Moved to... * config/glibc-c.cc: ...here. * config/glibc-d.c: Moved to... * config/glibc-d.cc: ...here. * config/h8300/h8300.c: Moved to... * config/h8300/h8300.cc: ...here. * config/host-darwin.c: Moved to... * config/host-darwin.cc: ...here. * config/host-hpux.c: Moved to... * config/host-hpux.cc: ...here. * config/host-linux.c: Moved to... * config/host-linux.cc: ...here. * config/host-netbsd.c: Moved to... * config/host-netbsd.cc: ...here. * config/host-openbsd.c: Moved to... * config/host-openbsd.cc: ...here. * config/host-solaris.c: Moved to... * config/host-solaris.cc: ...here. * config/i386/djgpp.c: Moved to... * config/i386/djgpp.cc: ...here. * config/i386/driver-i386.c: Moved to... * config/i386/driver-i386.cc: ...here. * config/i386/driver-mingw32.c: Moved to... * config/i386/driver-mingw32.cc: ...here. * config/i386/gnu-property.c: Moved to... * config/i386/gnu-property.cc: ...here. * config/i386/host-cygwin.c: Moved to... * config/i386/host-cygwin.cc: ...here. * config/i386/host-i386-darwin.c: Moved to... * config/i386/host-i386-darwin.cc: ...here. * config/i386/host-mingw32.c: Moved to... * config/i386/host-mingw32.cc: ...here. * config/i386/i386-builtins.c: Moved to... * config/i386/i386-builtins.cc: ...here. * config/i386/i386-c.c: Moved to... * config/i386/i386-c.cc: ...here. * config/i386/i386-d.c: Moved to... * config/i386/i386-d.cc: ...here. * config/i386/i386-expand.c: Moved to... * config/i386/i386-expand.cc: ...here. * config/i386/i386-features.c: Moved to... * config/i386/i386-features.cc: ...here. * config/i386/i386-options.c: Moved to... * config/i386/i386-options.cc: ...here. * config/i386/i386.c: Moved to... * config/i386/i386.cc: ...here. * config/i386/intelmic-mkoffload.c: Moved to... * config/i386/intelmic-mkoffload.cc: ...here. * config/i386/msformat-c.c: Moved to... * config/i386/msformat-c.cc: ...here. * config/i386/winnt-cxx.c: Moved to... * config/i386/winnt-cxx.cc: ...here. * config/i386/winnt-d.c: Moved to... * config/i386/winnt-d.cc: ...here. * config/i386/winnt-stubs.c: Moved to... * config/i386/winnt-stubs.cc: ...here. * config/i386/winnt.c: Moved to... * config/i386/winnt.cc: ...here. * config/i386/x86-tune-sched-atom.c: Moved to... * config/i386/x86-tune-sched-atom.cc: ...here. * config/i386/x86-tune-sched-bd.c: Moved to... * config/i386/x86-tune-sched-bd.cc: ...here. * config/i386/x86-tune-sched-core.c: Moved to... * config/i386/x86-tune-sched-core.cc: ...here. * config/i386/x86-tune-sched.c: Moved to... * config/i386/x86-tune-sched.cc: ...here. * config/ia64/ia64-c.c: Moved to... * config/ia64/ia64-c.cc: ...here. * config/ia64/ia64.c: Moved to... * config/ia64/ia64.cc: ...here. * config/iq2000/iq2000.c: Moved to... * config/iq2000/iq2000.cc: ...here. * config/linux.c: Moved to... * config/linux.cc: ...here. * config/lm32/lm32.c: Moved to... * config/lm32/lm32.cc: ...here. * config/m32c/m32c-pragma.c: Moved to... * config/m32c/m32c-pragma.cc: ...here. * config/m32c/m32c.c: Moved to... * config/m32c/m32c.cc: ...here. * config/m32r/m32r.c: Moved to... * config/m32r/m32r.cc: ...here. * config/m68k/m68k.c: Moved to... * config/m68k/m68k.cc: ...here. * config/mcore/mcore.c: Moved to... * config/mcore/mcore.cc: ...here. * config/microblaze/microblaze-c.c: Moved to... * config/microblaze/microblaze-c.cc: ...here. * config/microblaze/microblaze.c: Moved to... * config/microblaze/microblaze.cc: ...here. * config/mips/driver-native.c: Moved to... * config/mips/driver-native.cc: ...here. * config/mips/frame-header-opt.c: Moved to... * config/mips/frame-header-opt.cc: ...here. * config/mips/mips-d.c: Moved to... * config/mips/mips-d.cc: ...here. * config/mips/mips.c: Moved to... * config/mips/mips.cc: ...here. * config/mmix/mmix.c: Moved to... * config/mmix/mmix.cc: ...here. * config/mn10300/mn10300.c: Moved to... * config/mn10300/mn10300.cc: ...here. * config/moxie/moxie.c: Moved to... * config/moxie/moxie.cc: ...here. * config/msp430/driver-msp430.c: Moved to... * config/msp430/driver-msp430.cc: ...here. * config/msp430/msp430-c.c: Moved to... * config/msp430/msp430-c.cc: ...here. * config/msp430/msp430-devices.c: Moved to... * config/msp430/msp430-devices.cc: ...here. * config/msp430/msp430.c: Moved to... * config/msp430/msp430.cc: ...here. * config/nds32/nds32-cost.c: Moved to... * config/nds32/nds32-cost.cc: ...here. * config/nds32/nds32-fp-as-gp.c: Moved to... * config/nds32/nds32-fp-as-gp.cc: ...here. * config/nds32/nds32-intrinsic.c: Moved to... * config/nds32/nds32-intrinsic.cc: ...here. * config/nds32/nds32-isr.c: Moved to... * config/nds32/nds32-isr.cc: ...here. * config/nds32/nds32-md-auxiliary.c: Moved to... * config/nds32/nds32-md-auxiliary.cc: ...here. * config/nds32/nds32-memory-manipulation.c: Moved to... * config/nds32/nds32-memory-manipulation.cc: ...here. * config/nds32/nds32-pipelines-auxiliary.c: Moved to... * config/nds32/nds32-pipelines-auxiliary.cc: ...here. * config/nds32/nds32-predicates.c: Moved to... * config/nds32/nds32-predicates.cc: ...here. * config/nds32/nds32-relax-opt.c: Moved to... * config/nds32/nds32-relax-opt.cc: ...here. * config/nds32/nds32-utils.c: Moved to... * config/nds32/nds32-utils.cc: ...here. * config/nds32/nds32.c: Moved to... * config/nds32/nds32.cc: ...here. * config/netbsd-d.c: Moved to... * config/netbsd-d.cc: ...here. * config/netbsd.c: Moved to... * config/netbsd.cc: ...here. * config/nios2/nios2.c: Moved to... * config/nios2/nios2.cc: ...here. * config/nvptx/mkoffload.c: Moved to... * config/nvptx/mkoffload.cc: ...here. * config/nvptx/nvptx-c.c: Moved to... * config/nvptx/nvptx-c.cc: ...here. * config/nvptx/nvptx.c: Moved to... * config/nvptx/nvptx.cc: ...here. * config/openbsd-d.c: Moved to... * config/openbsd-d.cc: ...here. * config/or1k/or1k.c: Moved to... * config/or1k/or1k.cc: ...here. * config/pa/pa-d.c: Moved to... * config/pa/pa-d.cc: ...here. * config/pa/pa.c: Moved to... * config/pa/pa.cc: ...here. * config/pdp11/pdp11.c: Moved to... * config/pdp11/pdp11.cc: ...here. * config/pru/pru-passes.c: Moved to... * config/pru/pru-passes.cc: ...here. * config/pru/pru-pragma.c: Moved to... * config/pru/pru-pragma.cc: ...here. * config/pru/pru.c: Moved to... * config/pru/pru.cc: ...here. * config/riscv/riscv-builtins.c: Moved to... * config/riscv/riscv-builtins.cc: ...here. * config/riscv/riscv-c.c: Moved to... * config/riscv/riscv-c.cc: ...here. * config/riscv/riscv-d.c: Moved to... * config/riscv/riscv-d.cc: ...here. * config/riscv/riscv-shorten-memrefs.c: Moved to... * config/riscv/riscv-shorten-memrefs.cc: ...here. * config/riscv/riscv-sr.c: Moved to... * config/riscv/riscv-sr.cc: ...here. * config/riscv/riscv.c: Moved to... * config/riscv/riscv.cc: ...here. * config/rl78/rl78-c.c: Moved to... * config/rl78/rl78-c.cc: ...here. * config/rl78/rl78.c: Moved to... * config/rl78/rl78.cc: ...here. * config/rs6000/driver-rs6000.c: Moved to... * config/rs6000/driver-rs6000.cc: ...here. * config/rs6000/host-darwin.c: Moved to... * config/rs6000/host-darwin.cc: ...here. * config/rs6000/host-ppc64-darwin.c: Moved to... * config/rs6000/host-ppc64-darwin.cc: ...here. * config/rs6000/rbtree.c: Moved to... * config/rs6000/rbtree.cc: ...here. * config/rs6000/rs6000-c.c: Moved to... * config/rs6000/rs6000-c.cc: ...here. * config/rs6000/rs6000-call.c: Moved to... * config/rs6000/rs6000-call.cc: ...here. * config/rs6000/rs6000-d.c: Moved to... * config/rs6000/rs6000-d.cc: ...here. * config/rs6000/rs6000-gen-builtins.c: Moved to... * config/rs6000/rs6000-gen-builtins.cc: ...here. * config/rs6000/rs6000-linux.c: Moved to... * config/rs6000/rs6000-linux.cc: ...here. * config/rs6000/rs6000-logue.c: Moved to... * config/rs6000/rs6000-logue.cc: ...here. * config/rs6000/rs6000-p8swap.c: Moved to... * config/rs6000/rs6000-p8swap.cc: ...here. * config/rs6000/rs6000-pcrel-opt.c: Moved to... * config/rs6000/rs6000-pcrel-opt.cc: ...here. * config/rs6000/rs6000-string.c: Moved to... * config/rs6000/rs6000-string.cc: ...here. * config/rs6000/rs6000.c: Moved to... * config/rs6000/rs6000.cc: ...here. * config/rx/rx.c: Moved to... * config/rx/rx.cc: ...here. * config/s390/driver-native.c: Moved to... * config/s390/driver-native.cc: ...here. * config/s390/s390-c.c: Moved to... * config/s390/s390-c.cc: ...here. * config/s390/s390-d.c: Moved to... * config/s390/s390-d.cc: ...here. * config/s390/s390.c: Moved to... * config/s390/s390.cc: ...here. * config/sh/divtab-sh4-300.c: Moved to... * config/sh/divtab-sh4-300.cc: ...here. * config/sh/divtab-sh4.c: Moved to... * config/sh/divtab-sh4.cc: ...here. * config/sh/divtab.c: Moved to... * config/sh/divtab.cc: ...here. * config/sh/sh-c.c: Moved to... * config/sh/sh-c.cc: ...here. * config/sh/sh.c: Moved to... * config/sh/sh.cc: ...here. * config/sol2-c.c: Moved to... * config/sol2-c.cc: ...here. * config/sol2-cxx.c: Moved to... * config/sol2-cxx.cc: ...here. * config/sol2-d.c: Moved to... * config/sol2-d.cc: ...here. * config/sol2-stubs.c: Moved to... * config/sol2-stubs.cc: ...here. * config/sol2.c: Moved to... * config/sol2.cc: ...here. * config/sparc/driver-sparc.c: Moved to... * config/sparc/driver-sparc.cc: ...here. * config/sparc/sparc-c.c: Moved to... * config/sparc/sparc-c.cc: ...here. * config/sparc/sparc-d.c: Moved to... * config/sparc/sparc-d.cc: ...here. * config/sparc/sparc.c: Moved to... * config/sparc/sparc.cc: ...here. * config/stormy16/stormy16.c: Moved to... * config/stormy16/stormy16.cc: ...here. * config/tilegx/mul-tables.c: Moved to... * config/tilegx/mul-tables.cc: ...here. * config/tilegx/tilegx-c.c: Moved to... * config/tilegx/tilegx-c.cc: ...here. * config/tilegx/tilegx.c: Moved to... * config/tilegx/tilegx.cc: ...here. * config/tilepro/mul-tables.c: Moved to... * config/tilepro/mul-tables.cc: ...here. * config/tilepro/tilepro-c.c: Moved to... * config/tilepro/tilepro-c.cc: ...here. * config/tilepro/tilepro.c: Moved to... * config/tilepro/tilepro.cc: ...here. * config/v850/v850-c.c: Moved to... * config/v850/v850-c.cc: ...here. * config/v850/v850.c: Moved to... * config/v850/v850.cc: ...here. * config/vax/vax.c: Moved to... * config/vax/vax.cc: ...here. * config/visium/visium.c: Moved to... * config/visium/visium.cc: ...here. * config/vms/vms-c.c: Moved to... * config/vms/vms-c.cc: ...here. * config/vms/vms-f.c: Moved to... * config/vms/vms-f.cc: ...here. * config/vms/vms.c: Moved to... * config/vms/vms.cc: ...here. * config/vxworks-c.c: Moved to... * config/vxworks-c.cc: ...here. * config/vxworks.c: Moved to... * config/vxworks.cc: ...here. * config/winnt-c.c: Moved to... * config/winnt-c.cc: ...here. * config/xtensa/xtensa.c: Moved to... * config/xtensa/xtensa.cc: ...here. * context.c: Moved to... * context.cc: ...here. * convert.c: Moved to... * convert.cc: ...here. * coverage.c: Moved to... * coverage.cc: ...here. * cppbuiltin.c: Moved to... * cppbuiltin.cc: ...here. * cppdefault.c: Moved to... * cppdefault.cc: ...here. * cprop.c: Moved to... * cprop.cc: ...here. * cse.c: Moved to... * cse.cc: ...here. * cselib.c: Moved to... * cselib.cc: ...here. * ctfc.c: Moved to... * ctfc.cc: ...here. * ctfout.c: Moved to... * ctfout.cc: ...here. * data-streamer-in.c: Moved to... * data-streamer-in.cc: ...here. * data-streamer-out.c: Moved to... * data-streamer-out.cc: ...here. * data-streamer.c: Moved to... * data-streamer.cc: ...here. * dbgcnt.c: Moved to... * dbgcnt.cc: ...here. * dbxout.c: Moved to... * dbxout.cc: ...here. * dce.c: Moved to... * dce.cc: ...here. * ddg.c: Moved to... * ddg.cc: ...here. * debug.c: Moved to... * debug.cc: ...here. * df-core.c: Moved to... * df-core.cc: ...here. * df-problems.c: Moved to... * df-problems.cc: ...here. * df-scan.c: Moved to... * df-scan.cc: ...here. * dfp.c: Moved to... * dfp.cc: ...here. * diagnostic-color.c: Moved to... * diagnostic-color.cc: ...here. * diagnostic-show-locus.c: Moved to... * diagnostic-show-locus.cc: ...here. * diagnostic-spec.c: Moved to... * diagnostic-spec.cc: ...here. * diagnostic.c: Moved to... * diagnostic.cc: ...here. * dojump.c: Moved to... * dojump.cc: ...here. * dominance.c: Moved to... * dominance.cc: ...here. * domwalk.c: Moved to... * domwalk.cc: ...here. * double-int.c: Moved to... * double-int.cc: ...here. * dse.c: Moved to... * dse.cc: ...here. * dumpfile.c: Moved to... * dumpfile.cc: ...here. * dwarf2asm.c: Moved to... * dwarf2asm.cc: ...here. * dwarf2cfi.c: Moved to... * dwarf2cfi.cc: ...here. * dwarf2ctf.c: Moved to... * dwarf2ctf.cc: ...here. * dwarf2out.c: Moved to... * dwarf2out.cc: ...here. * early-remat.c: Moved to... * early-remat.cc: ...here. * edit-context.c: Moved to... * edit-context.cc: ...here. * emit-rtl.c: Moved to... * emit-rtl.cc: ...here. * errors.c: Moved to... * errors.cc: ...here. * et-forest.c: Moved to... * et-forest.cc: ...here. * except.c: Moved to... * except.cc: ...here. * explow.c: Moved to... * explow.cc: ...here. * expmed.c: Moved to... * expmed.cc: ...here. * expr.c: Moved to... * expr.cc: ...here. * fibonacci_heap.c: Moved to... * fibonacci_heap.cc: ...here. * file-find.c: Moved to... * file-find.cc: ...here. * file-prefix-map.c: Moved to... * file-prefix-map.cc: ...here. * final.c: Moved to... * final.cc: ...here. * fixed-value.c: Moved to... * fixed-value.cc: ...here. * fold-const-call.c: Moved to... * fold-const-call.cc: ...here. * fold-const.c: Moved to... * fold-const.cc: ...here. * fp-test.c: Moved to... * fp-test.cc: ...here. * function-tests.c: Moved to... * function-tests.cc: ...here. * function.c: Moved to... * function.cc: ...here. * fwprop.c: Moved to... * fwprop.cc: ...here. * gcc-ar.c: Moved to... * gcc-ar.cc: ...here. * gcc-main.c: Moved to... * gcc-main.cc: ...here. * gcc-rich-location.c: Moved to... * gcc-rich-location.cc: ...here. * gcc.c: Moved to... * gcc.cc: ...here. * gcov-dump.c: Moved to... * gcov-dump.cc: ...here. * gcov-io.c: Moved to... * gcov-io.cc: ...here. * gcov-tool.c: Moved to... * gcov-tool.cc: ...here. * gcov.c: Moved to... * gcov.cc: ...here. * gcse-common.c: Moved to... * gcse-common.cc: ...here. * gcse.c: Moved to... * gcse.cc: ...here. * genattr-common.c: Moved to... * genattr-common.cc: ...here. * genattr.c: Moved to... * genattr.cc: ...here. * genattrtab.c: Moved to... * genattrtab.cc: ...here. * genautomata.c: Moved to... * genautomata.cc: ...here. * gencfn-macros.c: Moved to... * gencfn-macros.cc: ...here. * gencheck.c: Moved to... * gencheck.cc: ...here. * genchecksum.c: Moved to... * genchecksum.cc: ...here. * gencodes.c: Moved to... * gencodes.cc: ...here. * genconditions.c: Moved to... * genconditions.cc: ...here. * genconfig.c: Moved to... * genconfig.cc: ...here. * genconstants.c: Moved to... * genconstants.cc: ...here. * genemit.c: Moved to... * genemit.cc: ...here. * genenums.c: Moved to... * genenums.cc: ...here. * generic-match-head.c: Moved to... * generic-match-head.cc: ...here. * genextract.c: Moved to... * genextract.cc: ...here. * genflags.c: Moved to... * genflags.cc: ...here. * gengenrtl.c: Moved to... * gengenrtl.cc: ...here. * gengtype-parse.c: Moved to... * gengtype-parse.cc: ...here. * gengtype-state.c: Moved to... * gengtype-state.cc: ...here. * gengtype.c: Moved to... * gengtype.cc: ...here. * genhooks.c: Moved to... * genhooks.cc: ...here. * genmatch.c: Moved to... * genmatch.cc: ...here. * genmddeps.c: Moved to... * genmddeps.cc: ...here. * genmddump.c: Moved to... * genmddump.cc: ...here. * genmodes.c: Moved to... * genmodes.cc: ...here. * genopinit.c: Moved to... * genopinit.cc: ...here. * genoutput.c: Moved to... * genoutput.cc: ...here. * genpeep.c: Moved to... * genpeep.cc: ...here. * genpreds.c: Moved to... * genpreds.cc: ...here. * genrecog.c: Moved to... * genrecog.cc: ...here. * gensupport.c: Moved to... * gensupport.cc: ...here. * gentarget-def.c: Moved to... * gentarget-def.cc: ...here. * genversion.c: Moved to... * genversion.cc: ...here. * ggc-common.c: Moved to... * ggc-common.cc: ...here. * ggc-none.c: Moved to... * ggc-none.cc: ...here. * ggc-page.c: Moved to... * ggc-page.cc: ...here. * ggc-tests.c: Moved to... * ggc-tests.cc: ...here. * gimple-builder.c: Moved to... * gimple-builder.cc: ...here. * gimple-expr.c: Moved to... * gimple-expr.cc: ...here. * gimple-fold.c: Moved to... * gimple-fold.cc: ...here. * gimple-iterator.c: Moved to... * gimple-iterator.cc: ...here. * gimple-laddress.c: Moved to... * gimple-laddress.cc: ...here. * gimple-loop-jam.c: Moved to... * gimple-loop-jam.cc: ...here. * gimple-low.c: Moved to... * gimple-low.cc: ...here. * gimple-match-head.c: Moved to... * gimple-match-head.cc: ...here. * gimple-pretty-print.c: Moved to... * gimple-pretty-print.cc: ...here. * gimple-ssa-backprop.c: Moved to... * gimple-ssa-backprop.cc: ...here. * gimple-ssa-evrp-analyze.c: Moved to... * gimple-ssa-evrp-analyze.cc: ...here. * gimple-ssa-evrp.c: Moved to... * gimple-ssa-evrp.cc: ...here. * gimple-ssa-isolate-paths.c: Moved to... * gimple-ssa-isolate-paths.cc: ...here. * gimple-ssa-nonnull-compare.c: Moved to... * gimple-ssa-nonnull-compare.cc: ...here. * gimple-ssa-split-paths.c: Moved to... * gimple-ssa-split-paths.cc: ...here. * gimple-ssa-sprintf.c: Moved to... * gimple-ssa-sprintf.cc: ...here. * gimple-ssa-store-merging.c: Moved to... * gimple-ssa-store-merging.cc: ...here. * gimple-ssa-strength-reduction.c: Moved to... * gimple-ssa-strength-reduction.cc: ...here. * gimple-ssa-warn-alloca.c: Moved to... * gimple-ssa-warn-alloca.cc: ...here. * gimple-ssa-warn-restrict.c: Moved to... * gimple-ssa-warn-restrict.cc: ...here. * gimple-streamer-in.c: Moved to... * gimple-streamer-in.cc: ...here. * gimple-streamer-out.c: Moved to... * gimple-streamer-out.cc: ...here. * gimple-walk.c: Moved to... * gimple-walk.cc: ...here. * gimple-warn-recursion.c: Moved to... * gimple-warn-recursion.cc: ...here. * gimple.c: Moved to... * gimple.cc: ...here. * gimplify-me.c: Moved to... * gimplify-me.cc: ...here. * gimplify.c: Moved to... * gimplify.cc: ...here. * godump.c: Moved to... * godump.cc: ...here. * graph.c: Moved to... * graph.cc: ...here. * graphds.c: Moved to... * graphds.cc: ...here. * graphite-dependences.c: Moved to... * graphite-dependences.cc: ...here. * graphite-isl-ast-to-gimple.c: Moved to... * graphite-isl-ast-to-gimple.cc: ...here. * graphite-optimize-isl.c: Moved to... * graphite-optimize-isl.cc: ...here. * graphite-poly.c: Moved to... * graphite-poly.cc: ...here. * graphite-scop-detection.c: Moved to... * graphite-scop-detection.cc: ...here. * graphite-sese-to-poly.c: Moved to... * graphite-sese-to-poly.cc: ...here. * graphite.c: Moved to... * graphite.cc: ...here. * haifa-sched.c: Moved to... * haifa-sched.cc: ...here. * hash-map-tests.c: Moved to... * hash-map-tests.cc: ...here. * hash-set-tests.c: Moved to... * hash-set-tests.cc: ...here. * hash-table.c: Moved to... * hash-table.cc: ...here. * hooks.c: Moved to... * hooks.cc: ...here. * host-default.c: Moved to... * host-default.cc: ...here. * hw-doloop.c: Moved to... * hw-doloop.cc: ...here. * hwint.c: Moved to... * hwint.cc: ...here. * ifcvt.c: Moved to... * ifcvt.cc: ...here. * inchash.c: Moved to... * inchash.cc: ...here. * incpath.c: Moved to... * incpath.cc: ...here. * init-regs.c: Moved to... * init-regs.cc: ...here. * input.c: Moved to... * input.cc: ...here. * internal-fn.c: Moved to... * internal-fn.cc: ...here. * intl.c: Moved to... * intl.cc: ...here. * ipa-comdats.c: Moved to... * ipa-comdats.cc: ...here. * ipa-cp.c: Moved to... * ipa-cp.cc: ...here. * ipa-devirt.c: Moved to... * ipa-devirt.cc: ...here. * ipa-fnsummary.c: Moved to... * ipa-fnsummary.cc: ...here. * ipa-icf-gimple.c: Moved to... * ipa-icf-gimple.cc: ...here. * ipa-icf.c: Moved to... * ipa-icf.cc: ...here. * ipa-inline-analysis.c: Moved to... * ipa-inline-analysis.cc: ...here. * ipa-inline-transform.c: Moved to... * ipa-inline-transform.cc: ...here. * ipa-inline.c: Moved to... * ipa-inline.cc: ...here. * ipa-modref-tree.c: Moved to... * ipa-modref-tree.cc: ...here. * ipa-modref.c: Moved to... * ipa-modref.cc: ...here. * ipa-param-manipulation.c: Moved to... * ipa-param-manipulation.cc: ...here. * ipa-polymorphic-call.c: Moved to... * ipa-polymorphic-call.cc: ...here. * ipa-predicate.c: Moved to... * ipa-predicate.cc: ...here. * ipa-profile.c: Moved to... * ipa-profile.cc: ...here. * ipa-prop.c: Moved to... * ipa-prop.cc: ...here. * ipa-pure-const.c: Moved to... * ipa-pure-const.cc: ...here. * ipa-ref.c: Moved to... * ipa-ref.cc: ...here. * ipa-reference.c: Moved to... * ipa-reference.cc: ...here. * ipa-split.c: Moved to... * ipa-split.cc: ...here. * ipa-sra.c: Moved to... * ipa-sra.cc: ...here. * ipa-utils.c: Moved to... * ipa-utils.cc: ...here. * ipa-visibility.c: Moved to... * ipa-visibility.cc: ...here. * ipa.c: Moved to... * ipa.cc: ...here. * ira-build.c: Moved to... * ira-build.cc: ...here. * ira-color.c: Moved to... * ira-color.cc: ...here. * ira-conflicts.c: Moved to... * ira-conflicts.cc: ...here. * ira-costs.c: Moved to... * ira-costs.cc: ...here. * ira-emit.c: Moved to... * ira-emit.cc: ...here. * ira-lives.c: Moved to... * ira-lives.cc: ...here. * ira.c: Moved to... * ira.cc: ...here. * jump.c: Moved to... * jump.cc: ...here. * langhooks.c: Moved to... * langhooks.cc: ...here. * lcm.c: Moved to... * lcm.cc: ...here. * lists.c: Moved to... * lists.cc: ...here. * loop-doloop.c: Moved to... * loop-doloop.cc: ...here. * loop-init.c: Moved to... * loop-init.cc: ...here. * loop-invariant.c: Moved to... * loop-invariant.cc: ...here. * loop-iv.c: Moved to... * loop-iv.cc: ...here. * loop-unroll.c: Moved to... * loop-unroll.cc: ...here. * lower-subreg.c: Moved to... * lower-subreg.cc: ...here. * lra-assigns.c: Moved to... * lra-assigns.cc: ...here. * lra-coalesce.c: Moved to... * lra-coalesce.cc: ...here. * lra-constraints.c: Moved to... * lra-constraints.cc: ...here. * lra-eliminations.c: Moved to... * lra-eliminations.cc: ...here. * lra-lives.c: Moved to... * lra-lives.cc: ...here. * lra-remat.c: Moved to... * lra-remat.cc: ...here. * lra-spills.c: Moved to... * lra-spills.cc: ...here. * lra.c: Moved to... * lra.cc: ...here. * lto-cgraph.c: Moved to... * lto-cgraph.cc: ...here. * lto-compress.c: Moved to... * lto-compress.cc: ...here. * lto-opts.c: Moved to... * lto-opts.cc: ...here. * lto-section-in.c: Moved to... * lto-section-in.cc: ...here. * lto-section-out.c: Moved to... * lto-section-out.cc: ...here. * lto-streamer-in.c: Moved to... * lto-streamer-in.cc: ...here. * lto-streamer-out.c: Moved to... * lto-streamer-out.cc: ...here. * lto-streamer.c: Moved to... * lto-streamer.cc: ...here. * lto-wrapper.c: Moved to... * lto-wrapper.cc: ...here. * main.c: Moved to... * main.cc: ...here. * mcf.c: Moved to... * mcf.cc: ...here. * mode-switching.c: Moved to... * mode-switching.cc: ...here. * modulo-sched.c: Moved to... * modulo-sched.cc: ...here. * multiple_target.c: Moved to... * multiple_target.cc: ...here. * omp-expand.c: Moved to... * omp-expand.cc: ...here. * omp-general.c: Moved to... * omp-general.cc: ...here. * omp-low.c: Moved to... * omp-low.cc: ...here. * omp-offload.c: Moved to... * omp-offload.cc: ...here. * omp-simd-clone.c: Moved to... * omp-simd-clone.cc: ...here. * opt-suggestions.c: Moved to... * opt-suggestions.cc: ...here. * optabs-libfuncs.c: Moved to... * optabs-libfuncs.cc: ...here. * optabs-query.c: Moved to... * optabs-query.cc: ...here. * optabs-tree.c: Moved to... * optabs-tree.cc: ...here. * optabs.c: Moved to... * optabs.cc: ...here. * opts-common.c: Moved to... * opts-common.cc: ...here. * opts-global.c: Moved to... * opts-global.cc: ...here. * opts.c: Moved to... * opts.cc: ...here. * passes.c: Moved to... * passes.cc: ...here. * plugin.c: Moved to... * plugin.cc: ...here. * postreload-gcse.c: Moved to... * postreload-gcse.cc: ...here. * postreload.c: Moved to... * postreload.cc: ...here. * predict.c: Moved to... * predict.cc: ...here. * prefix.c: Moved to... * prefix.cc: ...here. * pretty-print.c: Moved to... * pretty-print.cc: ...here. * print-rtl-function.c: Moved to... * print-rtl-function.cc: ...here. * print-rtl.c: Moved to... * print-rtl.cc: ...here. * print-tree.c: Moved to... * print-tree.cc: ...here. * profile-count.c: Moved to... * profile-count.cc: ...here. * profile.c: Moved to... * profile.cc: ...here. * read-md.c: Moved to... * read-md.cc: ...here. * read-rtl-function.c: Moved to... * read-rtl-function.cc: ...here. * read-rtl.c: Moved to... * read-rtl.cc: ...here. * real.c: Moved to... * real.cc: ...here. * realmpfr.c: Moved to... * realmpfr.cc: ...here. * recog.c: Moved to... * recog.cc: ...here. * ree.c: Moved to... * ree.cc: ...here. * reg-stack.c: Moved to... * reg-stack.cc: ...here. * regcprop.c: Moved to... * regcprop.cc: ...here. * reginfo.c: Moved to... * reginfo.cc: ...here. * regrename.c: Moved to... * regrename.cc: ...here. * regstat.c: Moved to... * regstat.cc: ...here. * reload.c: Moved to... * reload.cc: ...here. * reload1.c: Moved to... * reload1.cc: ...here. * reorg.c: Moved to... * reorg.cc: ...here. * resource.c: Moved to... * resource.cc: ...here. * rtl-error.c: Moved to... * rtl-error.cc: ...here. * rtl-tests.c: Moved to... * rtl-tests.cc: ...here. * rtl.c: Moved to... * rtl.cc: ...here. * rtlanal.c: Moved to... * rtlanal.cc: ...here. * rtlhash.c: Moved to... * rtlhash.cc: ...here. * rtlhooks.c: Moved to... * rtlhooks.cc: ...here. * rtx-vector-builder.c: Moved to... * rtx-vector-builder.cc: ...here. * run-rtl-passes.c: Moved to... * run-rtl-passes.cc: ...here. * sancov.c: Moved to... * sancov.cc: ...here. * sanopt.c: Moved to... * sanopt.cc: ...here. * sbitmap.c: Moved to... * sbitmap.cc: ...here. * sched-deps.c: Moved to... * sched-deps.cc: ...here. * sched-ebb.c: Moved to... * sched-ebb.cc: ...here. * sched-rgn.c: Moved to... * sched-rgn.cc: ...here. * sel-sched-dump.c: Moved to... * sel-sched-dump.cc: ...here. * sel-sched-ir.c: Moved to... * sel-sched-ir.cc: ...here. * sel-sched.c: Moved to... * sel-sched.cc: ...here. * selftest-diagnostic.c: Moved to... * selftest-diagnostic.cc: ...here. * selftest-rtl.c: Moved to... * selftest-rtl.cc: ...here. * selftest-run-tests.c: Moved to... * selftest-run-tests.cc: ...here. * selftest.c: Moved to... * selftest.cc: ...here. * sese.c: Moved to... * sese.cc: ...here. * shrink-wrap.c: Moved to... * shrink-wrap.cc: ...here. * simplify-rtx.c: Moved to... * simplify-rtx.cc: ...here. * sparseset.c: Moved to... * sparseset.cc: ...here. * spellcheck-tree.c: Moved to... * spellcheck-tree.cc: ...here. * spellcheck.c: Moved to... * spellcheck.cc: ...here. * sreal.c: Moved to... * sreal.cc: ...here. * stack-ptr-mod.c: Moved to... * stack-ptr-mod.cc: ...here. * statistics.c: Moved to... * statistics.cc: ...here. * stmt.c: Moved to... * stmt.cc: ...here. * stor-layout.c: Moved to... * stor-layout.cc: ...here. * store-motion.c: Moved to... * store-motion.cc: ...here. * streamer-hooks.c: Moved to... * streamer-hooks.cc: ...here. * stringpool.c: Moved to... * stringpool.cc: ...here. * substring-locations.c: Moved to... * substring-locations.cc: ...here. * symtab.c: Moved to... * symtab.cc: ...here. * target-globals.c: Moved to... * target-globals.cc: ...here. * targhooks.c: Moved to... * targhooks.cc: ...here. * timevar.c: Moved to... * timevar.cc: ...here. * toplev.c: Moved to... * toplev.cc: ...here. * tracer.c: Moved to... * tracer.cc: ...here. * trans-mem.c: Moved to... * trans-mem.cc: ...here. * tree-affine.c: Moved to... * tree-affine.cc: ...here. * tree-call-cdce.c: Moved to... * tree-call-cdce.cc: ...here. * tree-cfg.c: Moved to... * tree-cfg.cc: ...here. * tree-cfgcleanup.c: Moved to... * tree-cfgcleanup.cc: ...here. * tree-chrec.c: Moved to... * tree-chrec.cc: ...here. * tree-complex.c: Moved to... * tree-complex.cc: ...here. * tree-data-ref.c: Moved to... * tree-data-ref.cc: ...here. * tree-dfa.c: Moved to... * tree-dfa.cc: ...here. * tree-diagnostic.c: Moved to... * tree-diagnostic.cc: ...here. * tree-dump.c: Moved to... * tree-dump.cc: ...here. * tree-eh.c: Moved to... * tree-eh.cc: ...here. * tree-emutls.c: Moved to... * tree-emutls.cc: ...here. * tree-if-conv.c: Moved to... * tree-if-conv.cc: ...here. * tree-inline.c: Moved to... * tree-inline.cc: ...here. * tree-into-ssa.c: Moved to... * tree-into-ssa.cc: ...here. * tree-iterator.c: Moved to... * tree-iterator.cc: ...here. * tree-loop-distribution.c: Moved to... * tree-loop-distribution.cc: ...here. * tree-nested.c: Moved to... * tree-nested.cc: ...here. * tree-nrv.c: Moved to... * tree-nrv.cc: ...here. * tree-object-size.c: Moved to... * tree-object-size.cc: ...here. * tree-outof-ssa.c: Moved to... * tree-outof-ssa.cc: ...here. * tree-parloops.c: Moved to... * tree-parloops.cc: ...here. * tree-phinodes.c: Moved to... * tree-phinodes.cc: ...here. * tree-predcom.c: Moved to... * tree-predcom.cc: ...here. * tree-pretty-print.c: Moved to... * tree-pretty-print.cc: ...here. * tree-profile.c: Moved to... * tree-profile.cc: ...here. * tree-scalar-evolution.c: Moved to... * tree-scalar-evolution.cc: ...here. * tree-sra.c: Moved to... * tree-sra.cc: ...here. * tree-ssa-address.c: Moved to... * tree-ssa-address.cc: ...here. * tree-ssa-alias.c: Moved to... * tree-ssa-alias.cc: ...here. * tree-ssa-ccp.c: Moved to... * tree-ssa-ccp.cc: ...here. * tree-ssa-coalesce.c: Moved to... * tree-ssa-coalesce.cc: ...here. * tree-ssa-copy.c: Moved to... * tree-ssa-copy.cc: ...here. * tree-ssa-dce.c: Moved to... * tree-ssa-dce.cc: ...here. * tree-ssa-dom.c: Moved to... * tree-ssa-dom.cc: ...here. * tree-ssa-dse.c: Moved to... * tree-ssa-dse.cc: ...here. * tree-ssa-forwprop.c: Moved to... * tree-ssa-forwprop.cc: ...here. * tree-ssa-ifcombine.c: Moved to... * tree-ssa-ifcombine.cc: ...here. * tree-ssa-live.c: Moved to... * tree-ssa-live.cc: ...here. * tree-ssa-loop-ch.c: Moved to... * tree-ssa-loop-ch.cc: ...here. * tree-ssa-loop-im.c: Moved to... * tree-ssa-loop-im.cc: ...here. * tree-ssa-loop-ivcanon.c: Moved to... * tree-ssa-loop-ivcanon.cc: ...here. * tree-ssa-loop-ivopts.c: Moved to... * tree-ssa-loop-ivopts.cc: ...here. * tree-ssa-loop-manip.c: Moved to... * tree-ssa-loop-manip.cc: ...here. * tree-ssa-loop-niter.c: Moved to... * tree-ssa-loop-niter.cc: ...here. * tree-ssa-loop-prefetch.c: Moved to... * tree-ssa-loop-prefetch.cc: ...here. * tree-ssa-loop-split.c: Moved to... * tree-ssa-loop-split.cc: ...here. * tree-ssa-loop-unswitch.c: Moved to... * tree-ssa-loop-unswitch.cc: ...here. * tree-ssa-loop.c: Moved to... * tree-ssa-loop.cc: ...here. * tree-ssa-math-opts.c: Moved to... * tree-ssa-math-opts.cc: ...here. * tree-ssa-operands.c: Moved to... * tree-ssa-operands.cc: ...here. * tree-ssa-phiopt.c: Moved to... * tree-ssa-phiopt.cc: ...here. * tree-ssa-phiprop.c: Moved to... * tree-ssa-phiprop.cc: ...here. * tree-ssa-pre.c: Moved to... * tree-ssa-pre.cc: ...here. * tree-ssa-propagate.c: Moved to... * tree-ssa-propagate.cc: ...here. * tree-ssa-reassoc.c: Moved to... * tree-ssa-reassoc.cc: ...here. * tree-ssa-sccvn.c: Moved to... * tree-ssa-sccvn.cc: ...here. * tree-ssa-scopedtables.c: Moved to... * tree-ssa-scopedtables.cc: ...here. * tree-ssa-sink.c: Moved to... * tree-ssa-sink.cc: ...here. * tree-ssa-strlen.c: Moved to... * tree-ssa-strlen.cc: ...here. * tree-ssa-structalias.c: Moved to... * tree-ssa-structalias.cc: ...here. * tree-ssa-tail-merge.c: Moved to... * tree-ssa-tail-merge.cc: ...here. * tree-ssa-ter.c: Moved to... * tree-ssa-ter.cc: ...here. * tree-ssa-threadbackward.c: Moved to... * tree-ssa-threadbackward.cc: ...here. * tree-ssa-threadedge.c: Moved to... * tree-ssa-threadedge.cc: ...here. * tree-ssa-threadupdate.c: Moved to... * tree-ssa-threadupdate.cc: ...here. * tree-ssa-uncprop.c: Moved to... * tree-ssa-uncprop.cc: ...here. * tree-ssa-uninit.c: Moved to... * tree-ssa-uninit.cc: ...here. * tree-ssa.c: Moved to... * tree-ssa.cc: ...here. * tree-ssanames.c: Moved to... * tree-ssanames.cc: ...here. * tree-stdarg.c: Moved to... * tree-stdarg.cc: ...here. * tree-streamer-in.c: Moved to... * tree-streamer-in.cc: ...here. * tree-streamer-out.c: Moved to... * tree-streamer-out.cc: ...here. * tree-streamer.c: Moved to... * tree-streamer.cc: ...here. * tree-switch-conversion.c: Moved to... * tree-switch-conversion.cc: ...here. * tree-tailcall.c: Moved to... * tree-tailcall.cc: ...here. * tree-vect-data-refs.c: Moved to... * tree-vect-data-refs.cc: ...here. * tree-vect-generic.c: Moved to... * tree-vect-generic.cc: ...here. * tree-vect-loop-manip.c: Moved to... * tree-vect-loop-manip.cc: ...here. * tree-vect-loop.c: Moved to... * tree-vect-loop.cc: ...here. * tree-vect-patterns.c: Moved to... * tree-vect-patterns.cc: ...here. * tree-vect-slp-patterns.c: Moved to... * tree-vect-slp-patterns.cc: ...here. * tree-vect-slp.c: Moved to... * tree-vect-slp.cc: ...here. * tree-vect-stmts.c: Moved to... * tree-vect-stmts.cc: ...here. * tree-vector-builder.c: Moved to... * tree-vector-builder.cc: ...here. * tree-vectorizer.c: Moved to... * tree-vectorizer.cc: ...here. * tree-vrp.c: Moved to... * tree-vrp.cc: ...here. * tree.c: Moved to... * tree.cc: ...here. * tsan.c: Moved to... * tsan.cc: ...here. * typed-splay-tree.c: Moved to... * typed-splay-tree.cc: ...here. * ubsan.c: Moved to... * ubsan.cc: ...here. * valtrack.c: Moved to... * valtrack.cc: ...here. * value-prof.c: Moved to... * value-prof.cc: ...here. * var-tracking.c: Moved to... * var-tracking.cc: ...here. * varasm.c: Moved to... * varasm.cc: ...here. * varpool.c: Moved to... * varpool.cc: ...here. * vec-perm-indices.c: Moved to... * vec-perm-indices.cc: ...here. * vec.c: Moved to... * vec.cc: ...here. * vmsdbgout.c: Moved to... * vmsdbgout.cc: ...here. * vr-values.c: Moved to... * vr-values.cc: ...here. * vtable-verify.c: Moved to... * vtable-verify.cc: ...here. * web.c: Moved to... * web.cc: ...here. * xcoffout.c: Moved to... * xcoffout.cc: ...here. gcc/c-family/ChangeLog: * c-ada-spec.c: Moved to... * c-ada-spec.cc: ...here. * c-attribs.c: Moved to... * c-attribs.cc: ...here. * c-common.c: Moved to... * c-common.cc: ...here. * c-cppbuiltin.c: Moved to... * c-cppbuiltin.cc: ...here. * c-dump.c: Moved to... * c-dump.cc: ...here. * c-format.c: Moved to... * c-format.cc: ...here. * c-gimplify.c: Moved to... * c-gimplify.cc: ...here. * c-indentation.c: Moved to... * c-indentation.cc: ...here. * c-lex.c: Moved to... * c-lex.cc: ...here. * c-omp.c: Moved to... * c-omp.cc: ...here. * c-opts.c: Moved to... * c-opts.cc: ...here. * c-pch.c: Moved to... * c-pch.cc: ...here. * c-ppoutput.c: Moved to... * c-ppoutput.cc: ...here. * c-pragma.c: Moved to... * c-pragma.cc: ...here. * c-pretty-print.c: Moved to... * c-pretty-print.cc: ...here. * c-semantics.c: Moved to... * c-semantics.cc: ...here. * c-ubsan.c: Moved to... * c-ubsan.cc: ...here. * c-warn.c: Moved to... * c-warn.cc: ...here. * cppspec.c: Moved to... * cppspec.cc: ...here. * stub-objc.c: Moved to... * stub-objc.cc: ...here. gcc/c/ChangeLog: * c-aux-info.c: Moved to... * c-aux-info.cc: ...here. * c-convert.c: Moved to... * c-convert.cc: ...here. * c-decl.c: Moved to... * c-decl.cc: ...here. * c-errors.c: Moved to... * c-errors.cc: ...here. * c-fold.c: Moved to... * c-fold.cc: ...here. * c-lang.c: Moved to... * c-lang.cc: ...here. * c-objc-common.c: Moved to... * c-objc-common.cc: ...here. * c-parser.c: Moved to... * c-parser.cc: ...here. * c-typeck.c: Moved to... * c-typeck.cc: ...here. * gccspec.c: Moved to... * gccspec.cc: ...here. * gimple-parser.c: Moved to... * gimple-parser.cc: ...here. gcc/cp/ChangeLog: * call.c: Moved to... * call.cc: ...here. * class.c: Moved to... * class.cc: ...here. * constexpr.c: Moved to... * constexpr.cc: ...here. * cp-gimplify.c: Moved to... * cp-gimplify.cc: ...here. * cp-lang.c: Moved to... * cp-lang.cc: ...here. * cp-objcp-common.c: Moved to... * cp-objcp-common.cc: ...here. * cp-ubsan.c: Moved to... * cp-ubsan.cc: ...here. * cvt.c: Moved to... * cvt.cc: ...here. * cxx-pretty-print.c: Moved to... * cxx-pretty-print.cc: ...here. * decl.c: Moved to... * decl.cc: ...here. * decl2.c: Moved to... * decl2.cc: ...here. * dump.c: Moved to... * dump.cc: ...here. * error.c: Moved to... * error.cc: ...here. * except.c: Moved to... * except.cc: ...here. * expr.c: Moved to... * expr.cc: ...here. * friend.c: Moved to... * friend.cc: ...here. * g++spec.c: Moved to... * g++spec.cc: ...here. * init.c: Moved to... * init.cc: ...here. * lambda.c: Moved to... * lambda.cc: ...here. * lex.c: Moved to... * lex.cc: ...here. * mangle.c: Moved to... * mangle.cc: ...here. * method.c: Moved to... * method.cc: ...here. * name-lookup.c: Moved to... * name-lookup.cc: ...here. * optimize.c: Moved to... * optimize.cc: ...here. * parser.c: Moved to... * parser.cc: ...here. * pt.c: Moved to... * pt.cc: ...here. * ptree.c: Moved to... * ptree.cc: ...here. * rtti.c: Moved to... * rtti.cc: ...here. * search.c: Moved to... * search.cc: ...here. * semantics.c: Moved to... * semantics.cc: ...here. * tree.c: Moved to... * tree.cc: ...here. * typeck.c: Moved to... * typeck.cc: ...here. * typeck2.c: Moved to... * typeck2.cc: ...here. * vtable-class-hierarchy.c: Moved to... * vtable-class-hierarchy.cc: ...here. gcc/fortran/ChangeLog: * arith.c: Moved to... * arith.cc: ...here. * array.c: Moved to... * array.cc: ...here. * bbt.c: Moved to... * bbt.cc: ...here. * check.c: Moved to... * check.cc: ...here. * class.c: Moved to... * class.cc: ...here. * constructor.c: Moved to... * constructor.cc: ...here. * convert.c: Moved to... * convert.cc: ...here. * cpp.c: Moved to... * cpp.cc: ...here. * data.c: Moved to... * data.cc: ...here. * decl.c: Moved to... * decl.cc: ...here. * dependency.c: Moved to... * dependency.cc: ...here. * dump-parse-tree.c: Moved to... * dump-parse-tree.cc: ...here. * error.c: Moved to... * error.cc: ...here. * expr.c: Moved to... * expr.cc: ...here. * f95-lang.c: Moved to... * f95-lang.cc: ...here. * frontend-passes.c: Moved to... * frontend-passes.cc: ...here. * gfortranspec.c: Moved to... * gfortranspec.cc: ...here. * interface.c: Moved to... * interface.cc: ...here. * intrinsic.c: Moved to... * intrinsic.cc: ...here. * io.c: Moved to... * io.cc: ...here. * iresolve.c: Moved to... * iresolve.cc: ...here. * match.c: Moved to... * match.cc: ...here. * matchexp.c: Moved to... * matchexp.cc: ...here. * misc.c: Moved to... * misc.cc: ...here. * module.c: Moved to... * module.cc: ...here. * openmp.c: Moved to... * openmp.cc: ...here. * options.c: Moved to... * options.cc: ...here. * parse.c: Moved to... * parse.cc: ...here. * primary.c: Moved to... * primary.cc: ...here. * resolve.c: Moved to... * resolve.cc: ...here. * scanner.c: Moved to... * scanner.cc: ...here. * simplify.c: Moved to... * simplify.cc: ...here. * st.c: Moved to... * st.cc: ...here. * symbol.c: Moved to... * symbol.cc: ...here. * target-memory.c: Moved to... * target-memory.cc: ...here. * trans-array.c: Moved to... * trans-array.cc: ...here. * trans-common.c: Moved to... * trans-common.cc: ...here. * trans-const.c: Moved to... * trans-const.cc: ...here. * trans-decl.c: Moved to... * trans-decl.cc: ...here. * trans-expr.c: Moved to... * trans-expr.cc: ...here. * trans-intrinsic.c: Moved to... * trans-intrinsic.cc: ...here. * trans-io.c: Moved to... * trans-io.cc: ...here. * trans-openmp.c: Moved to... * trans-openmp.cc: ...here. * trans-stmt.c: Moved to... * trans-stmt.cc: ...here. * trans-types.c: Moved to... * trans-types.cc: ...here. * trans.c: Moved to... * trans.cc: ...here. gcc/go/ChangeLog: * go-backend.c: Moved to... * go-backend.cc: ...here. * go-lang.c: Moved to... * go-lang.cc: ...here. * gospec.c: Moved to... * gospec.cc: ...here. gcc/jit/ChangeLog: * dummy-frontend.c: Moved to... * dummy-frontend.cc: ...here. * jit-builtins.c: Moved to... * jit-builtins.cc: ...here. * jit-logging.c: Moved to... * jit-logging.cc: ...here. * jit-playback.c: Moved to... * jit-playback.cc: ...here. * jit-recording.c: Moved to... * jit-recording.cc: ...here. * jit-result.c: Moved to... * jit-result.cc: ...here. * jit-spec.c: Moved to... * jit-spec.cc: ...here. * jit-tempdir.c: Moved to... * jit-tempdir.cc: ...here. * jit-w32.c: Moved to... * jit-w32.cc: ...here. * libgccjit.c: Moved to... * libgccjit.cc: ...here. gcc/lto/ChangeLog: * common.c: Moved to... * common.cc: ...here. * lto-common.c: Moved to... * lto-common.cc: ...here. * lto-dump.c: Moved to... * lto-dump.cc: ...here. * lto-lang.c: Moved to... * lto-lang.cc: ...here. * lto-object.c: Moved to... * lto-object.cc: ...here. * lto-partition.c: Moved to... * lto-partition.cc: ...here. * lto-symtab.c: Moved to... * lto-symtab.cc: ...here. * lto.c: Moved to... * lto.cc: ...here. gcc/objc/ChangeLog: * objc-act.c: Moved to... * objc-act.cc: ...here. * objc-encoding.c: Moved to... * objc-encoding.cc: ...here. * objc-gnu-runtime-abi-01.c: Moved to... * objc-gnu-runtime-abi-01.cc: ...here. * objc-lang.c: Moved to... * objc-lang.cc: ...here. * objc-map.c: Moved to... * objc-map.cc: ...here. * objc-next-runtime-abi-01.c: Moved to... * objc-next-runtime-abi-01.cc: ...here. * objc-next-runtime-abi-02.c: Moved to... * objc-next-runtime-abi-02.cc: ...here. * objc-runtime-shared-support.c: Moved to... * objc-runtime-shared-support.cc: ...here. gcc/objcp/ChangeLog: * objcp-decl.c: Moved to... * objcp-decl.cc: ...here. * objcp-lang.c: Moved to... * objcp-lang.cc: ...here. libcpp/ChangeLog: * charset.c: Moved to... * charset.cc: ...here. * directives.c: Moved to... * directives.cc: ...here. * errors.c: Moved to... * errors.cc: ...here. * expr.c: Moved to... * expr.cc: ...here. * files.c: Moved to... * files.cc: ...here. * identifiers.c: Moved to... * identifiers.cc: ...here. * init.c: Moved to... * init.cc: ...here. * lex.c: Moved to... * lex.cc: ...here. * line-map.c: Moved to... * line-map.cc: ...here. * macro.c: Moved to... * macro.cc: ...here. * makeucnid.c: Moved to... * makeucnid.cc: ...here. * mkdeps.c: Moved to... * mkdeps.cc: ...here. * pch.c: Moved to... * pch.cc: ...here. * symtab.c: Moved to... * symtab.cc: ...here. * traditional.c: Moved to... * traditional.cc: ...here.
Diffstat (limited to 'gcc/config/sh/sh.cc')
-rw-r--r--gcc/config/sh/sh.cc12630
1 files changed, 12630 insertions, 0 deletions
diff --git a/gcc/config/sh/sh.cc b/gcc/config/sh/sh.cc
new file mode 100644
index 0000000..ca096ad
--- /dev/null
+++ b/gcc/config/sh/sh.cc
@@ -0,0 +1,12630 @@
+/* Output routines for GCC for Renesas / SuperH SH.
+ Copyright (C) 1993-2022 Free Software Foundation, Inc.
+ Contributed by Steve Chamberlain (sac@cygnus.com).
+ Improved by Jim Wilson (wilson@cygnus.com).
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include <sstream>
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "optabs.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "alias.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "varasm.h"
+#include "flags.h"
+#include "explow.h"
+#include "expr.h"
+#include "reload.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "dwarf2.h"
+#include "langhooks.h"
+#include "cfgrtl.h"
+#include "intl.h"
+#include "sched-int.h"
+#include "gimplify.h"
+#include "tm-constrs.h"
+#include "opts.h"
+#include "tree-pass.h"
+#include "context.h"
+#include "builtins.h"
+#include "rtl-iter.h"
+#include "regs.h"
+#include "toplev.h"
+
+/* This file should be included last. */
+#include "target-def.h"
+
+int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
+
+#define CONST_OK_FOR_ADD(size) CONST_OK_FOR_I08 (size)
+#define GEN_MOV (*(gen_movsi))
+#define GEN_ADD3 (*(gen_addsi3))
+#define GEN_SUB3 (*(gen_subsi3))
+
+/* Used to simplify the logic below. Find the attributes wherever
+ they may be. */
+#define SH_ATTRIBUTES(decl) \
+ (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl) \
+ : DECL_ATTRIBUTES (decl) \
+ ? (DECL_ATTRIBUTES (decl)) \
+ : TYPE_ATTRIBUTES (TREE_TYPE (decl))
+
+/* Set to true by expand_prologue() when the function is an
+ interrupt handler. */
+bool current_function_interrupt;
+
+tree sh_deferred_function_attributes;
+tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
+
+/* Global variables for machine-dependent things. */
+
+/* Which cpu are we scheduling for. */
+enum processor_type sh_cpu;
+
+/* Definitions used in ready queue reordering for first scheduling pass. */
+
+/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID. */
+static short *regmode_weight[2];
+
+/* Total SFmode and SImode weights of scheduled insns. */
+static int curr_regmode_pressure[2];
+
+/* Number of r0 life regions. */
+static int r0_life_regions;
+
+/* If true, skip cycles for Q -> R movement. */
+static int skip_cycles = 0;
+
+/* Cached value of can_issue_more. This is cached in sh_variable_issue hook
+ and returned from sh_reorder2. */
+static short cached_can_issue_more;
+
+/* Unique number for UNSPEC_BBR pattern. */
+static unsigned int unspec_bbr_uid = 1;
+
+/* Provides the class number of the smallest class containing
+ reg number. */
+enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
+{
+ R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+ DF_REGS, DF_REGS, DF_REGS, DF_REGS,
+ DF_REGS, DF_REGS, DF_REGS, DF_REGS,
+ NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
+ MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
+ GENERAL_REGS, GENERAL_REGS,
+};
+
+char sh_register_names[FIRST_PSEUDO_REGISTER] \
+ [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
+
+char sh_additional_register_names[ADDREGNAMES_SIZE] \
+ [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
+ = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
+
+int assembler_dialect;
+
+static void split_branches (rtx_insn *);
+static int branch_dest (rtx);
+static void print_slot (rtx_sequence *);
+static rtx_code_label *add_constant (rtx, machine_mode, rtx);
+static void dump_table (rtx_insn *, rtx_insn *);
+static bool broken_move (rtx_insn *);
+static bool mova_p (rtx_insn *);
+static rtx_insn *find_barrier (int, rtx_insn *, rtx_insn *);
+static bool noncall_uses_reg (rtx, rtx_insn *, rtx *);
+static rtx_insn *gen_block_redirect (rtx_insn *, int, int);
+static void sh_reorg (void);
+static void sh_option_override (void);
+static void sh_override_options_after_change (void);
+static void output_stack_adjust (int, rtx, int, HARD_REG_SET *, bool);
+static rtx_insn* emit_frame_insn (rtx);
+static rtx push (int);
+static void pop (int);
+static void push_regs (HARD_REG_SET* mask, bool interrupt_handler);
+static int calc_live_regs (HARD_REG_SET *);
+static HOST_WIDE_INT rounded_frame_size (int);
+static bool sh_frame_pointer_required (void);
+static void sh_emit_mode_set (int, int, int, HARD_REG_SET);
+static int sh_mode_needed (int, rtx_insn *);
+static int sh_mode_after (int, int, rtx_insn *);
+static int sh_mode_entry (int);
+static int sh_mode_exit (int);
+static int sh_mode_priority (int entity, int n);
+
+static rtx mark_constant_pool_use (rtx);
+static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree,
+ int, bool *);
+static tree sh_handle_resbank_handler_attribute (tree *, tree,
+ tree, int, bool *);
+static tree sh2a_handle_function_vector_handler_attribute (tree *, tree,
+ tree, int, bool *);
+static tree sh_handle_sp_switch_attribute (tree *, tree, tree, int, bool *);
+static tree sh_handle_trap_exit_attribute (tree *, tree, tree, int, bool *);
+static tree sh_handle_renesas_attribute (tree *, tree, tree, int, bool *);
+static void sh_print_operand (FILE *, rtx, int);
+static void sh_print_operand_address (FILE *, machine_mode, rtx);
+static bool sh_print_operand_punct_valid_p (unsigned char code);
+static bool sh_asm_output_addr_const_extra (FILE *file, rtx x);
+static void sh_output_function_epilogue (FILE *);
+static void sh_insert_attributes (tree, tree *);
+static const char *sh_check_pch_target_flags (int);
+static int sh_register_move_cost (machine_mode, reg_class_t, reg_class_t);
+static int sh_adjust_cost (rtx_insn *, int, rtx_insn *, int, unsigned int);
+static int sh_issue_rate (void);
+static int sh_dfa_new_cycle (FILE *, int, rtx_insn *, int, int, int *sort_p);
+static short find_set_regmode_weight (rtx, machine_mode);
+static short find_insn_regmode_weight (rtx, machine_mode);
+static void find_regmode_weight (basic_block, machine_mode);
+static int find_r0_life_regions (basic_block);
+static void sh_md_init_global (FILE *, int, int);
+static void sh_md_finish_global (FILE *, int);
+static int rank_for_reorder (const void *, const void *);
+static void swap_reorder (rtx_insn **, int);
+static void ready_reorder (rtx_insn **, int);
+static bool high_pressure (machine_mode);
+static int sh_reorder (FILE *, int, rtx_insn **, int *, int);
+static int sh_reorder2 (FILE *, int, rtx_insn **, int *, int);
+static void sh_md_init (FILE *, int, int);
+static int sh_variable_issue (FILE *, int, rtx_insn *, int);
+
+static bool sh_function_ok_for_sibcall (tree, tree);
+
+static bool sh_can_follow_jump (const rtx_insn *, const rtx_insn *);
+static bool sh_ms_bitfield_layout_p (const_tree);
+
+static void sh_init_builtins (void);
+static tree sh_builtin_decl (unsigned, bool);
+static rtx sh_expand_builtin (tree, rtx, rtx, machine_mode, int);
+static void sh_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree);
+static void sh_file_start (void);
+static bool sh_assemble_integer (rtx, unsigned int, int);
+static bool flow_dependent_p (rtx_insn *, rtx_insn *);
+static void flow_dependent_p_1 (rtx, const_rtx, void *);
+static int shiftcosts (rtx);
+static int and_xor_ior_costs (rtx, int);
+static int addsubcosts (rtx);
+static int multcosts (rtx);
+static bool unspec_caller_rtx_p (rtx);
+static bool sh_cannot_copy_insn_p (rtx_insn *);
+static bool sh_cannot_force_const_mem_p (machine_mode, rtx);
+static bool sh_rtx_costs (rtx, machine_mode, int, int, int *, bool);
+static int sh_address_cost (rtx, machine_mode, addr_space_t, bool);
+static int sh_pr_n_sets (void);
+static rtx sh_allocate_initial_value (rtx);
+static reg_class_t sh_preferred_reload_class (rtx, reg_class_t);
+static reg_class_t sh_secondary_reload (bool, rtx, reg_class_t,
+ machine_mode,
+ struct secondary_reload_info *);
+static bool sh_legitimate_address_p (machine_mode, rtx, bool);
+static rtx sh_legitimize_address (rtx, rtx, machine_mode);
+static rtx sh_delegitimize_address (rtx);
+static bool sh_cannot_substitute_mem_equiv_p (rtx);
+static bool sh_legitimize_address_displacement (rtx *, rtx *,
+ poly_int64, machine_mode);
+static int scavenge_reg (HARD_REG_SET *s);
+
+static rtx sh_struct_value_rtx (tree, int);
+static rtx sh_function_value (const_tree, const_tree, bool);
+static bool sh_function_value_regno_p (const unsigned int);
+static rtx sh_libcall_value (machine_mode, const_rtx);
+static bool sh_return_in_memory (const_tree, const_tree);
+static rtx sh_builtin_saveregs (void);
+static void sh_setup_incoming_varargs (cumulative_args_t,
+ const function_arg_info &, int *, int);
+static bool sh_strict_argument_naming (cumulative_args_t);
+static bool sh_pretend_outgoing_varargs_named (cumulative_args_t);
+static void sh_atomic_assign_expand_fenv (tree *, tree *, tree *);
+static tree sh_build_builtin_va_list (void);
+static void sh_va_start (tree, rtx);
+static tree sh_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
+static bool sh_promote_prototypes (const_tree);
+static machine_mode sh_promote_function_mode (const_tree type,
+ machine_mode,
+ int *punsignedp,
+ const_tree funtype,
+ int for_return);
+static bool sh_pass_by_reference (cumulative_args_t,
+ const function_arg_info &);
+static bool sh_callee_copies (cumulative_args_t, const function_arg_info &);
+static int sh_arg_partial_bytes (cumulative_args_t, const function_arg_info &);
+static void sh_function_arg_advance (cumulative_args_t,
+ const function_arg_info &);
+static rtx sh_function_arg (cumulative_args_t, const function_arg_info &);
+static int sh_dwarf_calling_convention (const_tree);
+static void sh_encode_section_info (tree, rtx, int);
+static bool sh2a_function_vector_p (tree);
+static void sh_trampoline_init (rtx, tree, rtx);
+static rtx sh_trampoline_adjust_address (rtx);
+static void sh_conditional_register_usage (void);
+static bool sh_legitimate_constant_p (machine_mode, rtx);
+static int mov_insn_size (machine_mode, bool);
+static int mov_insn_alignment_mask (machine_mode, bool);
+static bool sh_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT,
+ unsigned int,
+ enum by_pieces_operation,
+ bool);
+static bool sequence_insn_p (rtx_insn *);
+static void sh_canonicalize_comparison (int *, rtx *, rtx *, bool);
+static void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
+ machine_mode, bool);
+static bool sh_legitimate_combined_insn (rtx_insn* insn);
+
+static bool sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2);
+
+static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
+static unsigned int sh_hard_regno_nregs (unsigned int, machine_mode);
+static bool sh_hard_regno_mode_ok (unsigned int, machine_mode);
+static bool sh_modes_tieable_p (machine_mode, machine_mode);
+static bool sh_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
+
+static const struct attribute_spec sh_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+ { "interrupt_handler", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "sp_switch", 1, 1, true, false, false, false,
+ sh_handle_sp_switch_attribute, NULL },
+ { "trap_exit", 1, 1, true, false, false, false,
+ sh_handle_trap_exit_attribute, NULL },
+ { "renesas", 0, 0, false, true, false, false,
+ sh_handle_renesas_attribute, NULL },
+ { "trapa_handler", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "nosave_low_regs", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "resbank", 0, 0, true, false, false, false,
+ sh_handle_resbank_handler_attribute, NULL },
+ { "function_vector", 1, 1, true, false, false, false,
+ sh2a_handle_function_vector_handler_attribute, NULL },
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
+};
+
+/* Initialize the GCC target structure. */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE sh_attribute_table
+
+/* The next two are used for debug info when compiling with -gdwarf. */
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE sh_option_override
+
+#undef TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE
+#define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE \
+ sh_override_options_after_change
+
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND sh_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS sh_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P sh_print_operand_punct_valid_p
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA sh_asm_output_addr_const_extra
+
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK sh_output_mi_thunk
+
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK \
+ hook_bool_const_tree_hwi_hwi_const_tree_true
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START sh_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER sh_assemble_integer
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST sh_register_move_cost
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
+
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE sh_issue_rate
+
+/* The next 5 hooks have been implemented for reenabling sched1. With the
+ help of these macros we are limiting the movement of insns in sched1 to
+ reduce the register pressure. The overall idea is to keep count of SImode
+ and SFmode regs required by already scheduled insns. When these counts
+ cross some threshold values; give priority to insns that free registers.
+ The insn that frees registers is most likely to be the insn with lowest
+ LUID (original insn order); but such an insn might be there in the stalled
+ queue (Q) instead of the ready queue (R). To solve this, we skip cycles
+ up to a max of 8 cycles so that such insns may move from Q -> R.
+
+ The description of the hooks are as below:
+
+ TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic
+ scheduler; it is called inside the sched_init function just after
+ find_insn_reg_weights function call. It is used to calculate the SImode
+ and SFmode weights of insns of basic blocks; much similar to what
+ find_insn_reg_weights does.
+ TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook.
+
+ TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is
+ indicated by TARGET_SCHED_REORDER2; doing this may move insns from
+ (Q)->(R).
+
+ TARGET_SCHED_REORDER: If the register pressure for SImode or SFmode is
+ high; reorder the ready queue so that the insn with lowest LUID will be
+ issued next.
+
+ TARGET_SCHED_REORDER2: If the register pressure is high, indicate to
+ TARGET_SCHED_DFA_NEW_CYCLE to skip cycles.
+
+ TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it
+ can be returned from TARGET_SCHED_REORDER2.
+
+ TARGET_SCHED_INIT: Reset the register pressure counting variables. */
+
+#undef TARGET_SCHED_DFA_NEW_CYCLE
+#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle
+
+#undef TARGET_SCHED_INIT_GLOBAL
+#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global
+
+#undef TARGET_SCHED_FINISH_GLOBAL
+#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global
+
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue
+
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER sh_reorder
+
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 sh_reorder2
+
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT sh_md_init
+
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS sh_delegitimize_address
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS sh_legitimize_address
+
+#undef TARGET_CAN_FOLLOW_JUMP
+#define TARGET_CAN_FOLLOW_JUMP sh_can_follow_jump
+
+#undef TARGET_MS_BITFIELD_LAYOUT_P
+#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS sh_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL sh_builtin_decl
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN sh_expand_builtin
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL sh_function_ok_for_sibcall
+
+#undef TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P sh_cannot_copy_insn_p
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS sh_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST sh_address_cost
+#undef TARGET_ALLOCATE_INITIAL_VALUE
+#define TARGET_ALLOCATE_INITIAL_VALUE sh_allocate_initial_value
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
+
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN sh_dwarf_register_span
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE sh_promote_function_mode
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE sh_function_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P sh_function_value_regno_p
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE sh_libcall_value
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY sh_return_in_memory
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS sh_builtin_saveregs
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS sh_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING sh_strict_argument_naming
+#undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED
+#define TARGET_PRETEND_OUTGOING_VARARGS_NAMED sh_pretend_outgoing_varargs_named
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE sh_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES sh_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG sh_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE sh_function_arg_advance
+
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV sh_atomic_assign_expand_fenv
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START sh_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR sh_gimplify_va_arg_expr
+
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P sh_vector_mode_supported_p
+
+#undef TARGET_CHECK_PCH_TARGET_FLAGS
+#define TARGET_CHECK_PCH_TARGET_FLAGS sh_check_pch_target_flags
+
+#undef TARGET_DWARF_CALLING_CONVENTION
+#define TARGET_DWARF_CALLING_CONVENTION sh_dwarf_calling_convention
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED sh_frame_pointer_required
+
+#undef TARGET_MODE_EMIT
+#define TARGET_MODE_EMIT sh_emit_mode_set
+
+#undef TARGET_MODE_NEEDED
+#define TARGET_MODE_NEEDED sh_mode_needed
+
+#undef TARGET_MODE_AFTER
+#define TARGET_MODE_AFTER sh_mode_after
+
+#undef TARGET_MODE_ENTRY
+#define TARGET_MODE_ENTRY sh_mode_entry
+
+#undef TARGET_MODE_EXIT
+#define TARGET_MODE_EXIT sh_mode_exit
+
+#undef TARGET_MODE_PRIORITY
+#define TARGET_MODE_PRIORITY sh_mode_priority
+
+/* Return regmode weight for insn. */
+#define INSN_REGMODE_WEIGHT(INSN, MODE)\
+ regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)]
+
+/* Return current register pressure for regmode. */
+#define CURR_REGMODE_PRESSURE(MODE)\
+ curr_regmode_pressure[((MODE) == SImode) ? 0 : 1]
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO sh_encode_section_info
+
+#undef TARGET_LRA_P
+#define TARGET_LRA_P sh_lra_p
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD sh_secondary_reload
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS sh_preferred_reload_class
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE sh_conditional_register_usage
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P sh_legitimate_address_p
+
+#undef TARGET_CANNOT_SUBSTITUTE_MEM_EQUIV_P
+#define TARGET_CANNOT_SUBSTITUTE_MEM_EQUIV_P sh_cannot_substitute_mem_equiv_p
+
+#undef TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT
+#define TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT \
+ sh_legitimize_address_displacement
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT sh_trampoline_init
+#undef TARGET_TRAMPOLINE_ADJUST_ADDRESS
+#define TARGET_TRAMPOLINE_ADJUST_ADDRESS sh_trampoline_adjust_address
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P sh_legitimate_constant_p
+
+#undef TARGET_CANONICALIZE_COMPARISON
+#define TARGET_CANONICALIZE_COMPARISON sh_canonicalize_comparison
+
+#undef TARGET_LEGITIMATE_COMBINED_INSN
+#define TARGET_LEGITIMATE_COMBINED_INSN sh_legitimate_combined_insn
+
+#undef TARGET_FIXED_CONDITION_CODE_REGS
+#define TARGET_FIXED_CONDITION_CODE_REGS sh_fixed_condition_code_regs
+
+#undef TARGET_USE_BY_PIECES_INFRASTRUCTURE_P
+#define TARGET_USE_BY_PIECES_INFRASTRUCTURE_P \
+ sh_use_by_pieces_infrastructure_p
+
+/* Machine-specific symbol_ref flags. */
+#define SYMBOL_FLAG_FUNCVEC_FUNCTION (SYMBOL_FLAG_MACH_DEP << 0)
+
+/* The tas.b instruction sets the 7th bit in the byte, i.e. 0x80. This value
+ is used by optabs.c atomic op expansion code as well as in sync.md. */
+#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
+#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 0x80
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM sh_cannot_force_const_mem_p
+
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS sh_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK sh_hard_regno_mode_ok
+
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P sh_modes_tieable_p
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS sh_can_change_mode_class
+
+#undef TARGET_CONSTANT_ALIGNMENT
+#define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings
+
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+
+/* Information on the currently selected atomic model.
+ This is initialized in sh_option_override. */
+static sh_atomic_model selected_atomic_model_;
+
+const sh_atomic_model&
+selected_atomic_model (void)
+{
+ return selected_atomic_model_;
+}
+
+static sh_atomic_model
+parse_validate_atomic_model_option (const char* str)
+{
+ const char* model_names[sh_atomic_model::num_models];
+ model_names[sh_atomic_model::none] = "none";
+ model_names[sh_atomic_model::soft_gusa] = "soft-gusa";
+ model_names[sh_atomic_model::hard_llcs] = "hard-llcs";
+ model_names[sh_atomic_model::soft_tcb] = "soft-tcb";
+ model_names[sh_atomic_model::soft_imask] = "soft-imask";
+
+ const char* model_cdef_names[sh_atomic_model::num_models];
+ model_cdef_names[sh_atomic_model::none] = "NONE";
+ model_cdef_names[sh_atomic_model::soft_gusa] = "SOFT_GUSA";
+ model_cdef_names[sh_atomic_model::hard_llcs] = "HARD_LLCS";
+ model_cdef_names[sh_atomic_model::soft_tcb] = "SOFT_TCB";
+ model_cdef_names[sh_atomic_model::soft_imask] = "SOFT_IMASK";
+
+ sh_atomic_model ret;
+ ret.type = sh_atomic_model::none;
+ ret.name = model_names[sh_atomic_model::none];
+ ret.cdef_name = model_cdef_names[sh_atomic_model::none];
+ ret.strict = false;
+ ret.tcb_gbr_offset = -1;
+
+ /* Handle empty string as 'none'. */
+ if (str == NULL || *str == '\0')
+ return ret;
+
+#define err_ret(...) do { error (__VA_ARGS__); return ret; } while (0)
+
+ std::vector<std::string> tokens;
+ for (std::stringstream ss (str); ss.good (); )
+ {
+ tokens.push_back (std::string ());
+ std::getline (ss, tokens.back (), ',');
+ }
+
+ if (tokens.empty ())
+ err_ret ("invalid atomic model option");
+
+ /* The first token must be the atomic model name. */
+ {
+ for (size_t i = 0; i < sh_atomic_model::num_models; ++i)
+ if (tokens.front () == model_names[i])
+ {
+ ret.type = (sh_atomic_model::enum_type)i;
+ ret.name = model_names[i];
+ ret.cdef_name = model_cdef_names[i];
+ goto got_mode_name;
+ }
+
+ err_ret ("invalid atomic model name \"%s\"", tokens.front ().c_str ());
+got_mode_name:;
+ }
+
+ /* Go through the remaining tokens. */
+ for (size_t i = 1; i < tokens.size (); ++i)
+ {
+ if (tokens[i] == "strict")
+ ret.strict = true;
+ else if (!tokens[i].compare (0, strlen ("gbr-offset="), "gbr-offset="))
+ {
+ std::string offset_str = tokens[i].substr (strlen ("gbr-offset="));
+ ret.tcb_gbr_offset = integral_argument (offset_str.c_str ());
+ if (offset_str.empty () || ret.tcb_gbr_offset == -1)
+ err_ret ("could not parse gbr-offset value \"%s\" in atomic model "
+ "option", offset_str.c_str ());
+ }
+ else
+ err_ret ("unknown parameter \"%s\" in atomic model option",
+ tokens[i].c_str ());
+ }
+
+ /* Check that the selection makes sense. */
+ if (ret.type == sh_atomic_model::soft_gusa && !TARGET_SH3)
+ err_ret ("atomic model %s is only available on SH3 and SH4 targets",
+ ret.name);
+
+ if (ret.type == sh_atomic_model::hard_llcs && !TARGET_SH4A)
+ err_ret ("atomic model %s is only available on SH4A targets", ret.name);
+
+ if (ret.type == sh_atomic_model::soft_tcb && ret.tcb_gbr_offset == -1)
+ err_ret ("atomic model %s requires gbr-offset parameter", ret.name);
+
+ if (ret.type == sh_atomic_model::soft_tcb
+ && (ret.tcb_gbr_offset < 0 || ret.tcb_gbr_offset > 1020
+ || (ret.tcb_gbr_offset & 3) != 0))
+ err_ret ("invalid gbr-offset value \"%d\" for atomic model %s; it must be "
+ "a multiple of 4 in the range 0-1020", ret.tcb_gbr_offset,
+ ret.name);
+
+ if (ret.type == sh_atomic_model::soft_imask && TARGET_USERMODE)
+ err_ret ("cannot use atomic model %s in user mode", ret.name);
+
+ return ret;
+
+#undef err_ret
+}
+
+/* Register SH specific RTL passes. */
+extern opt_pass* make_pass_sh_treg_combine (gcc::context* ctx, bool split_insns,
+ const char* name);
+extern opt_pass* make_pass_sh_optimize_sett_clrt (gcc::context* ctx,
+ const char* name);
+static void
+register_sh_passes (void)
+{
+/* Running the sh_treg_combine pass after ce1 generates better code when
+ comparisons are combined and reg-reg moves are introduced, because
+ reg-reg moves will be eliminated afterwards. However, there are quite
+ some cases where combine will be unable to fold comparison related insns,
+ thus for now don't do it.
+ register_pass (make_pass_sh_treg_combine (g, false, "sh_treg_combine1"),
+ PASS_POS_INSERT_AFTER, "ce1", 1);
+*/
+
+ /* Run sh_treg_combine pass after combine but before register allocation. */
+ register_pass (make_pass_sh_treg_combine (g, true, "sh_treg_combine2"),
+ PASS_POS_INSERT_AFTER, "split1", 1);
+
+ /* Run sh_treg_combine pass after register allocation and basic block
+ reordering as this sometimes creates new opportunities. */
+ register_pass (make_pass_sh_treg_combine (g, true, "sh_treg_combine3"),
+ PASS_POS_INSERT_AFTER, "split3", 1);
+
+ /* Optimize sett and clrt insns, by e.g. removing them if the T bit value
+ is known after a conditional branch.
+ This must be done after basic blocks and branch conditions have
+ stabilized and won't be changed by further passes. */
+ register_pass (make_pass_sh_optimize_sett_clrt (g, "sh_optimize_sett_clrt"),
+ PASS_POS_INSERT_BEFORE, "sched2", 1);
+}
+
+/* Implement TARGET_OPTION_OVERRIDE macro. Validate and override
+ various options, and do some machine dependent initialization. */
+static void
+sh_option_override (void)
+{
+ int regno;
+
+ SUBTARGET_OVERRIDE_OPTIONS;
+
+ sh_cpu = PROCESSOR_SH1;
+ assembler_dialect = 0;
+ if (TARGET_SH2)
+ sh_cpu = PROCESSOR_SH2;
+ if (TARGET_SH2E)
+ sh_cpu = PROCESSOR_SH2E;
+ if (TARGET_SH2A)
+ sh_cpu = PROCESSOR_SH2A;
+ if (TARGET_SH3)
+ sh_cpu = PROCESSOR_SH3;
+ if (TARGET_SH3E)
+ sh_cpu = PROCESSOR_SH3E;
+ if (TARGET_SH4)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4;
+ }
+ if (TARGET_SH4A)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4A;
+ }
+
+ /* User/priviledged mode is supported only on SH3* and SH4*.
+ Disable it for everything else. */
+ if (!TARGET_SH3 && TARGET_USERMODE)
+ TARGET_USERMODE = false;
+
+ if (! strcmp (sh_div_str, "call-div1"))
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ else if (! strcmp (sh_div_str, "call-fp") && TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_CALL_FP;
+ else if (! strcmp (sh_div_str, "call-table") && TARGET_DYNSHIFT)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ else
+ {
+ /* Pick one that makes most sense for the target in general.
+ It is not much good to use different functions depending on -Os,
+ since then we'll end up with two different functions when some of
+ the code is compiled for size, and some for speed. */
+
+ /* SH4 tends to emphasize speed. */
+ if (TARGET_HARD_SH4)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ /* These have their own way of doing things. */
+ else if (TARGET_SH2A)
+ sh_div_strategy = SH_DIV_INTRINSIC;
+ /* SH1 .. SH3 cores often go into small-footprint systems, so
+ default to the smallest implementation available. */
+ else
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ }
+
+ if (sh_divsi3_libfunc[0])
+ ; /* User supplied - leave it alone. */
+ else if (TARGET_DIVIDE_CALL_FP)
+ sh_divsi3_libfunc = "__sdivsi3_i4";
+ else if (TARGET_DIVIDE_CALL_TABLE)
+ sh_divsi3_libfunc = "__sdivsi3_i4i";
+ else
+ sh_divsi3_libfunc = "__sdivsi3";
+
+ if (sh_branch_cost == -1)
+ {
+ /* The SH1 does not have delay slots, hence we get a pipeline stall
+ at every branch. The SH4 is superscalar, so the single delay slot
+ is not sufficient to keep both pipelines filled.
+ In any case, set the default branch cost to '2', as it results in
+ slightly overall smaller code and also enables some if conversions
+ that are required for matching special T bit related insns. */
+ sh_branch_cost = 2;
+ }
+
+ /* Set -mzdcbranch for SH4 / SH4A if not otherwise specified by the user. */
+ if (! OPTION_SET_P (TARGET_ZDCBRANCH) && TARGET_HARD_SH4)
+ TARGET_ZDCBRANCH = 1;
+
+ /* FDPIC code is a special form of PIC, and the vast majority of code
+ generation constraints that apply to PIC also apply to FDPIC, so we
+ set flag_pic to avoid the need to check TARGET_FDPIC everywhere
+ flag_pic is checked. */
+ if (TARGET_FDPIC && !flag_pic)
+ flag_pic = 2;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (! VALID_REGISTER_P (regno))
+ sh_register_names[regno][0] = '\0';
+
+ for (regno = 0; regno < ADDREGNAMES_SIZE; regno++)
+ if (! VALID_REGISTER_P (ADDREGNAMES_REGNO (regno)))
+ sh_additional_register_names[regno][0] = '\0';
+
+ if (flag_pic && ! TARGET_PREFERGOT)
+ flag_no_function_cse = 1;
+
+ if (targetm.small_register_classes_for_mode_p (VOIDmode))
+ {
+ /* Never run scheduling before reload, since that can
+ break global alloc, and generates slower code anyway due
+ to the pressure on R0. */
+ /* Enable sched1 for SH4 if the user explicitly requests.
+ When sched1 is enabled, the ready queue will be reordered by
+ the target hooks if pressure is high. We cannot do this for
+ PIC, SH3 and lower as they give spill failures for R0. */
+ if (!TARGET_HARD_SH4 || flag_pic)
+ flag_schedule_insns = 0;
+ /* ??? Current exception handling places basic block boundaries
+ after call_insns. It causes the high pressure on R0 and gives
+ spill failures for R0 in reload. See PR 22553 and the thread
+ on gcc-patches
+ <http://gcc.gnu.org/ml/gcc-patches/2005-10/msg00816.html>. */
+ else if (flag_exceptions)
+ {
+ if (flag_schedule_insns && OPTION_SET_P (flag_schedule_insns))
+ warning (0, "ignoring %<-fschedule-insns%> because of exception "
+ "handling bug");
+ flag_schedule_insns = 0;
+ }
+ else if (flag_schedule_insns
+ && !OPTION_SET_P (flag_schedule_insns))
+ flag_schedule_insns = 0;
+ }
+
+ /* Unwind info is not correct around the CFG unless either a frame
+ pointer is present or M_A_O_A is set. Fixing this requires rewriting
+ unwind info generation to be aware of the CFG and propagating states
+ around edges. */
+ if ((flag_unwind_tables || flag_asynchronous_unwind_tables
+ || flag_exceptions || flag_non_call_exceptions)
+ && flag_omit_frame_pointer && !TARGET_ACCUMULATE_OUTGOING_ARGS)
+ {
+ warning (0, "unwind tables currently require either a frame pointer "
+ "or %<-maccumulate-outgoing-args%> for correctness");
+ TARGET_ACCUMULATE_OUTGOING_ARGS = 1;
+ }
+
+ if (flag_unsafe_math_optimizations)
+ {
+ /* Enable fsca insn for SH4A if not otherwise specified by the user. */
+ if (OPTION_SET_P (TARGET_FSCA) == 0
+ && (TARGET_SH4A_FP || TARGET_FPU_SH4_300))
+ TARGET_FSCA = 1;
+
+ /* Enable fsrra insn for SH4A if not otherwise specified by the user. */
+ if (OPTION_SET_P (TARGET_FSRRA) == 0
+ && (TARGET_SH4A_FP || TARGET_FPU_SH4_300))
+ TARGET_FSRRA = 1;
+ }
+
+ /* Allow fsrra insn only if -funsafe-math-optimizations and
+ -ffinite-math-only is enabled. */
+ TARGET_FSRRA = TARGET_FSRRA
+ && flag_unsafe_math_optimizations
+ && flag_finite_math_only;
+
+ /* If the -mieee option was not explicitly set by the user, turn it on
+ unless -ffinite-math-only was specified. See also PR 33135. */
+ if (! OPTION_SET_P (TARGET_IEEE))
+ TARGET_IEEE = ! flag_finite_math_only;
+
+ if (sh_fixed_range_str)
+ sh_fix_range (sh_fixed_range_str);
+
+ /* This target defaults to strict volatile bitfields. */
+ if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
+ flag_strict_volatile_bitfields = 1;
+
+ sh_override_options_after_change ();
+
+ /* Parse atomic model option and make sure it is valid for the current
+ target CPU. */
+ selected_atomic_model_
+ = parse_validate_atomic_model_option (sh_atomic_model_str);
+
+ register_sh_passes ();
+}
+
+/* Implement targetm.override_options_after_change. */
+
+static void
+sh_override_options_after_change (void)
+{
+ /* Adjust loop, jump and function alignment values (in bytes), if those
+ were not specified by the user using -falign-loops, -falign-jumps
+ and -falign-functions options.
+ 32 bit alignment is better for speed, because instructions can be
+ fetched as a pair from a longword boundary. For size use 16 bit
+ alignment to get more compact code.
+ Aligning all jumps increases the code size, even if it might
+ result in slightly faster code. Thus, it is set to the smallest
+ alignment possible if not specified by the user. */
+ if (flag_align_loops && !str_align_loops)
+ str_align_loops = optimize_size ? "2" : "4";
+
+ /* Parse values so that we can compare for current value. */
+ parse_alignment_opts ();
+ if (flag_align_jumps && !str_align_jumps)
+ str_align_jumps = "2";
+ else if (align_jumps.levels[0].get_value () < 2)
+ str_align_jumps = "2";
+
+ if (flag_align_functions && !str_align_functions)
+ str_align_functions = optimize_size ? "2" : "4";
+
+ /* The linker relaxation code breaks when a function contains
+ alignments that are larger than that at the start of a
+ compilation unit. */
+ if (TARGET_RELAX)
+ {
+ /* Parse values so that we can compare for current value. */
+ parse_alignment_opts ();
+ int min_align = MAX (align_loops.levels[0].get_value (),
+ align_jumps.levels[0].get_value ());
+
+ /* Also take possible .long constants / mova tables into account. */
+ if (min_align < 4)
+ min_align = 4;
+ if (align_functions.levels[0].get_value () < min_align)
+ {
+ char *r = XNEWVEC (char, 16);
+ sprintf (r, "%d", min_align);
+ str_align_functions = r;
+ }
+ }
+}
+
+/* Print the operand address in x to the stream. */
+static void
+sh_print_operand_address (FILE *stream, machine_mode /*mode*/, rtx x)
+{
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ fprintf (stream, "@%s", reg_names[true_regnum (x)]);
+ break;
+
+ case PLUS:
+ {
+ rtx base = XEXP (x, 0);
+ rtx index = XEXP (x, 1);
+
+ switch (GET_CODE (index))
+ {
+ case CONST_INT:
+ fprintf (stream, "@(%d,%s)", (int) INTVAL (index),
+ reg_names[true_regnum (base)]);
+ break;
+
+ case REG:
+ case SUBREG:
+ {
+ int base_num = true_regnum (base);
+ int index_num = true_regnum (index);
+
+ /* If base or index is R0, make sure that it comes first.
+ Usually one of them will be R0, but the order might be wrong.
+ If neither base nor index are R0 it's an error and we just
+ pass it on to the assembler. This avoids silent wrong code
+ bugs. */
+ if (base_num == 0 && index_num != 0)
+ std::swap (base_num, index_num);
+
+ fprintf (stream, "@(%s,%s)", reg_names[index_num],
+ reg_names[base_num]);
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ break;
+
+ case PRE_DEC:
+ fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]);
+ break;
+
+ case POST_INC:
+ fprintf (stream, "@%s+", reg_names[true_regnum (XEXP (x, 0))]);
+ break;
+
+ default:
+ x = mark_constant_pool_use (x);
+ output_addr_const (stream, x);
+ break;
+ }
+}
+
+/* Print operand x (an rtx) in assembler syntax to file stream
+ according to modifier code.
+
+ '.' print a .s if insn needs delay slot
+ ',' print LOCAL_LABEL_PREFIX
+ '@' print trap, rte or rts depending upon pragma interruptness
+ '#' output a nop if there is nothing to put in the delay slot
+ ''' print likelihood suffix (/u for unlikely).
+ '>' print branch target if -fverbose-asm
+ 'O' print a constant without the #
+ 'R' print the LSW of a dp value - changes if in little endian
+ 'S' print the MSW of a dp value - changes if in little endian
+ 'T' print the next word of a dp value - same as 'R' in big endian mode.
+ 'M' print .b / .w / .l / .s / .d suffix if operand is a MEM.
+ 'N' print 'r63' if the operand is (const_int 0).
+ 'd' print a V2SF reg as dN instead of fpN.
+ 'm' print a pair `base,offset' or `base,index', for LD and ST.
+ 'U' Likewise for {LD,ST}{HI,LO}.
+ 'V' print the position of a single bit set.
+ 'W' print the position of a single bit cleared.
+ 't' print a memory address which is a register.
+ 'u' prints the lowest 16 bits of CONST_INT, as an unsigned value.
+ 'o' output an operator. */
+static void
+sh_print_operand (FILE *stream, rtx x, int code)
+{
+ int regno;
+ machine_mode mode;
+
+ switch (code)
+ {
+ tree trapa_attr;
+
+ case '.':
+ if (final_sequence
+ && ! INSN_ANNULLED_BRANCH_P (final_sequence->insn (0))
+ && get_attr_length (final_sequence->insn (1)))
+ fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s");
+ break;
+ case ',':
+ fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
+ break;
+ case '@':
+ trapa_attr = lookup_attribute ("trap_exit",
+ DECL_ATTRIBUTES (current_function_decl));
+ if (trapa_attr)
+ fprintf (stream, "trapa #%ld",
+ (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr))));
+ else if (sh_cfun_interrupt_handler_p ())
+ {
+ if (sh_cfun_resbank_handler_p ())
+ fprintf (stream, "resbank\n");
+ fprintf (stream, "rte");
+ }
+ else
+ fprintf (stream, "rts");
+ break;
+ case '#':
+ /* Output a nop if there's nothing in the delay slot. */
+ if (dbr_sequence_length () == 0)
+ fprintf (stream, "\n\tnop");
+ break;
+ case '\'':
+ {
+ rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
+
+ if (note
+ && profile_probability::from_reg_br_prob_note (XINT (note, 0))
+ < profile_probability::even ())
+ fputs ("/u", stream);
+ break;
+ }
+ case '>':
+ if (flag_verbose_asm && JUMP_LABEL (current_output_insn))
+ {
+ fputs ("\t! target: ", stream);
+ output_addr_const (stream, JUMP_LABEL (current_output_insn));
+ }
+ break;
+ case 'O':
+ x = mark_constant_pool_use (x);
+ output_addr_const (stream, x);
+ break;
+ /* N.B.: %R / %S / %T adjust memory addresses by four.
+ While they can be used to access 64 bit parts of a larger value
+ held in general purpose registers, that won't work with memory -
+ neither for fp registers, since the frxx names are used. */
+ case 'R':
+ if (REG_P (x) || GET_CODE (x) == SUBREG)
+ {
+ regno = true_regnum (x);
+ regno += FP_REGISTER_P (regno) ? 1 : SH_REG_LSW_OFFSET;
+ fputs (reg_names[regno], (stream));
+ }
+ else if (MEM_P (x))
+ {
+ x = adjust_address (x, SImode, 4 * SH_REG_LSW_OFFSET);
+ sh_print_operand_address (stream, GET_MODE (x), XEXP (x, 0));
+ }
+ else
+ {
+ rtx sub = NULL_RTX;
+
+ mode = GET_MODE (x);
+ if (mode == VOIDmode)
+ mode = DImode;
+ if (GET_MODE_SIZE (mode) >= 8)
+ sub = simplify_subreg (SImode, x, mode, 4 * SH_REG_LSW_OFFSET);
+ if (sub)
+ sh_print_operand (stream, sub, 0);
+ else
+ output_operand_lossage ("invalid operand to %%R");
+ }
+ break;
+ case 'S':
+ if (REG_P (x) || GET_CODE (x) == SUBREG)
+ {
+ regno = true_regnum (x);
+ regno += FP_REGISTER_P (regno) ? 0 : SH_REG_MSW_OFFSET;
+ fputs (reg_names[regno], (stream));
+ }
+ else if (MEM_P (x))
+ {
+ x = adjust_address (x, SImode, 4 * SH_REG_MSW_OFFSET);
+ sh_print_operand_address (stream, GET_MODE (x), XEXP (x, 0));
+ }
+ else
+ {
+ rtx sub = NULL_RTX;
+
+ mode = GET_MODE (x);
+ if (mode == VOIDmode)
+ mode = DImode;
+ if (GET_MODE_SIZE (mode) >= 8)
+ sub = simplify_subreg (SImode, x, mode, 4 * SH_REG_MSW_OFFSET);
+ if (sub)
+ sh_print_operand (stream, sub, 0);
+ else
+ output_operand_lossage ("invalid operand to %%S");
+ }
+ break;
+ case 'T':
+ /* Next word of a double. */
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fputs (reg_names[REGNO (x) + 1], (stream));
+ break;
+ case MEM:
+ {
+ machine_mode mode = GET_MODE (x);
+ if (GET_CODE (XEXP (x, 0)) != PRE_DEC
+ && GET_CODE (XEXP (x, 0)) != POST_INC)
+ x = adjust_address (x, SImode, 4);
+ sh_print_operand_address (stream, mode, XEXP (x, 0));
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 't':
+ gcc_assert (MEM_P (x));
+ x = XEXP (x, 0);
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ sh_print_operand (stream, x, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 'o':
+ switch (GET_CODE (x))
+ {
+ case PLUS: fputs ("add", stream); break;
+ case MINUS: fputs ("sub", stream); break;
+ case MULT: fputs ("mul", stream); break;
+ case DIV: fputs ("div", stream); break;
+ case EQ: fputs ("eq", stream); break;
+ case NE: fputs ("ne", stream); break;
+ case GT: case LT: fputs ("gt", stream); break;
+ case GE: case LE: fputs ("ge", stream); break;
+ case GTU: case LTU: fputs ("gtu", stream); break;
+ case GEU: case LEU: fputs ("geu", stream); break;
+ default:
+ break;
+ }
+ break;
+ case 'M':
+ if (MEM_P (x))
+ {
+ switch (GET_MODE (x))
+ {
+ case E_QImode: fputs (".b", stream); break;
+ case E_HImode: fputs (".w", stream); break;
+ case E_SImode: fputs (".l", stream); break;
+ case E_SFmode: fputs (".s", stream); break;
+ case E_DFmode: fputs (".d", stream); break;
+ default: gcc_unreachable ();
+ }
+ }
+ break;
+
+ case 'm':
+ gcc_assert (MEM_P (x));
+ x = XEXP (x, 0);
+ /* Fall through. */
+ case 'U':
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ sh_print_operand (stream, x, 0);
+ fputs (", 0", stream);
+ break;
+
+ case PLUS:
+ sh_print_operand (stream, XEXP (x, 0), 0);
+ fputs (", ", stream);
+ sh_print_operand (stream, XEXP (x, 1), 0);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 'V':
+ {
+ int num = exact_log2 (INTVAL (x));
+ gcc_assert (num >= 0);
+ fprintf (stream, "#%d", num);
+ }
+ break;
+
+ case 'W':
+ {
+ int num = exact_log2 (~INTVAL (x));
+ gcc_assert (num >= 0);
+ fprintf (stream, "#%d", num);
+ }
+ break;
+
+ case 'd':
+ gcc_assert (REG_P (x) && GET_MODE (x) == V2SFmode);
+
+ fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
+ break;
+
+ case 'N':
+ if (x == CONST0_RTX (GET_MODE (x)))
+ {
+ fprintf ((stream), "r63");
+ break;
+ }
+ goto default_output;
+ case 'u':
+ if (CONST_INT_P (x))
+ {
+ fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
+ break;
+ }
+ /* Fall through. */
+
+ default_output:
+ default:
+ regno = 0;
+ mode = GET_MODE (x);
+
+ switch (GET_CODE (x))
+ {
+ case TRUNCATE:
+ {
+ rtx inner = XEXP (x, 0);
+ int offset = 0;
+ machine_mode inner_mode;
+
+ /* We might see SUBREGs with vector mode registers inside. */
+ if (GET_CODE (inner) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (inner))
+ == GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+ && subreg_lowpart_p (inner))
+ inner = SUBREG_REG (inner);
+ if (CONST_INT_P (inner))
+ {
+ x = GEN_INT (trunc_int_for_mode (INTVAL (inner), GET_MODE (x)));
+ goto default_output;
+ }
+ inner_mode = GET_MODE (inner);
+ if (GET_CODE (inner) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (inner))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+ && REG_P (SUBREG_REG (inner)))
+ {
+ offset = subreg_regno_offset (REGNO (SUBREG_REG (inner)),
+ GET_MODE (SUBREG_REG (inner)),
+ SUBREG_BYTE (inner),
+ GET_MODE (inner));
+ inner = SUBREG_REG (inner);
+ }
+ if (!REG_P (inner) || GET_MODE_SIZE (inner_mode) > 8)
+ abort ();
+ /* Floating point register pairs are always big endian;
+ general purpose registers are 64 bit wide. */
+ regno = REGNO (inner);
+ regno = (hard_regno_nregs (regno, inner_mode)
+ - hard_regno_nregs (regno, mode))
+ + offset;
+ x = inner;
+ goto reg;
+ }
+ case SIGN_EXTEND:
+ x = XEXP (x, 0);
+ goto reg;
+ case SUBREG:
+ gcc_assert (SUBREG_BYTE (x) == 0
+ && REG_P (SUBREG_REG (x)));
+
+ x = SUBREG_REG (x);
+ /* Fall through. */
+
+ reg:
+ case REG:
+ regno += REGNO (x);
+ if (FP_REGISTER_P (regno)
+ && mode == V16SFmode)
+ fprintf ((stream), "mtrx%s", reg_names[regno] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && mode == V4SFmode)
+ fprintf ((stream), "fv%s", reg_names[regno] + 2);
+ else if (REG_P (x)
+ && mode == V2SFmode)
+ fprintf ((stream), "fp%s", reg_names[regno] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && GET_MODE_SIZE (mode) > 4)
+ fprintf ((stream), "d%s", reg_names[regno] + 1);
+ else
+ fputs (reg_names[regno], (stream));
+ break;
+
+ case MEM:
+ output_address (GET_MODE (x), XEXP (x, 0));
+ break;
+
+ default:
+ fputc ('#', stream);
+ output_addr_const (stream, x);
+ break;
+ }
+ break;
+ }
+}
+
+static bool
+sh_print_operand_punct_valid_p (unsigned char code)
+{
+ return (code == '.' || code == '#' || code == '@' || code == ','
+ || code == '$' || code == '\'' || code == '>');
+}
+
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
+static bool
+sh_asm_output_addr_const_extra (FILE *file, rtx x)
+{
+ if (GET_CODE (x) == UNSPEC)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_PIC:
+ /* GLOBAL_OFFSET_TABLE or local symbols, no suffix. */
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ break;
+ case UNSPEC_GOT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOT", file);
+ break;
+ case UNSPEC_GOTOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTOFF", file);
+ break;
+ case UNSPEC_PLT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@PLT", file);
+ break;
+ case UNSPEC_GOTPLT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTPLT", file);
+ break;
+ case UNSPEC_PCREL:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@PCREL", file);
+ break;
+ case UNSPEC_DTPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@DTPOFF", file);
+ break;
+ case UNSPEC_GOTTPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTTPOFF", file);
+ break;
+ case UNSPEC_TPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@TPOFF", file);
+ break;
+ case UNSPEC_CALLER:
+ {
+ char name[32];
+ /* LPCS stands for Label for PIC Call Site. */
+ targetm.asm_out.generate_internal_label (name, "LPCS",
+ INTVAL (XVECEXP (x, 0, 0)));
+ assemble_name (file, name);
+ }
+ break;
+ case UNSPEC_SYMOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputc ('-', file);
+ if (GET_CODE (XVECEXP (x, 0, 1)) == CONST)
+ {
+ fputc ('(', file);
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ fputc (')', file);
+ }
+ else
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ break;
+ case UNSPEC_PCREL_SYMOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("-(", file);
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ fputs ("-.)", file);
+ break;
+ case UNSPEC_GOTFUNCDESC:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTFUNCDESC", file);
+ break;
+ case UNSPEC_GOTOFFFUNCDESC:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTOFFFUNCDESC", file);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Encode symbol attributes of a SYMBOL_REF into its
+ SYMBOL_REF_FLAGS. */
+static void
+sh_encode_section_info (tree decl, rtx rtl, int first)
+{
+ default_encode_section_info (decl, rtl, first);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && sh2a_function_vector_p (decl) && TARGET_SH2A)
+ SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_FUNCVEC_FUNCTION;
+}
+
+/* Prepare operands for a move define_expand; specifically, one of the
+ operands must be in a register. */
+void
+prepare_move_operands (rtx operands[], machine_mode mode)
+{
+ if ((mode == SImode || mode == DImode)
+ && flag_pic
+ && ! ((mode == Pmode || mode == ptr_mode)
+ && tls_symbolic_operand (operands[1], Pmode) != TLS_MODEL_NONE))
+ {
+ rtx temp;
+ if (SYMBOLIC_CONST_P (operands[1]))
+ {
+ if (MEM_P (operands[0]))
+ operands[1] = force_reg (Pmode, operands[1]);
+ else
+ {
+ temp = (!can_create_pseudo_p ()
+ ? operands[0]
+ : gen_reg_rtx (Pmode));
+ operands[1] = legitimize_pic_address (operands[1], mode, temp);
+ }
+ }
+ else if (GET_CODE (operands[1]) == CONST
+ && GET_CODE (XEXP (operands[1], 0)) == PLUS
+ && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
+ {
+ temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
+ temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
+ mode, temp);
+ operands[1] = expand_binop (mode, add_optab, temp,
+ XEXP (XEXP (operands[1], 0), 1),
+ (!can_create_pseudo_p ()
+ ? temp
+ : gen_reg_rtx (Pmode)),
+ 0, OPTAB_LIB_WIDEN);
+ }
+ }
+
+ if (! reload_in_progress && ! reload_completed)
+ {
+ /* Copy the source to a register if both operands aren't registers. */
+ if (! register_operand (operands[0], mode)
+ && ! register_operand (operands[1], mode))
+ operands[1] = copy_to_mode_reg (mode, operands[1]);
+
+ if (MEM_P (operands[0]) && ! memory_operand (operands[0], mode))
+ {
+ /* This is like change_address_1 (operands[0], mode, 0, 1) ,
+ except that we can't use that function because it is static. */
+ rtx new_rtx = change_address (operands[0], mode, 0);
+ MEM_COPY_ATTRIBUTES (new_rtx, operands[0]);
+ operands[0] = new_rtx;
+ }
+
+ /* This case can happen while generating code to move the result
+ of a library call to the target. Reject `st r0,@(rX,rY)' because
+ reload will fail to find a spill register for rX, since r0 is already
+ being used for the source. */
+ else if (refers_to_regno_p (R0_REG, operands[1])
+ && MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == PLUS
+ && REG_P (XEXP (XEXP (operands[0], 0), 1)))
+ operands[1] = copy_to_mode_reg (mode, operands[1]);
+
+ /* When the displacement addressing is used, RA will assign r0 to
+ the pseudo register operand for the QI/HImode load/store.
+ This tends to make a long live range for R0 and might cause
+ anomalous register spills in some case with LRA. See PR
+ target/55212.
+ We split possible load/store to two move insns via r0 so as to
+ shorten R0 live range. It will make some codes worse but will
+ win on average for LRA.
+ Also when base+index addressing is used and the index term is
+ a subreg, LRA assumes that more hard registers can be available
+ in some situation. It isn't the case for SH in the problematic
+ case. We can pre-allocate R0 for that index term to avoid
+ the issue. See PR target/66591. */
+ else if (sh_lra_p ()
+ && ! TARGET_SH2A
+ && ((REG_P (operands[0]) && MEM_P (operands[1]))
+ || (REG_P (operands[1]) && MEM_P (operands[0]))))
+ {
+ bool load_p = REG_P (operands[0]);
+ rtx reg = operands[load_p ? 0 : 1];
+ rtx adr = XEXP (operands[load_p ? 1 : 0], 0);
+
+ if ((mode == QImode || mode == HImode)
+ && REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ && GET_CODE (adr) == PLUS
+ && REG_P (XEXP (adr, 0))
+ && (REGNO (XEXP (adr, 0)) >= FIRST_PSEUDO_REGISTER)
+ && CONST_INT_P (XEXP (adr, 1))
+ && INTVAL (XEXP (adr, 1)) != 0
+ && sh_legitimate_index_p (mode, XEXP (adr, 1), false, true))
+ {
+ rtx r0_rtx = gen_rtx_REG (mode, R0_REG);
+ emit_move_insn (r0_rtx, operands[1]);
+ operands[1] = r0_rtx;
+ }
+ if (REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ && GET_CODE (adr) == PLUS
+ && REG_P (XEXP (adr, 0))
+ && (REGNO (XEXP (adr, 0)) >= FIRST_PSEUDO_REGISTER)
+ && SUBREG_P (XEXP (adr, 1))
+ && REG_P (SUBREG_REG (XEXP (adr, 1))))
+ {
+ rtx r0_rtx = gen_rtx_REG (GET_MODE (XEXP (adr, 1)), R0_REG);
+ emit_move_insn (r0_rtx, XEXP (adr, 1));
+ XEXP (adr, 1) = r0_rtx;
+ }
+ }
+ }
+
+ if (mode == Pmode || mode == ptr_mode)
+ {
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ rtx opc;
+ if (GET_CODE (op1) == CONST
+ && GET_CODE (XEXP (op1, 0)) == PLUS
+ && (tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode)
+ != TLS_MODEL_NONE))
+ {
+ opc = XEXP (XEXP (op1, 0), 1);
+ op1 = XEXP (XEXP (op1, 0), 0);
+ }
+ else
+ opc = NULL_RTX;
+
+ enum tls_model tls_kind;
+
+ if (! reload_in_progress && ! reload_completed
+ && (tls_kind = tls_symbolic_operand (op1, Pmode)) != TLS_MODEL_NONE)
+ {
+ rtx tga_op1, tga_ret, tmp, tmp2;
+
+ if (! flag_pic
+ && (tls_kind == TLS_MODEL_GLOBAL_DYNAMIC
+ || tls_kind == TLS_MODEL_LOCAL_DYNAMIC
+ || tls_kind == TLS_MODEL_INITIAL_EXEC))
+ {
+ static int got_labelno;
+ /* Don't schedule insns for getting GOT address when
+ the first scheduling is enabled, to avoid spill
+ failures for R0. */
+ if (flag_schedule_insns)
+ emit_insn (gen_blockage ());
+ emit_insn (gen_GOTaddr2picreg (GEN_INT (++got_labelno)));
+ emit_use (gen_rtx_REG (SImode, PIC_REG));
+ if (flag_schedule_insns)
+ emit_insn (gen_blockage ());
+ }
+
+ switch (tls_kind)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
+ emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
+ tmp = gen_reg_rtx (Pmode);
+ emit_move_insn (tmp, tga_ret);
+ op1 = tmp;
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
+ emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
+
+ tmp = gen_reg_rtx (Pmode);
+ emit_move_insn (tmp, tga_ret);
+
+ if (register_operand (op0, Pmode))
+ tmp2 = op0;
+ else
+ tmp2 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symDTPOFF2reg (tmp2, op1, tmp));
+ op1 = tmp2;
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
+ tmp = gen_sym2GOTTPOFF (op1);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
+ emit_insn (gen_tls_initial_exec (tga_op1, tmp));
+ op1 = tga_op1;
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ tmp2 = gen_reg_rtx (Pmode);
+ emit_insn (gen_store_gbr (tmp2));
+ tmp = gen_reg_rtx (Pmode);
+ emit_insn (gen_symTPOFF2reg (tmp, op1));
+
+ if (register_operand (op0, Pmode))
+ op1 = op0;
+ else
+ op1 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_addsi3 (op1, tmp, tmp2));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ if (opc)
+ emit_insn (gen_addsi3 (op1, op1, force_reg (SImode, opc)));
+ operands[1] = op1;
+ }
+ }
+
+ if (SH_OFFSETS_MUST_BE_WITHIN_SECTIONS_P)
+ {
+ rtx base, offset;
+ split_const (operands[1], &base, &offset);
+
+ if (GET_CODE (base) == SYMBOL_REF
+ && !offset_within_block_p (base, INTVAL (offset)))
+ {
+ rtx tmp = can_create_pseudo_p () ? gen_reg_rtx (mode) : operands[0];
+ emit_move_insn (tmp, base);
+ if (!arith_operand (offset, mode))
+ offset = force_reg (mode, offset);
+ emit_insn (gen_add3_insn (operands[0], tmp, offset));
+ }
+ }
+}
+
+/* Implement the canonicalize_comparison target hook for the combine
+ pass. For the target hook this function is invoked via
+ sh_canonicalize_comparison. This function is also re-used to
+ canonicalize comparisons in cbranch pattern expanders. */
+static void
+sh_canonicalize_comparison (enum rtx_code& cmp, rtx& op0, rtx& op1,
+ machine_mode mode,
+ bool op0_preserve_value)
+{
+ /* When invoked from within the combine pass the mode is not specified,
+ so try to get it from one of the operands. */
+ if (mode == VOIDmode)
+ mode = GET_MODE (op0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (op1);
+
+ // We need to have a mode to do something useful here.
+ if (mode == VOIDmode)
+ return;
+
+ // Currently, we don't deal with floats here.
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return;
+
+ // Make sure that the constant operand is the second operand.
+ if (CONST_INT_P (op0) && !CONST_INT_P (op1))
+ {
+ if (op0_preserve_value)
+ return;
+
+ std::swap (op0, op1);
+ cmp = swap_condition (cmp);
+ }
+
+ if (CONST_INT_P (op1))
+ {
+ /* Try to adjust the constant operand in such a way that available
+ comparison insns can be utilized better and the constant can be
+ loaded with a 'mov #imm,Rm' insn. This avoids a load from the
+ constant pool. */
+ const HOST_WIDE_INT val = INTVAL (op1);
+
+ /* x > -1 --> x >= 0
+ x > 0xFFFFFF7F --> x >= 0xFFFFFF80
+ x <= -1 --> x < 0
+ x <= 0xFFFFFF7F --> x < 0xFFFFFF80 */
+ if ((val == -1 || val == -0x81) && (cmp == GT || cmp == LE))
+ {
+ cmp = cmp == GT ? GE : LT;
+ op1 = gen_int_mode (val + 1, mode);
+ }
+
+ /* x >= 1 --> x > 0
+ x >= 0x80 --> x > 0x7F
+ x < 1 --> x <= 0
+ x < 0x80 --> x <= 0x7F */
+ else if ((val == 1 || val == 0x80) && (cmp == GE || cmp == LT))
+ {
+ cmp = cmp == GE ? GT : LE;
+ op1 = gen_int_mode (val - 1, mode);
+ }
+
+ /* unsigned x >= 1 --> x != 0
+ unsigned x < 1 --> x == 0 */
+ else if (val == 1 && (cmp == GEU || cmp == LTU))
+ {
+ cmp = cmp == GEU ? NE : EQ;
+ op1 = CONST0_RTX (mode);
+ }
+
+ /* unsigned x >= 0x80 --> unsigned x > 0x7F
+ unsigned x < 0x80 --> unsigned x < 0x7F */
+ else if (val == 0x80 && (cmp == GEU || cmp == LTU))
+ {
+ cmp = cmp == GEU ? GTU : LEU;
+ op1 = gen_int_mode (val - 1, mode);
+ }
+
+ /* unsigned x > 0 --> x != 0
+ unsigned x <= 0 --> x == 0 */
+ else if (val == 0 && (cmp == GTU || cmp == LEU))
+ cmp = cmp == GTU ? NE : EQ;
+
+ /* unsigned x > 0x7FFFFFFF --> signed x < 0
+ unsigned x <= 0x7FFFFFFF --> signed x >= 0 */
+ else if (mode == SImode && (cmp == GTU || cmp == LEU)
+ && val == 0x7FFFFFFF)
+ {
+ cmp = cmp == GTU ? LT : GE;
+ op1 = const0_rtx;
+ }
+
+ /* unsigned x >= 0x80000000 --> signed x < 0
+ unsigned x < 0x80000000 --> signed x >= 0 */
+ else if (mode == SImode && (cmp == GEU || cmp == LTU)
+ && (unsigned HOST_WIDE_INT)val
+ == ((unsigned HOST_WIDE_INT)0x7FFFFFFF + 1))
+ {
+ cmp = cmp == GEU ? LT : GE;
+ op1 = const0_rtx;
+ }
+ }
+}
+
+/* This function implements the canonicalize_comparison target hook.
+ This wrapper around the internally used sh_canonicalize_comparison
+ function is needed to do the enum rtx_code <-> int conversion.
+ Target hooks cannot use enum rtx_code in its definition. */
+static void
+sh_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+ bool op0_preserve_value)
+{
+ enum rtx_code tmp_code = (enum rtx_code)*code;
+ sh_canonicalize_comparison (tmp_code, *op0, *op1,
+ VOIDmode, op0_preserve_value);
+ *code = (int)tmp_code;
+}
+
+/* This function implements the legitimate_combined_insn target hook,
+ which the combine pass uses to early reject combined insns, before
+ it tries to recog the insn and determine its cost. */
+static bool
+sh_legitimate_combined_insn (rtx_insn* insn)
+{
+ /* Reject combinations of memory loads and zero extensions, as these
+ interfere with other combine patterns such as zero extracts and bit
+ tests. The SH2A movu.{b|w} insns are formed later in the
+ 'sh_optimize_extu_exts' pass after combine/split1. */
+ rtx p = PATTERN (insn);
+ if (GET_CODE (p) == SET
+ && REG_P (XEXP (p, 0)) && GET_MODE (XEXP (p, 0)) == SImode
+ && GET_CODE (XEXP (p, 1)) == ZERO_EXTEND
+ && MEM_P (XEXP (XEXP (p, 1), 0)))
+ return false;
+
+ return true;
+}
+
+bool
+sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2)
+{
+ *p1 = T_REG;
+ *p2 = INVALID_REGNUM;
+ return true;
+}
+
+/* Try to calculate the branch distance of a conditional branch in bytes.
+
+ FIXME: Because of PR 59189 we can't use the CFG here. Instead just
+ walk from this insn into the next (fall-through) basic block and see if
+ we hit the label. */
+unsigned int
+sh_cbranch_distance (rtx_insn* _cbranch_insn, unsigned int max_dist)
+{
+ rtx_jump_insn* cbranch_insn = safe_as_a<rtx_jump_insn*> (_cbranch_insn);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "sh_cbranch_distance insn = \n");
+ print_rtl_single (dump_file, cbranch_insn);
+ }
+
+ unsigned int dist = 0;
+
+ for (rtx_insn* i = next_nonnote_insn (cbranch_insn);
+ i != NULL && dist < max_dist; i = next_nonnote_insn (i))
+ {
+ const unsigned int i_len = get_attr_length (i);
+ dist += i_len;
+
+ if (dump_file)
+ fprintf (dump_file, " insn %d length = %u dist = %u\n",
+ INSN_UID (i), i_len, dist);
+
+ if (rtx_code_label* l = dyn_cast<rtx_code_label*> (i))
+ {
+ if (l == cbranch_insn->jump_target ())
+ {
+ if (dump_file)
+ fprintf (dump_file, " cbranch dist = %u\n", dist);
+ return dist;
+ }
+ break;
+ }
+ }
+
+ if (dump_file)
+ fprintf (dump_file, " cbranch dist = unknown\n");
+
+ return unknown_cbranch_distance;
+}
+
+enum rtx_code
+prepare_cbranch_operands (rtx *operands, machine_mode mode,
+ enum rtx_code comparison)
+{
+ gcc_assert (can_create_pseudo_p ());
+
+ if (comparison == LAST_AND_UNUSED_RTX_CODE)
+ comparison = GET_CODE (operands[0]);
+
+ sh_canonicalize_comparison (comparison, operands[1], operands[2],
+ mode, false);
+
+ rtx op1 = operands[1];
+ operands[1] = force_reg (mode, op1);
+
+ /* When we are handling DImode comparisons, we want to keep constants so
+ that we can optimize the component comparisons; however, memory loads
+ are better issued as a whole so that they can be scheduled well.
+ SImode equality comparisons allow I08 constants, but only when they
+ compare r0. Hence, if operands[1] has to be loaded from somewhere else
+ into a register, that register might as well be r0, and we allow the
+ constant. If it is already in a register, this is likely to be
+ allocated to a different hard register, thus we load the constant into
+ a register unless it is zero. */
+ if (!REG_P (operands[2])
+ && (!CONST_INT_P (operands[2])
+ || (mode == SImode && operands[2] != CONST0_RTX (SImode)
+ && ((comparison != EQ && comparison != NE)
+ || (REG_P (op1) && REGNO (op1) != R0_REG)
+ || !satisfies_constraint_I08 (operands[2])))))
+ operands[2] = force_reg (mode, operands[2]);
+
+ return comparison;
+}
+
+static void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison,
+ profile_probability probability)
+{
+ rtx (*branch_expander) (rtx) = gen_branch_true;
+ comparison = prepare_cbranch_operands (operands, SImode, comparison);
+ switch (comparison)
+ {
+ case NE: case LT: case LE: case LTU: case LEU:
+ comparison = reverse_condition (comparison);
+ branch_expander = gen_branch_false;
+ default: ;
+ }
+ emit_insn (gen_rtx_SET (get_t_reg_rtx (),
+ gen_rtx_fmt_ee (comparison, SImode,
+ operands[1], operands[2])));
+ rtx_insn *jump = emit_jump_insn (branch_expander (operands[3]));
+ if (probability.initialized_p ())
+ add_reg_br_prob_note (jump, probability);
+}
+
+void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison)
+{
+ expand_cbranchsi4 (operands, comparison,
+ profile_probability::uninitialized ());
+}
+
+/* ??? How should we distribute probabilities when more than one branch
+ is generated. So far we only have some ad-hoc observations:
+ - If the operands are random, they are likely to differ in both parts.
+ - If comparing items in a hash chain, the operands are random or equal;
+ operation should be EQ or NE.
+ - If items are searched in an ordered tree from the root, we can expect
+ the highpart to be unequal about half of the time; operation should be
+ an inequality comparison, operands non-constant, and overall probability
+ about 50%. Likewise for quicksort.
+ - Range checks will be often made against constants. Even if we assume for
+ simplicity an even distribution of the non-constant operand over a
+ sub-range here, the same probability could be generated with differently
+ wide sub-ranges - as long as the ratio of the part of the subrange that
+ is before the threshold to the part that comes after the threshold stays
+ the same. Thus, we can't really tell anything here;
+ assuming random distribution is at least simple.
+ */
+bool
+expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
+{
+ enum rtx_code msw_taken, msw_skip, lsw_taken;
+ rtx_code_label *skip_label = NULL;
+ rtx op1h, op1l, op2h, op2l;
+ int num_branches;
+ profile_probability prob, rev_prob;
+ profile_probability msw_taken_prob = profile_probability::uninitialized (),
+ msw_skip_prob = profile_probability::uninitialized (),
+ lsw_taken_prob = profile_probability::uninitialized ();
+
+ comparison = prepare_cbranch_operands (operands, DImode, comparison);
+ op1h = gen_highpart_mode (SImode, DImode, operands[1]);
+ op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+ op1l = gen_lowpart (SImode, operands[1]);
+ op2l = gen_lowpart (SImode, operands[2]);
+ msw_taken = msw_skip = lsw_taken = LAST_AND_UNUSED_RTX_CODE;
+ prob = split_branch_probability;
+ rev_prob = prob.invert ();
+ switch (comparison)
+ {
+ case EQ:
+ msw_skip = NE;
+ lsw_taken = EQ;
+ if (prob.initialized_p ())
+ {
+ /* FIXME: This is not optimal. We do not really know the probability
+ that values differ by MCW only, but we should probably distribute
+ probabilities more evenly. */
+ msw_skip_prob = rev_prob;
+ lsw_taken_prob = prob > profile_probability::never ()
+ ? profile_probability::guessed_always ()
+ : profile_probability::guessed_never ();
+ }
+ break;
+ case NE:
+ msw_taken = NE;
+ msw_taken_prob = prob;
+ lsw_taken = NE;
+ lsw_taken_prob = profile_probability::guessed_never ();
+ break;
+ case GTU: case GT:
+ msw_taken = comparison;
+ if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
+ break;
+ if (comparison != GTU || op2h != CONST0_RTX (SImode))
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GTU;
+ break;
+ case GEU: case GE:
+ if (op2l == CONST0_RTX (SImode))
+ msw_taken = comparison;
+ else
+ {
+ msw_taken = comparison == GE ? GT : GTU;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GEU;
+ }
+ break;
+ case LTU: case LT:
+ msw_taken = comparison;
+ if (op2l == CONST0_RTX (SImode))
+ break;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = LTU;
+ break;
+ case LEU: case LE:
+ if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
+ msw_taken = comparison;
+ else
+ {
+ lsw_taken = LEU;
+ if (comparison == LE)
+ msw_taken = LT;
+ else if (op2h != CONST0_RTX (SImode))
+ msw_taken = LTU;
+ else
+ {
+ msw_skip = swap_condition (LTU);
+ break;
+ }
+ msw_skip = swap_condition (msw_taken);
+ }
+ break;
+ default: return false;
+ }
+ num_branches = ((msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ + (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ + (lsw_taken != LAST_AND_UNUSED_RTX_CODE));
+ if (comparison != EQ && comparison != NE && num_branches > 1)
+ {
+ if (!CONSTANT_P (operands[2])
+ && prob.initialized_p ()
+ && prob.to_reg_br_prob_base () >= (int) (REG_BR_PROB_BASE * 3 / 8U)
+ && prob.to_reg_br_prob_base () <= (int) (REG_BR_PROB_BASE * 5 / 8U))
+ {
+ msw_taken_prob = prob.apply_scale (1, 2);
+ msw_skip_prob = rev_prob.apply_scale (REG_BR_PROB_BASE,
+ rev_prob.to_reg_br_prob_base ()
+ + REG_BR_PROB_BASE);
+ lsw_taken_prob = prob;
+ }
+ else
+ {
+ msw_taken_prob = prob;
+ msw_skip_prob = profile_probability::guessed_always ();
+ /* ??? If we have a constant op2h, should we use that when
+ calculating lsw_taken_prob? */
+ lsw_taken_prob = prob;
+ }
+ }
+ operands[1] = op1h;
+ operands[2] = op2h;
+
+ if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
+ if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ {
+ rtx taken_label = operands[3];
+
+ /* Operands were possibly modified, but msw_skip doesn't expect this.
+ Always use the original ones. */
+ if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ {
+ operands[1] = op1h;
+ operands[2] = op2h;
+ }
+
+ operands[3] = skip_label = gen_label_rtx ();
+ expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
+ operands[3] = taken_label;
+ }
+ operands[1] = op1l;
+ operands[2] = op2l;
+ if (lsw_taken != LAST_AND_UNUSED_RTX_CODE)
+ expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
+ if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ emit_label (skip_label);
+ return true;
+}
+
+/* Given an operand, return 1 if the evaluated operand plugged into an
+ if_then_else will result in a branch_true, 0 if branch_false, or
+ -1 if neither nor applies. The truth table goes like this:
+
+ op | cmpval | code | result
+ ---------+--------+---------+--------------------
+ T (0) | 0 | EQ (1) | 0 = 0 ^ (0 == 1)
+ T (0) | 1 | EQ (1) | 1 = 0 ^ (1 == 1)
+ T (0) | 0 | NE (0) | 1 = 0 ^ (0 == 0)
+ T (0) | 1 | NE (0) | 0 = 0 ^ (1 == 0)
+ !T (1) | 0 | EQ (1) | 1 = 1 ^ (0 == 1)
+ !T (1) | 1 | EQ (1) | 0 = 1 ^ (1 == 1)
+ !T (1) | 0 | NE (0) | 0 = 1 ^ (0 == 0)
+ !T (1) | 1 | NE (0) | 1 = 1 ^ (1 == 0) */
+int
+sh_eval_treg_value (rtx op)
+{
+ if (t_reg_operand (op, GET_MODE (op)))
+ return 1;
+ if (negt_reg_operand (op, GET_MODE (op)))
+ return 0;
+
+ rtx_code code = GET_CODE (op);
+ if ((code != EQ && code != NE) || !CONST_INT_P (XEXP (op, 1)))
+ return -1;
+
+ int cmpop = code == EQ ? 1 : 0;
+ int cmpval = INTVAL (XEXP (op, 1));
+ if (cmpval != 0 && cmpval != 1)
+ return -1;
+
+ int t;
+ if (t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
+ t = 0;
+ else if (negt_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
+ t = 1;
+ else
+ return -1;
+
+ return t ^ (cmpval == cmpop);
+}
+
+/* Emit INSN, possibly in a PARALLEL with an USE/CLOBBER of FPSCR bits in case
+ of floating-point comparisons. */
+static void
+sh_emit_set_t_insn (rtx insn, machine_mode mode)
+{
+ if (TARGET_FPU_ANY && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_CODE (insn) != PARALLEL)
+ {
+ insn = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (3, insn,
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, FPSCR_STAT_REG)),
+ gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, FPSCR_MODES_REG))));
+ }
+ emit_insn (insn);
+}
+
+/* Prepare the operands for an scc instruction; make sure that the
+ compare has been done and the result is in T_REG. */
+void
+sh_emit_scc_to_t (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx t_reg = get_t_reg_rtx ();
+ enum rtx_code oldcode = code;
+
+ /* First need a compare insn. */
+ switch (code)
+ {
+ case NE:
+ /* It isn't possible to handle this case. */
+ gcc_unreachable ();
+ case LT:
+ code = GT;
+ break;
+ case LE:
+ code = GE;
+ break;
+ case LTU:
+ code = GTU;
+ break;
+ case LEU:
+ code = GEU;
+ break;
+ default:
+ break;
+ }
+ if (code != oldcode)
+ std::swap (op0, op1);
+
+ machine_mode mode = GET_MODE (op0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (op1);
+
+ op0 = force_reg (mode, op0);
+ if ((code != EQ && code != NE
+ && (op1 != const0_rtx
+ || code == GTU || code == GEU || code == LTU || code == LEU))
+ || (mode == DImode && op1 != const0_rtx)
+ || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
+ op1 = force_reg (mode, op1);
+
+ sh_emit_set_t_insn (gen_rtx_SET (t_reg,
+ gen_rtx_fmt_ee (code, SImode, op0, op1)),
+ mode);
+}
+
+/* Called from the md file, set up the operands of a compare instruction. */
+void
+sh_emit_compare_and_branch (rtx *operands, machine_mode mode)
+{
+ enum rtx_code code = GET_CODE (operands[0]);
+ enum rtx_code branch_code;
+ rtx op0 = operands[1];
+ rtx op1 = operands[2];
+ rtx insn;
+ bool need_ccmpeq = false;
+
+ if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ op0 = force_reg (mode, op0);
+ op1 = force_reg (mode, op1);
+ }
+ else
+ {
+ if (code != EQ || mode == DImode)
+ {
+ /* Force args into regs, since we can't use constants here. */
+ op0 = force_reg (mode, op0);
+ if (op1 != const0_rtx || code == GTU || code == GEU)
+ op1 = force_reg (mode, op1);
+ }
+ }
+
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ if (code == LT
+ || (code == LE && TARGET_IEEE && TARGET_SH2E)
+ || (code == GE && !(TARGET_IEEE && TARGET_SH2E)))
+ {
+ std::swap (op0, op1);
+ code = swap_condition (code);
+ }
+
+ /* GE becomes fcmp/gt+fcmp/eq, for SH2E and TARGET_IEEE only. */
+ if (code == GE)
+ {
+ gcc_assert (TARGET_IEEE && TARGET_SH2E);
+ need_ccmpeq = true;
+ code = GT;
+ }
+
+ /* Now we can have EQ, NE, GT, LE. NE and LE are then transformed
+ to EQ/GT respectively. */
+ gcc_assert (code == EQ || code == GT || code == NE || code == LE);
+ }
+
+ switch (code)
+ {
+ case EQ:
+ case GT:
+ case GE:
+ case GTU:
+ case GEU:
+ branch_code = code;
+ break;
+ case NE:
+ case LT:
+ case LE:
+ case LTU:
+ case LEU:
+ branch_code = reverse_condition (code);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ insn = gen_rtx_SET (get_t_reg_rtx (),
+ gen_rtx_fmt_ee (branch_code, SImode, op0, op1));
+
+ sh_emit_set_t_insn (insn, mode);
+ if (need_ccmpeq)
+ sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
+
+ if (branch_code == code)
+ emit_jump_insn (gen_branch_true (operands[3]));
+ else
+ emit_jump_insn (gen_branch_false (operands[3]));
+}
+
+void
+sh_emit_compare_and_set (rtx *operands, machine_mode mode)
+{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx op0 = operands[2];
+ rtx op1 = operands[3];
+ rtx_code_label *lab = NULL;
+ bool invert = false;
+
+ op0 = force_reg (mode, op0);
+ if ((code != EQ && code != NE
+ && (op1 != const0_rtx
+ || code == GTU || code == GEU || code == LTU || code == LEU))
+ || (mode == DImode && op1 != const0_rtx)
+ || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
+ op1 = force_reg (mode, op1);
+
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ if (code == LT || code == LE)
+ {
+ std::swap (op0, op1);
+ code = swap_condition (code);
+ }
+ if (code == GE)
+ {
+ if (TARGET_IEEE)
+ {
+ lab = gen_label_rtx ();
+ sh_emit_scc_to_t (EQ, op0, op1);
+ emit_jump_insn (gen_branch_true (lab));
+ code = GT;
+ }
+ else
+ {
+ code = LT;
+ invert = true;
+ }
+ }
+ }
+
+ if (code == NE)
+ {
+ code = EQ;
+ invert = true;
+ }
+
+ sh_emit_scc_to_t (code, op0, op1);
+ if (lab)
+ emit_label (lab);
+ if (invert)
+ emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ()));
+ else
+ emit_move_insn (operands[0], get_t_reg_rtx ());
+}
+
+/* Functions to output assembly code. */
+
+/* Return a sequence of instructions to perform DI or DF move.
+
+ Since the SH cannot move a DI or DF in one instruction, we have
+ to take care when we see overlapping source and dest registers. */
+const char *
+output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
+ machine_mode mode)
+{
+ rtx dst = operands[0];
+ rtx src = operands[1];
+
+ if (MEM_P (dst)
+ && GET_CODE (XEXP (dst, 0)) == PRE_DEC)
+ return "mov.l %T1,%0" "\n"
+ " mov.l %1,%0";
+
+ if (register_operand (dst, mode)
+ && register_operand (src, mode))
+ {
+ if (REGNO (src) == MACH_REG)
+ return "sts mach,%S0" "\n"
+ " sts macl,%R0";
+
+ /* When mov.d r1,r2 do r2->r3 then r1->r2;
+ when mov.d r1,r0 do r1->r0 then r2->r1. */
+ if (REGNO (src) + 1 == REGNO (dst))
+ return "mov %T1,%T0" "\n"
+ " mov %1,%0";
+ else
+ return "mov %1,%0" "\n"
+ " mov %T1,%T0";
+ }
+ else if (CONST_INT_P (src))
+ {
+ if (INTVAL (src) < 0)
+ output_asm_insn ("mov #-1,%S0", operands);
+ else
+ output_asm_insn ("mov #0,%S0", operands);
+
+ return "mov %1,%R0";
+ }
+ else if (MEM_P (src))
+ {
+ int ptrreg = -1;
+ int dreg = REGNO (dst);
+ rtx inside = XEXP (src, 0);
+
+ switch (GET_CODE (inside))
+ {
+ case REG:
+ ptrreg = REGNO (inside);
+ break;
+
+ case SUBREG:
+ ptrreg = subreg_regno (inside);
+ break;
+
+ case PLUS:
+ ptrreg = REGNO (XEXP (inside, 0));
+ /* ??? A r0+REG address shouldn't be possible here, because it isn't
+ an offsettable address. Unfortunately, offsettable addresses use
+ QImode to check the offset, and a QImode offsettable address
+ requires r0 for the other operand, which is not currently
+ supported, so we can't use the 'o' constraint.
+ Thus we must check for and handle r0+REG addresses here.
+ We punt for now, since this is likely very rare. */
+ gcc_assert (!REG_P (XEXP (inside, 1)));
+ break;
+
+ case LABEL_REF:
+ return "mov.l %1,%0" "\n"
+ " mov.l %1+4,%T0";
+ case POST_INC:
+ return "mov.l %1,%0" "\n"
+ " mov.l %1,%T0";
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Work out the safe way to copy. Copy into the second half first. */
+ if (dreg == ptrreg)
+ return "mov.l %T1,%T0" "\n"
+ " mov.l %1,%0";
+ }
+
+ return "mov.l %1,%0" "\n"
+ " mov.l %T1,%T0";
+}
+
+/* Print an instruction which would have gone into a delay slot after
+ another instruction, but couldn't because the other instruction expanded
+ into a sequence where putting the slot insn at the end wouldn't work. */
+static void
+print_slot (rtx_sequence *seq)
+{
+ final_scan_insn (seq->insn (1), asm_out_file, optimize, 1, NULL);
+
+ seq->insn (1)->set_deleted ();
+}
+
+const char *
+output_far_jump (rtx_insn *insn, rtx op)
+{
+ struct { rtx lab, reg, op; } this_jmp;
+ rtx_code_label *braf_base_lab = NULL;
+ const char *jump;
+ int far;
+ int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
+ rtx_insn *prev;
+
+ this_jmp.lab = gen_label_rtx ();
+
+ if (TARGET_SH2
+ && offset >= -32764
+ && offset - get_attr_length (insn) <= 32766
+ && ! CROSSING_JUMP_P (insn))
+ {
+ far = 0;
+ jump = "mov.w %O0,%1" "\n"
+ " braf %1";
+ }
+ else
+ {
+ far = 1;
+ if (flag_pic)
+ {
+ if (TARGET_SH2)
+ jump = "mov.l %O0,%1" "\n"
+ " braf %1";
+ else
+ jump = "mov.l r0,@-r15" "\n"
+ " mova %O0,r0" "\n"
+ " mov.l @r0,%1" "\n"
+ " add r0,%1" "\n"
+ " mov.l @r15+,r0" "\n"
+ " jmp @%1";
+ }
+ else
+ jump = "mov.l %O0,%1" "\n"
+ " jmp @%1";
+ }
+ /* If we have a scratch register available, use it. */
+ if (NONJUMP_INSN_P ((prev = prev_nonnote_insn (insn)))
+ && INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
+ {
+ this_jmp.reg = SET_DEST (XVECEXP (PATTERN (prev), 0, 0));
+ if (REGNO (this_jmp.reg) == R0_REG && flag_pic && ! TARGET_SH2)
+ jump = "mov.l r1,@-r15" "\n"
+ " mova %O0,r0" "\n"
+ " mov.l @r0,r1" "\n"
+ " add r1,r0" "\n"
+ " mov.l @r15+,r1" "\n"
+ " jmp @%1";
+ output_asm_insn (jump, &this_jmp.lab);
+ if (dbr_sequence_length ())
+ print_slot (final_sequence);
+ else
+ output_asm_insn ("nop", 0);
+ }
+ else
+ {
+ /* Output the delay slot insn first if any. */
+ if (dbr_sequence_length ())
+ print_slot (final_sequence);
+
+ this_jmp.reg = gen_rtx_REG (SImode, 13);
+ output_asm_insn ("mov.l r13,@-r15", 0);
+ output_asm_insn (jump, &this_jmp.lab);
+ output_asm_insn ("mov.l @r15+,r13", 0);
+ }
+ if (far && flag_pic && TARGET_SH2)
+ {
+ braf_base_lab = gen_label_rtx ();
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (braf_base_lab));
+ }
+ if (far)
+ output_asm_insn (".align 2", 0);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (this_jmp.lab));
+ this_jmp.op = op;
+ if (far && flag_pic)
+ {
+ if (TARGET_SH2)
+ this_jmp.lab = braf_base_lab;
+ output_asm_insn (".long %O2-%O0", &this_jmp.lab);
+ }
+ else
+ output_asm_insn (far ? ".long %O2" : ".word %O2-%O0", &this_jmp.lab);
+ return "";
+}
+
+/* Local label counter, used for constants in the pool and inside
+ pattern branches. */
+static int lf = 100;
+
+/* Output code for ordinary branches. */
+const char *
+output_branch (int logic, rtx_insn *insn, rtx *operands)
+{
+ switch (get_attr_length (insn))
+ {
+ case 6:
+ /* This can happen if filling the delay slot has caused a forward
+ branch to exceed its range (we could reverse it, but only
+ when we know we won't overextend other branches; this should
+ best be handled by relaxation).
+ It can also happen when other condbranches hoist delay slot insn
+ from their destination, thus leading to code size increase.
+ But the branch will still be in the range -4092..+4098 bytes. */
+ if (! TARGET_RELAX)
+ {
+ int label = lf++;
+ /* The call to print_slot will clobber the operands. */
+ rtx op0 = operands[0];
+
+ /* If the instruction in the delay slot is annulled (true), then
+ there is no delay slot where we can put it now. The only safe
+ place for it is after the label. final will do that by default. */
+
+ if (final_sequence
+ && ! INSN_ANNULLED_BRANCH_P (final_sequence->insn (0))
+ && get_attr_length (final_sequence->insn (1)))
+ {
+ asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t",
+ ASSEMBLER_DIALECT ? "/" : ".", label);
+ print_slot (final_sequence);
+ }
+ else
+ asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label);
+
+ output_asm_insn ("bra\t%l0", &op0);
+ fprintf (asm_out_file, "\tnop\n");
+ (*targetm.asm_out.internal_label) (asm_out_file, "LF", label);
+
+ return "";
+ }
+ /* FALLTHRU */
+ /* When relaxing, handle this like a short branch. The linker
+ will fix it up if it still doesn't fit after relaxation. */
+ case 2:
+ return logic ? "bt%.\t%l0" : "bf%.\t%l0";
+
+ /* These are for SH2e, in which we have to account for the
+ extra nop because of the hardware bug in annulled branches. */
+ case 8:
+ if (! TARGET_RELAX)
+ {
+ int label = lf++;
+
+ gcc_assert (!final_sequence
+ || !(INSN_ANNULLED_BRANCH_P
+ (XVECEXP (final_sequence, 0, 0))));
+ asm_fprintf (asm_out_file, "b%s%ss\t%LLF%d\n",
+ logic ? "f" : "t",
+ ASSEMBLER_DIALECT ? "/" : ".", label);
+ fprintf (asm_out_file, "\tnop\n");
+ output_asm_insn ("bra\t%l0", operands);
+ fprintf (asm_out_file, "\tnop\n");
+ (*targetm.asm_out.internal_label) (asm_out_file, "LF", label);
+
+ return "";
+ }
+ /* FALLTHRU */
+ case 4:
+ {
+ char buffer[10];
+
+ sprintf (buffer, "b%s%ss\t%%l0",
+ logic ? "t" : "f",
+ ASSEMBLER_DIALECT ? "/" : ".");
+ output_asm_insn (buffer, &operands[0]);
+ return "nop";
+ }
+
+ default:
+ /* There should be no longer branches now - that would
+ indicate that something has destroyed the branches set
+ up in machine_dependent_reorg. */
+ gcc_unreachable ();
+ }
+}
+
+/* Output a code sequence for INSN using TEMPL with OPERANDS; but before,
+ fill in operands 9 as a label to the successor insn.
+ We try to use jump threading where possible.
+ IF CODE matches the comparison in the IF_THEN_ELSE of a following jump,
+ we assume the jump is taken. I.e. EQ means follow jmp and bf, NE means
+ follow jmp and bt, if the address is in range. */
+const char *
+output_branchy_insn (enum rtx_code code, const char *templ,
+ rtx_insn *insn, rtx *operands)
+{
+ rtx_insn *next_insn = NEXT_INSN (insn);
+
+ if (next_insn && JUMP_P (next_insn) && condjump_p (next_insn))
+ {
+ rtx src = SET_SRC (PATTERN (next_insn));
+ if (GET_CODE (src) == IF_THEN_ELSE && GET_CODE (XEXP (src, 0)) != code)
+ {
+ /* Following branch not taken */
+ rtx_code_label *lab = gen_label_rtx ();
+ emit_label_after (lab, next_insn);
+ INSN_ADDRESSES_NEW (lab,
+ INSN_ADDRESSES (INSN_UID (next_insn))
+ + get_attr_length (next_insn));
+ operands[9] = lab;
+ return templ;
+ }
+ else
+ {
+ int offset = (branch_dest (next_insn)
+ - INSN_ADDRESSES (INSN_UID (next_insn)) + 4);
+ if (offset >= -252 && offset <= 258)
+ {
+ if (GET_CODE (src) == IF_THEN_ELSE)
+ /* branch_true */
+ src = XEXP (src, 1);
+ operands[9] = src;
+ return templ;
+ }
+ }
+ }
+ rtx_code_label *lab = gen_label_rtx ();
+ emit_label_after (lab, insn);
+ INSN_ADDRESSES_NEW (lab,
+ INSN_ADDRESSES (INSN_UID (insn))
+ + get_attr_length (insn));
+ operands[9] = lab;
+ return templ;
+}
+
+const char *
+output_ieee_ccmpeq (rtx_insn *insn, rtx *operands)
+{
+ return output_branchy_insn (NE, "bt %l9" "\n"
+ " fcmp/eq %1,%0",
+ insn, operands);
+}
+
+/* Output the start of the assembler file. */
+static void
+sh_file_start (void)
+{
+ default_file_start ();
+
+ if (TARGET_ELF)
+ /* We need to show the text section with the proper
+ attributes as in TEXT_SECTION_ASM_OP, before dwarf2out
+ emits it without attributes in TEXT_SECTION_ASM_OP, else GAS
+ will complain. We can teach GAS specifically about the
+ default attributes for our choice of text section, but
+ then we would have to change GAS again if/when we change
+ the text section name. */
+ fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
+ else
+ /* Switch to the data section so that the coffsem symbol
+ isn't in the text section. */
+ switch_to_section (data_section);
+
+ if (TARGET_LITTLE_ENDIAN)
+ fputs ("\t.little\n", asm_out_file);
+}
+
+/* Implementation of TARGET_ASM_INTEGER for SH. Pointers to functions
+ need to be output as pointers to function descriptors for
+ FDPIC. */
+
+static bool
+sh_assemble_integer (rtx value, unsigned int size, int aligned_p)
+{
+ if (TARGET_FDPIC && size == UNITS_PER_WORD
+ && GET_CODE (value) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (value))
+ {
+ fputs ("\t.long\t", asm_out_file);
+ output_addr_const (asm_out_file, value);
+ fputs ("@FUNCDESC\n", asm_out_file);
+ return true;
+ }
+ return default_assemble_integer (value, size, aligned_p);
+}
+
+/* Check if PAT includes UNSPEC_CALLER unspec pattern. */
+static bool
+unspec_caller_rtx_p (rtx pat)
+{
+ rtx base, offset;
+ split_const (pat, &base, &offset);
+
+ if (GET_CODE (base) == UNSPEC)
+ {
+ if (XINT (base, 1) == UNSPEC_CALLER)
+ return true;
+ for (int i = 0; i < XVECLEN (base, 0); i++)
+ if (unspec_caller_rtx_p (XVECEXP (base, 0, i)))
+ return true;
+ }
+ return false;
+}
+
+/* Indicate that INSN cannot be duplicated. This is true for insn
+ that generates a unique label. */
+static bool
+sh_cannot_copy_insn_p (rtx_insn *insn)
+{
+ if (!reload_completed || !flag_pic)
+ return false;
+
+ if (!NONJUMP_INSN_P (insn))
+ return false;
+ if (asm_noperands (insn) >= 0)
+ return false;
+
+ rtx pat = PATTERN (insn);
+
+ if (GET_CODE (pat) == CLOBBER || GET_CODE (pat) == USE)
+ return false;
+
+ if (TARGET_FDPIC && GET_CODE (pat) == PARALLEL)
+ {
+ rtx t = XVECEXP (pat, 0, XVECLEN (pat, 0) - 1);
+ if (GET_CODE (t) == USE && unspec_caller_rtx_p (XEXP (t, 0)))
+ return true;
+ }
+
+ if (GET_CODE (pat) != SET)
+ return false;
+ pat = SET_SRC (pat);
+
+ if (unspec_caller_rtx_p (pat))
+ return true;
+
+ return false;
+}
+
+/* Number of instructions used to make an arithmetic right shift by N. */
+static const char ashiftrt_insns[] =
+ { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
+
+/* Description of a logical left or right shift, when expanded to a sequence
+ of 1/2/8/16 shifts.
+ Notice that one bit right shifts clobber the T bit. One bit left shifts
+ are done with an 'add Rn,Rm' insn and thus do not clobber the T bit. */
+enum
+{
+ ASHL_CLOBBERS_T = 1 << 0,
+ LSHR_CLOBBERS_T = 1 << 1
+};
+
+struct ashl_lshr_sequence
+{
+ char insn_count;
+ signed char amount[6];
+ char clobbers_t;
+};
+
+static const struct ashl_lshr_sequence ashl_lshr_seq[32] =
+{
+ { 0, { 0 }, 0 }, // 0
+ { 1, { 1 }, LSHR_CLOBBERS_T },
+ { 1, { 2 }, 0 },
+ { 2, { 2, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 2, 2 }, 0 }, // 4
+ { 3, { 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 2, 2, 2 }, 0 },
+ { 4, { 2, 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 1, { 8 }, 0 }, // 8
+ { 2, { 8, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 8, 2 }, 0 },
+ { 3, { 8, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 8, 2, 2 }, 0 }, // 12
+ { 4, { 8, 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 8, -2, 8 }, 0 },
+ { 3, { 8, -1, 8 }, ASHL_CLOBBERS_T },
+ { 1, { 16 }, 0 }, // 16
+ { 2, { 16, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 16, 2 }, 0 },
+ { 3, { 16, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 16, 2, 2 }, 0 }, // 20
+ { 4, { 16, 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 16, -2, 8 }, 0 },
+ { 3, { 16, -1, 8 }, ASHL_CLOBBERS_T },
+ { 2, { 16, 8 }, 0 }, // 24
+ { 3, { 16, 1, 8 }, LSHR_CLOBBERS_T },
+ { 3, { 16, 8, 2 }, 0 },
+ { 4, { 16, 8, 1, 2 }, LSHR_CLOBBERS_T },
+ { 4, { 16, 8, 2, 2 }, 0 }, // 28
+ { 4, { 16, -1, -2, 16 }, ASHL_CLOBBERS_T },
+ { 3, { 16, -2, 16 }, 0 },
+
+ /* For a right shift by 31 a 2 insn shll-movt sequence can be used.
+ For a left shift by 31 a 2 insn and-rotl sequences can be used.
+ However, the shift-and combiner code needs this entry here to be in
+ terms of real shift insns. */
+ { 3, { 16, -1, 16 }, ASHL_CLOBBERS_T }
+};
+
+/* Individual shift amounts for shift amounts < 16, up to three highmost
+ bits might be clobbered. This is typically used when combined with some
+ kind of sign or zero extension. */
+static const struct ashl_lshr_sequence ext_ashl_lshr_seq[32] =
+{
+ { 0, { 0 }, 0 }, // 0
+ { 1, { 1 }, LSHR_CLOBBERS_T },
+ { 1, { 2 }, 0 },
+ { 2, { 2, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 2, 2 }, 0 }, // 4
+ { 3, { 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 2, { 8, -2 }, 0 },
+ { 2, { 8, -1 }, ASHL_CLOBBERS_T },
+ { 1, { 8 }, 0 }, // 8
+ { 2, { 8, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 8, 2 }, 0 },
+ { 3, { 8, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 8, 2, 2 }, 0 }, // 12
+ { 3, { 16, -2, -1 }, ASHL_CLOBBERS_T },
+ { 2, { 16, -2 }, 0 },
+ { 2, { 16, -1 }, ASHL_CLOBBERS_T },
+ { 1, { 16 }, 0 }, // 16
+ { 2, { 16, 1 }, LSHR_CLOBBERS_T },
+ { 2, { 16, 2 }, 0 },
+ { 3, { 16, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 16, 2, 2 }, 0 }, // 20
+ { 4, { 16, 2, 1, 2 }, LSHR_CLOBBERS_T },
+ { 3, { 16, -2, 8 }, 0 },
+ { 3, { 16, -1, 8 }, ASHL_CLOBBERS_T },
+ { 2, { 16, 8 }, 0 }, // 24
+ { 3, { 16, 1, 8 }, LSHR_CLOBBERS_T },
+ { 3, { 16, 8, 2 }, 0 },
+ { 4, { 16, 8, 1, 2 }, LSHR_CLOBBERS_T },
+ { 4, { 16, 8, 2, 2 }, 0 }, // 28
+ { 4, { 16, -1, -2, 16 }, ASHL_CLOBBERS_T },
+ { 3, { 16, -2, 16 }, 0 },
+ { 3, { 16, -1, 16 }, ASHL_CLOBBERS_T }
+};
+
+/* Return true if a shift left consisting of 1/2/8/16 shift instructions
+ will clobber the T bit. */
+bool
+sh_ashlsi_clobbers_t_reg_p (rtx shift_amount)
+{
+ gcc_assert (CONST_INT_P (shift_amount));
+
+ const int shift_amount_i = INTVAL (shift_amount) & 31;
+
+ /* Special case for shift count of 31: use and-rotl sequence. */
+ if (shift_amount_i == 31)
+ return true;
+
+ return (ashl_lshr_seq[shift_amount_i].clobbers_t
+ & ASHL_CLOBBERS_T) != 0;
+}
+
+/* Return true if a logical right shift consisting of 1/2/8/16 shift
+ instructions will clobber the T bit. */
+bool
+sh_lshrsi_clobbers_t_reg_p (rtx shift_amount)
+{
+ gcc_assert (CONST_INT_P (shift_amount));
+
+ /* For right shifts the constant might be negative. */
+ const int shift_amount_i = std::abs (INTVAL (shift_amount)) & 31;
+
+ /* Special case for shift count of 31: use shll-movt sequence. */
+ if (shift_amount_i == 31)
+ return true;
+
+ return (ashl_lshr_seq[shift_amount_i].clobbers_t
+ & LSHR_CLOBBERS_T) != 0;
+}
+
+/* Return true if it is potentially beneficial to use a dynamic shift
+ instruction (shad / shar) instead of a combination of 1/2/8/16
+ shift instructions for the specified shift count.
+ If dynamic shifts are not available, always return false. */
+bool
+sh_dynamicalize_shift_p (rtx count)
+{
+ gcc_assert (CONST_INT_P (count));
+
+ /* For right shifts the constant might be negative. */
+ const int shift_amount_i = std::abs (INTVAL (count)) & 31;
+ int insn_count;
+
+ /* For left and right shifts, there are shorter 2 insn sequences for
+ shift amounts of 31. */
+ if (shift_amount_i == 31)
+ insn_count = 2;
+ else
+ insn_count = ashl_lshr_seq[shift_amount_i].insn_count;
+
+ return TARGET_DYNSHIFT && (insn_count > 1 + SH_DYNAMIC_SHIFT_COST);
+}
+
+/* Assuming we have a value that has been sign-extended by at least one bit,
+ can we use the ext_shift_amounts with the last shift turned to an
+ arithmetic shift to shift it by N without data loss, and quicker than by
+ other means? */
+#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)
+
+/* Return the cost of a shift. */
+static inline int
+shiftcosts (rtx x)
+{
+ if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ {
+ if (GET_MODE (x) == DImode
+ && CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) == 1)
+ return 2;
+
+ /* Everything else is invalid, because there is no pattern for it. */
+ return -1;
+ }
+ /* If shift by a non constant, then this will be expensive. */
+ if (!CONST_INT_P (XEXP (x, 1)))
+ return SH_DYNAMIC_SHIFT_COST;
+
+ /* Otherwise, return the true cost in instructions. Cope with out of range
+ shift counts more or less arbitrarily. */
+ int value = INTVAL (XEXP (x, 1)) & 31;
+
+ if (GET_CODE (x) == ASHIFTRT)
+ {
+ int cost = ashiftrt_insns[value];
+ /* If dynamic shifts are available and profitable in this case, then we
+ put the constant in a reg and use shad. */
+ if (cost > 1 + SH_DYNAMIC_SHIFT_COST)
+ cost = 1 + SH_DYNAMIC_SHIFT_COST;
+ return cost;
+ }
+ else
+ return ashl_lshr_seq[value].insn_count;
+}
+
+/* Return the cost of an AND/XOR/IOR operation. */
+static inline int
+and_xor_ior_costs (rtx x, int code)
+{
+ /* On SH1-4 we have only max. SImode operations.
+ Double the cost for modes > SImode. */
+ const int cost_scale = GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD ? 2 : 1;
+
+ /* A logical operation with two registers is a single cycle
+ instruction. */
+ if (!CONST_INT_P (XEXP (x, 1)))
+ return 1 * cost_scale;
+
+ int i = INTVAL (XEXP (x, 1));
+
+ /* These constants are single cycle extu.[bw] instructions. */
+ if ((i == 0xff || i == 0xffff) && code == AND)
+ return 1 * cost_scale;
+ /* Constants that can be used in an instruction as an immediate are
+ a single cycle, but this requires r0, so make it a little more
+ expensive. */
+ if (CONST_OK_FOR_K08 (i))
+ return 2 * cost_scale;
+ /* Constants that can be loaded with a mov immediate need one more cycle.
+ This case is probably unnecessary. */
+ if (CONST_OK_FOR_I08 (i))
+ return 2 * cost_scale;
+ /* Any other constant requires an additional 2 cycle pc-relative load.
+ This case is probably unnecessary. */
+ return 3 * cost_scale;
+}
+
+/* Return the cost of an addition or a subtraction. */
+static inline int
+addsubcosts (rtx x)
+{
+ if (GET_MODE (x) == SImode)
+ {
+ /* The addc or subc patterns will eventually become one or two
+ instructions. Below are some costs for some of the patterns
+ which combine would reject because the costs of the individual
+ insns in the patterns are lower.
+
+ FIXME: It would be much easier if we had something like insn cost
+ attributes and the cost calculation machinery used those attributes
+ in the first place. This would eliminate redundant recog-like C
+ code to calculate costs of complex patterns. */
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+
+ if (GET_CODE (x) == PLUS)
+ {
+ if (GET_CODE (op0) == AND
+ && XEXP (op0, 1) == const1_rtx
+ && (GET_CODE (op1) == PLUS
+ || (GET_CODE (op1) == MULT && XEXP (op1, 1) == const2_rtx)))
+ return 1;
+
+ if (GET_CODE (op0) == MULT && XEXP (op0, 1) == const2_rtx
+ && GET_CODE (op1) == LSHIFTRT
+ && CONST_INT_P (XEXP (op1, 1)) && INTVAL (XEXP (op1, 1)) == 31)
+ return 1;
+ }
+ /* Let's assume that adding the result of an insns that stores into
+ the T bit is cheap. */
+ if (treg_set_expr (op1, SImode))
+ return 1;
+ if (treg_set_expr (op0, SImode))
+ return 1;
+ }
+
+ /* On SH1-4 we have only max. SImode operations.
+ Double the cost for modes > SImode. */
+ const int cost_scale = GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD ? 2 : 1;
+
+ /* Adding a register is a single cycle insn. */
+ if (REG_P (XEXP (x, 1))
+ || GET_CODE (XEXP (x, 1)) == SUBREG)
+ return 1 * cost_scale;
+
+ /* Likewise for small constants. */
+ if (CONST_INT_P (XEXP (x, 1))
+ && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
+ return 1 * cost_scale;
+
+ /* Any other constant requires a 2 cycle pc-relative load plus an
+ addition. */
+ return 3 * cost_scale;
+}
+
+/* Return the cost of a multiply. */
+static inline int
+multcosts (rtx x ATTRIBUTE_UNUSED)
+{
+ if (sh_multcost >= 0)
+ return sh_multcost;
+
+ if (TARGET_SH2)
+ {
+ /* We have a mul insn, so we can never take more than the mul and the
+ read of the mac reg, but count more because of the latency and extra
+ reg usage. */
+ if (optimize_size)
+ return 2;
+ return 3;
+ }
+
+ /* If we're aiming at small code, then just count the number of
+ insns in a multiply call sequence. */
+ if (optimize_size)
+ return 5;
+
+ /* Otherwise count all the insns in the routine we'd be calling too. */
+ return 20;
+}
+
+/* Compute a (partial) cost for rtx X. Return true if the complete
+ cost has been computed, and false if subexpressions should be
+ scanned. In either case, *TOTAL contains the cost result. */
+static bool
+sh_rtx_costs (rtx x, machine_mode mode ATTRIBUTE_UNUSED, int outer_code,
+ int opno ATTRIBUTE_UNUSED,
+ int *total, bool speed ATTRIBUTE_UNUSED)
+{
+ int code = GET_CODE (x);
+
+ switch (code)
+ {
+ /* The lower-subreg pass decides whether to split multi-word regs
+ into individual regs by looking at the cost for a SET of certain
+ modes with the following patterns:
+ (set (reg) (reg))
+ (set (reg) (const_int 0))
+ On machines that support vector-move operations a multi-word move
+ is the same cost as individual reg move. On SH there is no
+ vector-move, so we have to provide the correct cost in the number
+ of move insns to load/store the reg of the mode in question. */
+ case SET:
+ if (sh_movt_set_dest (x) != NULL || sh_movrt_set_dest (x) != NULL)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+
+ if (register_operand (SET_DEST (x), VOIDmode)
+ && (register_operand (SET_SRC (x), VOIDmode)
+ || satisfies_constraint_Z (SET_SRC (x))))
+ {
+ const machine_mode mode = GET_MODE (SET_DEST (x));
+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)
+ / mov_insn_size (mode, TARGET_SH2A));
+ return true;
+ }
+ return false;
+
+ /* The cost of a mem access is mainly the cost of the address mode. */
+ case MEM:
+ *total = sh_address_cost (XEXP (x, 0), GET_MODE (x), MEM_ADDR_SPACE (x),
+ true);
+ return true;
+
+ case IF_THEN_ELSE:
+ /* This case is required for the if_then_else negc pattern. */
+ if (treg_set_expr (XEXP (x, 0), SImode))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else
+ return false;
+
+ /* Zero extracts of single bits are usually combine patterns for the
+ tst insns. */
+ case ZERO_EXTRACT:
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && arith_reg_operand (XEXP (XEXP (x, 0), 0), VOIDmode)
+ && XEXP (x, 1) == const1_rtx
+ && CONST_INT_P (XEXP (x, 2))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+ /* Check that the xor constaint overlaps with the extracted bit. */
+ && (INTVAL (XEXP (XEXP (x, 0), 1)) & (1LL << INTVAL (XEXP (x, 2)))))
+ {
+ *total = 1; //COSTS_N_INSNS (1);
+ return true;
+ }
+
+ /* div0s variant. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ {
+ *total = 1;
+ return true;
+ }
+ return false;
+
+ /* The cost of a sign or zero extend depends on whether the source is a
+ reg or a mem. In case of a mem take the address into account. */
+ case SIGN_EXTEND:
+ if (arith_reg_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ if (MEM_P (XEXP (x, 0)))
+ {
+ *total = sh_address_cost (XEXP (XEXP (x, 0), 0),
+ GET_MODE (XEXP (x, 0)),
+ MEM_ADDR_SPACE (XEXP (x, 0)), true);
+ return true;
+ }
+ return false;
+
+ case ZERO_EXTEND:
+ if (arith_reg_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else if (TARGET_SH2A && MEM_P (XEXP (x, 0))
+ && (GET_MODE (XEXP (x, 0)) == QImode
+ || GET_MODE (XEXP (x, 0)) == HImode))
+ {
+ /* Handle SH2A's movu.b and movu.w insn. */
+ *total = sh_address_cost (XEXP (XEXP (x, 0), 0),
+ GET_MODE (XEXP (x, 0)),
+ MEM_ADDR_SPACE (XEXP (x, 0)), true);
+ return true;
+ }
+ return false;
+
+ /* mems for SFmode and DFmode can be inside a parallel due to
+ the way the fpscr is handled. */
+ case PARALLEL:
+ for (int i = 0; i < XVECLEN (x, 0); i++)
+ {
+ rtx xx = XVECEXP (x, 0, i);
+ if (GET_CODE (xx) == SET && MEM_P (XEXP (xx, 0)))
+ {
+ *total = sh_address_cost (XEXP (XEXP (xx, 0), 0),
+ GET_MODE (XEXP (xx, 0)),
+ MEM_ADDR_SPACE (XEXP (xx, 0)), true);
+ return true;
+ }
+ if (GET_CODE (xx) == SET && MEM_P (XEXP (xx, 1)))
+ {
+ *total = sh_address_cost (XEXP (XEXP (xx, 1), 0),
+ GET_MODE (XEXP (xx, 1)),
+ MEM_ADDR_SPACE (XEXP (xx, 1)), true);
+ return true;
+ }
+ }
+
+ if (sh_1el_vec (x, VOIDmode))
+ *total = outer_code != SET;
+ else if (sh_rep_vec (x, VOIDmode))
+ *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+ + (outer_code != SET));
+ else
+ *total = COSTS_N_INSNS (3) + (outer_code != SET);
+ return true;
+
+ case CONST_INT:
+ if (CONST_OK_FOR_I08 (INTVAL (x)))
+ *total = 0;
+ else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
+ && CONST_OK_FOR_K08 (INTVAL (x)))
+ *total = 1;
+ /* prepare_cmp_insn will force costly constants int registers before
+ the cbranch[sd]i4 patterns can see them, so preserve potentially
+ interesting ones not covered by I08 above. */
+ else if (outer_code == COMPARE
+ && ((unsigned HOST_WIDE_INT) INTVAL (x)
+ == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+ || INTVAL (x) == 0x7fffffff
+ || INTVAL (x) == 0x80 || INTVAL (x) == -0x81))
+ *total = 1;
+ else
+ *total = 8;
+ return true;
+
+ case EQ:
+ /* An and with a constant compared against zero is
+ most likely going to be a TST #imm, R0 instruction. */
+ if (XEXP (x, 1) == const0_rtx
+ && ((GET_CODE (XEXP (x, 0)) == AND
+ || (SUBREG_P (XEXP (x, 0))
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == AND))
+ || GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT))
+ {
+ *total = 1;
+ return true;
+ }
+
+ else if (XEXP (x, 1) == const0_rtx
+ && GET_CODE (XEXP (x, 0)) == AND
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT
+ && arith_reg_operand (XEXP (XEXP (XEXP (x, 0), 0), 0), SImode)
+ && CONST_INT_P (XEXP (XEXP (XEXP (x, 0), 0), 1)))
+ {
+ *total = 1;
+ return true;
+ }
+ else
+ return false;
+
+ case SMIN:
+ case SMAX:
+ /* This is most likely a clips.b or clips.w insn that is being made up
+ by combine. */
+ if (TARGET_SH2A
+ && (GET_CODE (XEXP (x, 0)) == SMAX || GET_CODE (XEXP (x, 0)) == SMIN)
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+ && REG_P (XEXP (XEXP (x, 0), 0))
+ && CONST_INT_P (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else
+ return false;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ *total = 5;
+ return true;
+
+ case CONST_DOUBLE:
+ /* prepare_cmp_insn will force costly constants int registers before
+ the cbranchdi4 pattern can see them, so preserve potentially
+ interesting ones. */
+ if (outer_code == COMPARE && GET_MODE (x) == DImode)
+ *total = 1;
+ else
+ *total = 10;
+ return true;
+
+ case CONST_VECTOR:
+ /* FIXME: This looks broken. Only the last statement has any effect.
+ Probably this could be folded with the PARALLEL case? */
+ if (x == CONST0_RTX (GET_MODE (x)))
+ *total = 0;
+ else if (sh_1el_vec (x, VOIDmode))
+ *total = outer_code != SET;
+ if (sh_rep_vec (x, VOIDmode))
+ *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+ + (outer_code != SET));
+ *total = COSTS_N_INSNS (3) + (outer_code != SET);
+ return true;
+
+ case PLUS:
+ case MINUS:
+ *total = COSTS_N_INSNS (addsubcosts (x));
+ return true;
+
+ case AND:
+ /* Check for (and (not (reg)) (const_int 1)) which is a tst insn. */
+ if (GET_CODE (XEXP (x, 0)) == NOT && XEXP (x, 1) == const1_rtx)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ /* Fall through. */
+
+ case XOR:
+ case IOR:
+ *total = COSTS_N_INSNS (and_xor_ior_costs (x, code));
+ return true;
+
+ case MULT:
+ *total = COSTS_N_INSNS (multcosts (x));
+ return true;
+
+ case LT:
+ case GE:
+ /* div0s sign comparison. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && REG_P ((XEXP (XEXP (x, 0), 0)))
+ && REG_P ((XEXP (XEXP (x, 0), 1)))
+ && satisfies_constraint_Z (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else
+ return false;
+
+ case LSHIFTRT:
+ /* div0s sign comparison. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && REG_P ((XEXP (XEXP (x, 0), 0)))
+ && REG_P ((XEXP (XEXP (x, 0), 1)))
+ && CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 31)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ /* FALLTHRU */
+ case ASHIFT:
+ case ASHIFTRT:
+ {
+ int cost = shiftcosts (x);
+ if (cost < 0)
+ return false;
+ *total = COSTS_N_INSNS (cost);
+ return true;
+ }
+
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ *total = COSTS_N_INSNS (20);
+ return true;
+
+ case FLOAT:
+ case FIX:
+ *total = 100;
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Determine the size of the fundamental move insn that will be used
+ for the specified mode. */
+static inline int
+mov_insn_size (machine_mode mode, bool consider_sh2a)
+{
+ const int mode_sz = GET_MODE_SIZE (mode);
+
+ if ((consider_sh2a && TARGET_SH2A_DOUBLE && mode == DFmode)
+ || (TARGET_FMOVD && mode == DFmode))
+ return mode_sz;
+ else
+ {
+ /* The max. available mode for actual move insns is SImode.
+ Larger accesses will be split into multiple loads/stores. */
+ const int max_mov_sz = GET_MODE_SIZE (SImode);
+ return mode_sz >= max_mov_sz ? max_mov_sz : mode_sz;
+ }
+}
+
+/* Determine the maximum possible displacement for a move insn for the
+ specified mode. */
+int
+sh_max_mov_insn_displacement (machine_mode mode, bool consider_sh2a)
+{
+ /* The 4 byte displacement move insns are the same as the 2 byte
+ versions but take a 12 bit displacement. All we need to do is to
+ scale the max. displacement value accordingly. */
+ const int disp_scale = consider_sh2a ? (4095 / 15) : 1;
+
+ /* SH2A supports FPU move insns with 12 bit displacements.
+ Other variants to do not support any kind of displacements for
+ FPU move insns. */
+ if (! consider_sh2a && TARGET_FPU_ANY && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return 0;
+ else
+ {
+ const int mov_insn_sz = mov_insn_size (mode, consider_sh2a);
+ const int mode_sz = GET_MODE_SIZE (mode);
+ int r = 15 * mov_insn_sz * disp_scale;
+
+ /* If the mov insn will be split into multiple loads/stores, the
+ maximum possible displacement is a bit smaller. */
+ if (mode_sz > mov_insn_sz)
+ r -= mode_sz - mov_insn_sz;
+ return r;
+ }
+}
+
+/* Determine the alignment mask for a move insn of the
+ specified mode. */
+static inline int
+mov_insn_alignment_mask (machine_mode mode, bool consider_sh2a)
+{
+ const int mov_insn_sz = mov_insn_size (mode, consider_sh2a);
+ return mov_insn_sz > 0 ? (mov_insn_sz - 1) : 0;
+}
+
+/* Return the displacement value of a displacement address. */
+HOST_WIDE_INT
+sh_disp_addr_displacement (rtx x)
+{
+ gcc_assert (satisfies_constraint_Sdd (x));
+ return INTVAL (XEXP (XEXP (x, 0), 1));
+}
+
+/* Compute the cost of an address. */
+static int
+sh_address_cost (rtx x, machine_mode mode,
+ addr_space_t as ATTRIBUTE_UNUSED, bool speed ATTRIBUTE_UNUSED)
+{
+ /* 'GBR + 0'. Account one more because of R0 restriction. */
+ if (REG_P (x) && REGNO (x) == GBR_REG)
+ return 2;
+
+ /* Simple reg, post-inc, pre-dec addressing. */
+ if (REG_P (x) || GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
+ return 1;
+
+ /* 'reg + disp' addressing. */
+ if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1)))
+ {
+ /* 'GBR + disp'. Account one more because of R0 restriction. */
+ if (REGNO (XEXP (x, 0)) == GBR_REG
+ && gbr_displacement (XEXP (x, 1), mode))
+ return 2;
+
+ const HOST_WIDE_INT offset = INTVAL (XEXP (x, 1));
+
+ if (offset == 0)
+ return 1;
+
+ /* The displacement would fit into a 2 byte move insn.
+ HImode and QImode loads/stores with displacement put pressure on
+ R0 which will most likely require another reg copy. Thus account
+ a higher cost for that. */
+ if (offset > 0 && offset <= sh_max_mov_insn_displacement (mode, false))
+ return (mode == HImode || mode == QImode) ? 2 : 1;
+
+ /* The displacement would fit into a 4 byte move insn (SH2A). */
+ if (TARGET_SH2A
+ && offset > 0 && offset <= sh_max_mov_insn_displacement (mode, true))
+ return 2;
+
+ /* The displacement is probably out of range and will require extra
+ calculations. */
+ return 3;
+ }
+
+ /* 'reg + reg' addressing. Account a slightly higher cost because of
+ increased pressure on R0. */
+ if (GET_CODE (x) == PLUS && ! CONSTANT_P (XEXP (x, 1)))
+ return 3;
+
+ /* Not sure what it is - probably expensive. */
+ return 10;
+}
+
+/* Code to expand a shift. */
+static void
+gen_ashift (int type, int n, rtx reg)
+{
+ rtx n_rtx;
+
+ /* Negative values here come from the shift_amounts array. */
+ if (n < 0)
+ {
+ if (type == ASHIFT)
+ type = LSHIFTRT;
+ else
+ type = ASHIFT;
+ n = -n;
+ }
+
+ n_rtx = GEN_INT (n);
+ gcc_assert (satisfies_constraint_P27 (n_rtx));
+
+ switch (type)
+ {
+ case ASHIFTRT:
+ emit_insn (gen_ashrsi3_k (reg, reg, n_rtx));
+ break;
+ case LSHIFTRT:
+ if (n == 1)
+ emit_insn (gen_shlr (reg, reg));
+ else
+ emit_insn (gen_lshrsi3_k (reg, reg, n_rtx));
+ break;
+ case ASHIFT:
+ emit_insn (gen_ashlsi3_k (reg, reg, n_rtx));
+ break;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Code to expand a HImode shift. */
+static void
+gen_ashift_hi (int type, int n, rtx reg)
+{
+ /* Negative values here come from the shift_amounts array. */
+ if (n < 0)
+ {
+ if (type == ASHIFT)
+ type = LSHIFTRT;
+ else
+ type = ASHIFT;
+ n = -n;
+ }
+
+ switch (type)
+ {
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* We don't have HImode right shift operations because using the
+ ordinary 32 bit shift instructions for that doesn't generate proper
+ zero/sign extension.
+ gen_ashift_hi is only called in contexts where we know that the
+ sign extension works out correctly. */
+ {
+ int offset = 0;
+ if (GET_CODE (reg) == SUBREG)
+ {
+ offset = SUBREG_BYTE (reg);
+ reg = SUBREG_REG (reg);
+ }
+ gen_ashift (type, n, gen_rtx_SUBREG (SImode, reg, offset));
+ break;
+ }
+ case ASHIFT:
+ emit_insn (gen_ashlhi3_k (reg, reg, GEN_INT (n)));
+ break;
+ }
+}
+
+/* Output RTL to split a constant shift into its component SH constant
+ shift instructions. */
+void
+gen_shifty_op (int code, rtx *operands)
+{
+ int value = INTVAL (operands[2]);
+ int max, i;
+
+ /* Truncate the shift count in case it is out of bounds. */
+ value = value & 31;
+
+ if (value == 31)
+ {
+ if (code == LSHIFTRT)
+ {
+ emit_insn (gen_rotlsi3_1 (operands[0], operands[0]));
+ emit_insn (gen_movt (operands[0], get_t_reg_rtx ()));
+ return;
+ }
+ else if (code == ASHIFT)
+ {
+ /* There is a two instruction sequence for 31 bit left shifts,
+ but it requires r0. */
+ if (REG_P (operands[0]) && REGNO (operands[0]) == 0)
+ {
+ emit_insn (gen_andsi3 (operands[0], operands[0], const1_rtx));
+ emit_insn (gen_rotlsi3_31 (operands[0], operands[0]));
+ return;
+ }
+ }
+ }
+ else if (value == 0)
+ {
+ /* This can happen even when optimizing, if there were subregs before
+ reload. Don't output a nop here, as this is never optimized away;
+ use a no-op move instead. */
+ emit_insn (gen_rtx_SET (operands[0], operands[0]));
+ return;
+ }
+
+ max = ashl_lshr_seq[value].insn_count;
+ for (i = 0; i < max; i++)
+ gen_ashift (code, ashl_lshr_seq[value].amount[i], operands[0]);
+}
+
+/* Same as gen_shifty_op, but optimized for values where the topmost bits
+ don't matter. */
+void
+gen_shifty_hi_op (int code, rtx *operands)
+{
+ int value = INTVAL (operands[2]);
+ int max, i;
+ void (*gen_fun) (int, int, rtx);
+
+ /* This operation is used by and_shl for SImode values with a few
+ high bits known to be cleared. */
+ value &= 31;
+ if (value == 0)
+ {
+ emit_insn (gen_nop ());
+ return;
+ }
+
+ gen_fun = GET_MODE (operands[0]) == HImode ? gen_ashift_hi : gen_ashift;
+ if (code == ASHIFT)
+ {
+ max = ext_ashl_lshr_seq[value].insn_count;
+ for (i = 0; i < max; i++)
+ gen_fun (code, ext_ashl_lshr_seq[value].amount[i], operands[0]);
+ }
+ else
+ /* When shifting right, emit the shifts in reverse order, so that
+ solitary negative values come first. */
+ for (i = ext_ashl_lshr_seq[value].insn_count - 1; i >= 0; i--)
+ gen_fun (code, ext_ashl_lshr_seq[value].amount[i], operands[0]);
+}
+
+/* Output RTL for an arithmetic right shift.
+ ??? Rewrite to use super-optimizer sequences. */
+bool
+expand_ashiftrt (rtx *operands)
+{
+ rtx wrk;
+ char func[18];
+ int value;
+
+ if (TARGET_DYNSHIFT)
+ {
+ if (!CONST_INT_P (operands[2]))
+ {
+ rtx count = copy_to_mode_reg (SImode, operands[2]);
+ emit_insn (gen_negsi2 (count, count));
+ emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+ return true;
+ }
+ else if (ashiftrt_insns[INTVAL (operands[2]) & 31]
+ > 1 + SH_DYNAMIC_SHIFT_COST)
+ {
+ rtx count
+ = force_reg (SImode, GEN_INT (- (INTVAL (operands[2]) & 31)));
+ emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+ return true;
+ }
+ }
+ if (!CONST_INT_P (operands[2]))
+ return false;
+
+ value = INTVAL (operands[2]) & 31;
+
+ if (value == 31)
+ {
+ /* If we are called from abs expansion, arrange things so that we
+ we can use a single MT instruction that doesn't clobber the source,
+ if LICM can hoist out the load of the constant zero. */
+ if (currently_expanding_to_rtl)
+ {
+ emit_insn (gen_cmpgtsi_t (force_reg (SImode, CONST0_RTX (SImode)),
+ operands[1]));
+ emit_insn (gen_mov_neg_si_t (operands[0], get_t_reg_rtx ()));
+ return true;
+ }
+ emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
+ return true;
+ }
+ else if (value >= 16 && value <= 19)
+ {
+ wrk = gen_reg_rtx (SImode);
+ emit_insn (gen_ashrsi2_16 (wrk, operands[1]));
+ value -= 16;
+ while (value--)
+ gen_ashift (ASHIFTRT, 1, wrk);
+ emit_move_insn (operands[0], wrk);
+ return true;
+ }
+ /* Expand a short sequence inline, longer call a magic routine. */
+ else if (value <= 5)
+ {
+ wrk = gen_reg_rtx (SImode);
+ emit_move_insn (wrk, operands[1]);
+ while (value--)
+ gen_ashift (ASHIFTRT, 1, wrk);
+ emit_move_insn (operands[0], wrk);
+ return true;
+ }
+
+ wrk = gen_reg_rtx (Pmode);
+
+ /* Load the value into an arg reg and call a helper. */
+ emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
+ sprintf (func, "__ashiftrt_r4_%d", value);
+ rtx lab = function_symbol (wrk, func, SFUNC_STATIC).lab;
+ emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk, lab));
+ emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
+ return true;
+}
+
+/* Try to find a good way to implement the combiner pattern
+ [(set (match_operand:SI 0 "register_operand" "r")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n"))) .
+ LEFT_RTX is operand 2 in the above pattern, and MASK_RTX is operand 3.
+ return 0 for simple right / left or left/right shift combination.
+ return 1 for a combination of shifts with zero_extend.
+ return 2 for a combination of shifts with an AND that needs r0.
+ return 3 for a combination of shifts with an AND that needs an extra
+ scratch register, when the three highmost bits of the AND mask are clear.
+ return 4 for a combination of shifts with an AND that needs an extra
+ scratch register, when any of the three highmost bits of the AND mask
+ is set.
+ If ATTRP is set, store an initial right shift width in ATTRP[0],
+ and the instruction length in ATTRP[1] . These values are not valid
+ when returning 0.
+ When ATTRP is set and returning 1, ATTRP[2] gets set to the index into
+ shift_amounts for the last shift value that is to be used before the
+ sign extend. */
+int
+shl_and_kind (rtx left_rtx, rtx mask_rtx, int *attrp)
+{
+ unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2;
+ int left = INTVAL (left_rtx), right;
+ int best = 0;
+ int cost, best_cost = 10000;
+ int best_right = 0, best_len = 0;
+ int i;
+ int can_ext;
+
+ if (left < 0 || left > 31)
+ return 0;
+ if (CONST_INT_P (mask_rtx))
+ mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left;
+ else
+ mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (SImode) >> left;
+ /* Can this be expressed as a right shift / left shift pair? */
+ lsb = ((mask ^ (mask - 1)) >> 1) + 1;
+ right = exact_log2 (lsb);
+ mask2 = ~(mask + lsb - 1);
+ lsb2 = ((mask2 ^ (mask2 - 1)) >> 1) + 1;
+ /* mask has no zeroes but trailing zeroes <==> ! mask2 */
+ if (! mask2)
+ best_cost = ashl_lshr_seq[right].insn_count
+ + ashl_lshr_seq[right + left].insn_count;
+ /* mask has no trailing zeroes <==> ! right */
+ else if (! right && mask2 == ~(lsb2 - 1))
+ {
+ int late_right = exact_log2 (lsb2);
+ best_cost = ashl_lshr_seq[left + late_right].insn_count
+ + ashl_lshr_seq[late_right].insn_count;
+ }
+ /* Try to use zero extend. */
+ if (mask2 == ~(lsb2 - 1))
+ {
+ int width, first;
+
+ for (width = 8; width <= 16; width += 8)
+ {
+ /* Can we zero-extend right away? */
+ if (lsb2 == (unsigned HOST_WIDE_INT) 1 << width)
+ {
+ cost = 1 + ext_ashl_lshr_seq[right].insn_count
+ + ext_ashl_lshr_seq[left + right].insn_count;
+ if (cost < best_cost)
+ {
+ best = 1;
+ best_cost = cost;
+ best_right = right;
+ best_len = cost;
+ if (attrp)
+ attrp[2] = -1;
+ }
+ continue;
+ }
+ /* ??? Could try to put zero extend into initial right shift,
+ or even shift a bit left before the right shift. */
+ /* Determine value of first part of left shift, to get to the
+ zero extend cut-off point. */
+ first = width - exact_log2 (lsb2) + right;
+ if (first >= 0 && right + left - first >= 0)
+ {
+ cost = ext_ashl_lshr_seq[right].insn_count
+ + ext_ashl_lshr_seq[first].insn_count + 1
+ + ext_ashl_lshr_seq[right + left - first].insn_count;
+
+ if (cost < best_cost)
+ {
+ best = 1;
+ best_cost = cost;
+ best_right = right;
+ best_len = cost;
+ if (attrp)
+ attrp[2] = first;
+ }
+ }
+ }
+ }
+ /* Try to use r0 AND pattern */
+ for (i = 0; i <= 2; i++)
+ {
+ if (i > right)
+ break;
+ if (! CONST_OK_FOR_K08 (mask >> i))
+ continue;
+ cost = (i != 0) + 2 + ext_ashl_lshr_seq[left + i].insn_count;
+ if (cost < best_cost)
+ {
+ best = 2;
+ best_cost = cost;
+ best_right = i;
+ best_len = cost - 1;
+ }
+ }
+ /* Try to use a scratch register to hold the AND operand. */
+ can_ext = ((mask << left) & ((unsigned HOST_WIDE_INT) 3 << 30)) == 0;
+ for (i = 0; i <= 2; i++)
+ {
+ if (i > right)
+ break;
+ cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3)
+ + (can_ext
+ ? ext_ashl_lshr_seq
+ : ashl_lshr_seq)[left + i].insn_count;
+ if (cost < best_cost)
+ {
+ best = 4 - can_ext;
+ best_cost = cost;
+ best_right = i;
+ best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i);
+ }
+ }
+
+ if (attrp)
+ {
+ attrp[0] = best_right;
+ attrp[1] = best_len;
+ }
+ return best;
+}
+
+/* This is used in length attributes of the unnamed instructions
+ corresponding to shl_and_kind return values of 1 and 2. */
+int
+shl_and_length (rtx insn)
+{
+ rtx set_src, left_rtx, mask_rtx;
+ int attributes[3];
+
+ set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ left_rtx = XEXP (XEXP (set_src, 0), 1);
+ mask_rtx = XEXP (set_src, 1);
+ shl_and_kind (left_rtx, mask_rtx, attributes);
+ return attributes[1];
+}
+
+/* This is used in length attribute of the and_shl_scratch instruction. */
+int
+shl_and_scr_length (rtx insn)
+{
+ rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ int len = ashl_lshr_seq[INTVAL (XEXP (set_src, 1)) & 31].insn_count;
+ rtx op = XEXP (set_src, 0);
+ len += ashl_lshr_seq[INTVAL (XEXP (op, 1)) & 31].insn_count + 1;
+ op = XEXP (XEXP (op, 0), 0);
+ return len + ashl_lshr_seq[INTVAL (XEXP (op, 1)) & 31].insn_count;
+}
+
+/* Generate rtl for instructions for which shl_and_kind advised a particular
+ method of generating them, i.e. returned zero. */
+bool
+gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source)
+{
+ int attributes[3];
+ unsigned HOST_WIDE_INT mask;
+ int kind = shl_and_kind (left_rtx, mask_rtx, attributes);
+ int right, total_shift;
+ void (*shift_gen_fun) (int, rtx *) = gen_shifty_hi_op;
+
+ right = attributes[0];
+ total_shift = INTVAL (left_rtx) + right;
+ mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> total_shift;
+ switch (kind)
+ {
+ default:
+ return true;
+ case 1:
+ {
+ int first = attributes[2];
+ rtx operands[3];
+
+ if (first < 0)
+ {
+ emit_insn ((mask << right) <= 0xff
+ ? gen_zero_extendqisi2 (dest,
+ gen_lowpart (QImode, source))
+ : gen_zero_extendhisi2 (dest,
+ gen_lowpart (HImode, source)));
+ source = dest;
+ }
+ if (source != dest)
+ emit_insn (gen_movsi (dest, source));
+ operands[0] = dest;
+ if (right)
+ {
+ operands[2] = GEN_INT (right);
+ gen_shifty_hi_op (LSHIFTRT, operands);
+ }
+ if (first > 0)
+ {
+ operands[2] = GEN_INT (first);
+ gen_shifty_hi_op (ASHIFT, operands);
+ total_shift -= first;
+ mask <<= first;
+ }
+ if (first >= 0)
+ emit_insn (mask <= 0xff
+ ? gen_zero_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_zero_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ if (total_shift > 0)
+ {
+ operands[2] = GEN_INT (total_shift);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ break;
+ }
+ case 4:
+ shift_gen_fun = gen_shifty_op;
+ /* FALLTHRU */
+ case 3:
+ /* If the topmost bit that matters is set, set the topmost bits
+ that don't matter. This way, we might be able to get a shorter
+ signed constant. */
+ if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift)))
+ mask |= (HOST_WIDE_INT) ((HOST_WIDE_INT_M1U) << (31 - total_shift));
+ /* FALLTHRU */
+ case 2:
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (currently_expanding_to_rtl
+ || reload_in_progress || reload_completed)
+ {
+ rtx operands[3];
+
+ /* Cases 3 and 4 should be handled by this split
+ only while combining */
+ gcc_assert (kind <= 2);
+ if (right)
+ {
+ emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
+ source = dest;
+ }
+ emit_insn (gen_andsi3 (dest, source, GEN_INT (mask)));
+ if (total_shift)
+ {
+ operands[0] = dest;
+ operands[1] = dest;
+ operands[2] = GEN_INT (total_shift);
+ shift_gen_fun (ASHIFT, operands);
+ }
+ break;
+ }
+ else
+ {
+ int neg = 0;
+ if (kind != 4 && total_shift < 16)
+ {
+ neg = -ext_ashl_lshr_seq[total_shift].amount[1];
+ if (neg > 0)
+ neg -= ext_ashl_lshr_seq[total_shift].amount[2];
+ else
+ neg = 0;
+ }
+ emit_insn (gen_and_shl_scratch (dest, source,
+ GEN_INT (right),
+ GEN_INT (mask),
+ GEN_INT (total_shift + neg),
+ GEN_INT (neg)));
+ emit_insn (gen_movsi (dest, dest));
+ break;
+ }
+ }
+ return false;
+}
+
+/* Try to find a good way to implement the combiner pattern
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "const_int_operand" "n")
+ (match_operand:SI 3 "const_int_operand" "n")
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ LEFT_RTX is operand 2 in the above pattern, and SIZE_RTX is operand 3.
+ return 0 for simple left / right shift combination.
+ return 1 for left shift / 8 bit sign extend / left shift.
+ return 2 for left shift / 16 bit sign extend / left shift.
+ return 3 for left shift / 8 bit sign extend / shift / sign extend.
+ return 4 for left shift / 16 bit sign extend / shift / sign extend.
+ return 5 for left shift / 16 bit sign extend / right shift
+ return 6 for < 8 bit sign extend / left shift.
+ return 7 for < 8 bit sign extend / left shift / single right shift.
+ If COSTP is nonzero, assign the calculated cost to *COSTP. */
+int
+shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp)
+{
+ int left, size, insize, ext;
+ int cost = 0, best_cost;
+ int kind;
+
+ left = INTVAL (left_rtx);
+ size = INTVAL (size_rtx);
+ insize = size - left;
+ gcc_assert (insize > 0);
+ /* Default to left / right shift. */
+ kind = 0;
+ best_cost = ashl_lshr_seq[32 - insize].insn_count
+ + ashl_lshr_seq[32 - size].insn_count;
+ if (size <= 16)
+ {
+ /* 16 bit shift / sign extend / 16 bit shift */
+ cost = ashl_lshr_seq[16 - insize].insn_count + 1
+ + ashl_lshr_seq[16 - size].insn_count;
+ /* If ashiftrt_insns[16 - size] is 8, this choice will be overridden
+ below, by alternative 3 or something even better. */
+ if (cost < best_cost)
+ {
+ kind = 5;
+ best_cost = cost;
+ }
+ }
+ /* Try a plain sign extend between two shifts. */
+ for (ext = 16; ext >= insize; ext -= 8)
+ {
+ if (ext <= size)
+ {
+ cost = ext_ashl_lshr_seq[ext - insize].insn_count + 1
+ + ashl_lshr_seq[size - ext].insn_count;
+ if (cost < best_cost)
+ {
+ kind = ext / (unsigned) 8;
+ best_cost = cost;
+ }
+ }
+ /* Check if we can do a sloppy shift with a final signed shift
+ restoring the sign. */
+ if (EXT_SHIFT_SIGNED (size - ext))
+ cost = ext_ashl_lshr_seq[ext - insize].insn_count
+ + ext_ashl_lshr_seq[size - ext].insn_count + 1;
+ /* If not, maybe it's still cheaper to do the second shift sloppy,
+ and do a final sign extend? */
+ else if (size <= 16)
+ cost = ext_ashl_lshr_seq[ext - insize].insn_count + 1
+ + ext_ashl_lshr_seq[size > ext ? size - ext : ext - size].insn_count
+ + 1;
+ else
+ continue;
+ if (cost < best_cost)
+ {
+ kind = ext / (unsigned) 8 + 2;
+ best_cost = cost;
+ }
+ }
+ /* Check if we can sign extend in r0 */
+ if (insize < 8)
+ {
+ cost = 3 + ashl_lshr_seq[left].insn_count;
+ if (cost < best_cost)
+ {
+ kind = 6;
+ best_cost = cost;
+ }
+ /* Try the same with a final signed shift. */
+ if (left < 31)
+ {
+ cost = 3 + ext_ashl_lshr_seq[left + 1].insn_count + 1;
+ if (cost < best_cost)
+ {
+ kind = 7;
+ best_cost = cost;
+ }
+ }
+ }
+ if (TARGET_DYNSHIFT)
+ {
+ /* Try to use a dynamic shift. */
+ cost = ashl_lshr_seq[32 - insize].insn_count + 1 + SH_DYNAMIC_SHIFT_COST;
+ if (cost < best_cost)
+ {
+ kind = 0;
+ best_cost = cost;
+ }
+ }
+ if (costp)
+ *costp = cost;
+ return kind;
+}
+
+/* Function to be used in the length attribute of the instructions
+ implementing this pattern. */
+int
+shl_sext_length (rtx insn)
+{
+ rtx set_src, left_rtx, size_rtx;
+ int cost;
+
+ set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ left_rtx = XEXP (XEXP (set_src, 0), 1);
+ size_rtx = XEXP (set_src, 1);
+ shl_sext_kind (left_rtx, size_rtx, &cost);
+ return cost;
+}
+
+/* Generate rtl for this pattern */
+bool
+gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source)
+{
+ int kind;
+ int left, size, insize, cost;
+ rtx operands[3];
+
+ kind = shl_sext_kind (left_rtx, size_rtx, &cost);
+ left = INTVAL (left_rtx);
+ size = INTVAL (size_rtx);
+ insize = size - left;
+ switch (kind)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ {
+ int ext = kind & 1 ? 8 : 16;
+ int shift2 = size - ext;
+
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ {
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ emit_insn (gen_movsi (dest, source));
+ break;
+ }
+ if (dest != source)
+ emit_insn (gen_movsi (dest, source));
+ operands[0] = dest;
+ if (ext - insize)
+ {
+ operands[2] = GEN_INT (ext - insize);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ emit_insn (kind & 1
+ ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ if (kind <= 2)
+ {
+ if (shift2)
+ {
+ operands[2] = GEN_INT (shift2);
+ gen_shifty_op (ASHIFT, operands);
+ }
+ }
+ else
+ {
+ if (shift2 > 0)
+ {
+ if (EXT_SHIFT_SIGNED (shift2))
+ {
+ operands[2] = GEN_INT (shift2 + 1);
+ gen_shifty_op (ASHIFT, operands);
+ operands[2] = const1_rtx;
+ gen_shifty_op (ASHIFTRT, operands);
+ break;
+ }
+ operands[2] = GEN_INT (shift2);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ else if (shift2)
+ {
+ operands[2] = GEN_INT (-shift2);
+ gen_shifty_hi_op (LSHIFTRT, operands);
+ }
+ emit_insn (size <= 8
+ ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ }
+ break;
+ }
+ case 5:
+ {
+ int i = 16 - size;
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ else
+ {
+ operands[0] = dest;
+ operands[2] = GEN_INT (16 - insize);
+ gen_shifty_hi_op (ASHIFT, operands);
+ emit_insn (gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ }
+ /* Don't use gen_ashrsi3 because it generates new pseudos. */
+ while (--i >= 0)
+ gen_ashift (ASHIFTRT, 1, dest);
+ break;
+ }
+ case 6:
+ case 7:
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ {
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ emit_insn (gen_movsi (dest, source));
+ break;
+ }
+ emit_insn (gen_andsi3 (dest, source, GEN_INT ((1 << insize) - 1)));
+ emit_insn (gen_xorsi3 (dest, dest, GEN_INT (1 << (insize - 1))));
+ emit_insn (gen_addsi3 (dest, dest, GEN_INT (HOST_WIDE_INT_M1U << (insize - 1))));
+ operands[0] = dest;
+ operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx;
+ gen_shifty_op (ASHIFT, operands);
+ if (kind == 7)
+ emit_insn (gen_ashrsi3_k (dest, dest, const1_rtx));
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+typedef struct label_ref_list_d
+{
+ rtx_code_label *label;
+ struct label_ref_list_d *next;
+} *label_ref_list_t;
+
+static object_allocator<label_ref_list_d> label_ref_list_d_pool
+ ("label references list");
+
+/* The SH cannot load a large constant into a register, constants have to
+ come from a pc relative load. The reference of a pc relative load
+ instruction must be less than 1k in front of the instruction. This
+ means that we often have to dump a constant inside a function, and
+ generate code to branch around it.
+
+ It is important to minimize this, since the branches will slow things
+ down and make things bigger.
+
+ Worst case code looks like:
+
+ mov.l L1,rn
+ bra L2
+ nop
+ align
+ L1: .long value
+ L2:
+ ..
+
+ mov.l L3,rn
+ bra L4
+ nop
+ align
+ L3: .long value
+ L4:
+ ..
+
+ We fix this by performing a scan before scheduling, which notices which
+ instructions need to have their operands fetched from the constant table
+ and builds the table.
+
+ The algorithm is:
+
+ scan, find an instruction which needs a pcrel move. Look forward, find the
+ last barrier which is within MAX_COUNT bytes of the requirement.
+ If there isn't one, make one. Process all the instructions between
+ the find and the barrier.
+
+ In the above example, we can tell that L3 is within 1k of L1, so
+ the first move can be shrunk from the 3 insn+constant sequence into
+ just 1 insn, and the constant moved to L3 to make:
+
+ mov.l L1,rn
+ ..
+ mov.l L3,rn
+ bra L4
+ nop
+ align
+ L3:.long value
+ L4:.long value
+
+ Then the second move becomes the target for the shortening process. */
+
+typedef struct
+{
+ rtx value; /* Value in table. */
+ rtx_code_label *label; /* Label of value. */
+ label_ref_list_t wend; /* End of window. */
+ machine_mode mode; /* Mode of value. */
+
+ /* True if this constant is accessed as part of a post-increment
+ sequence. Note that HImode constants are never accessed in this way. */
+ bool part_of_sequence_p;
+} pool_node;
+
+/* The maximum number of constants that can fit into one pool, since
+ constants in the range 0..510 are at least 2 bytes long, and in the
+ range from there to 1018 at least 4 bytes. */
+
+#define MAX_POOL_SIZE 372
+static pool_node pool_vector[MAX_POOL_SIZE];
+static int pool_size;
+static rtx_code_label *pool_window_label;
+static int pool_window_last;
+
+static int max_labelno_before_reorg;
+
+/* ??? If we need a constant in HImode which is the truncated value of a
+ constant we need in SImode, we could combine the two entries thus saving
+ two bytes. Is this common enough to be worth the effort of implementing
+ it? */
+
+/* ??? This stuff should be done at the same time that we shorten branches.
+ As it is now, we must assume that all branches are the maximum size, and
+ this causes us to almost always output constant pools sooner than
+ necessary. */
+
+/* Add a constant to the pool and return its label. */
+static rtx_code_label *
+add_constant (rtx x, machine_mode mode, rtx last_value)
+{
+ rtx_code_label *lab, *new_rtx;
+ label_ref_list_t ref, newref;
+
+ /* First see if we've already got it. */
+ for (int i = 0; i < pool_size; i++)
+ {
+ if (x->code == pool_vector[i].value->code
+ && mode == pool_vector[i].mode)
+ {
+ if (x->code == CODE_LABEL)
+ {
+ if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
+ continue;
+ }
+ if (rtx_equal_p (x, pool_vector[i].value))
+ {
+ lab = new_rtx = 0;
+ if (! last_value
+ || ! i
+ || ! rtx_equal_p (last_value, pool_vector[i-1].value))
+ {
+ new_rtx = gen_label_rtx ();
+ LABEL_REFS (new_rtx) = pool_vector[i].label;
+ pool_vector[i].label = lab = new_rtx;
+ }
+ if (lab && pool_window_label)
+ {
+ newref = label_ref_list_d_pool.allocate ();
+ newref->label = pool_window_label;
+ ref = pool_vector[pool_window_last].wend;
+ newref->next = ref;
+ pool_vector[pool_window_last].wend = newref;
+ }
+ if (new_rtx)
+ pool_window_label = new_rtx;
+ pool_window_last = i;
+ return lab;
+ }
+ }
+ }
+
+ /* Need a new one. */
+ pool_vector[pool_size].value = x;
+ if (last_value && rtx_equal_p (last_value, pool_vector[pool_size - 1].value))
+ {
+ lab = 0;
+ pool_vector[pool_size - 1].part_of_sequence_p = true;
+ }
+ else
+ lab = gen_label_rtx ();
+ pool_vector[pool_size].mode = mode;
+ pool_vector[pool_size].label = lab;
+ pool_vector[pool_size].wend = NULL;
+ pool_vector[pool_size].part_of_sequence_p = (lab == 0);
+ if (lab && pool_window_label)
+ {
+ newref = label_ref_list_d_pool.allocate ();
+ newref->label = pool_window_label;
+ ref = pool_vector[pool_window_last].wend;
+ newref->next = ref;
+ pool_vector[pool_window_last].wend = newref;
+ }
+ if (lab)
+ pool_window_label = lab;
+ pool_window_last = pool_size;
+ pool_size++;
+ return lab;
+}
+
+/* Output the literal table. START, if nonzero, is the first instruction
+ this table is needed for, and also indicates that there is at least one
+ casesi_worker_2 instruction; We have to emit the operand3 labels from
+ these insns at a 4-byte aligned position. BARRIER is the barrier
+ after which we are to place the table. */
+static void
+dump_table (rtx_insn *start, rtx_insn *barrier)
+{
+ rtx_insn *scan = barrier;
+ bool need_align = true;
+ rtx_code_label *lab;
+ label_ref_list_t ref;
+ bool have_df = false;
+
+ /* Do two passes, first time dump out the HI sized constants. */
+
+ for (int i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ if (p->mode == HImode)
+ {
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_2 (), scan);
+ need_align = false;
+ }
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
+ scan);
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
+ }
+ else if (p->mode == DFmode)
+ have_df = true;
+ }
+
+ need_align = true;
+
+ if (start)
+ {
+ scan = emit_insn_after (gen_align_4 (), scan);
+ need_align = false;
+ for (; start != barrier; start = NEXT_INSN (start))
+ if (NONJUMP_INSN_P (start)
+ && recog_memoized (start) == CODE_FOR_casesi_worker_2)
+ {
+ rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
+ rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
+
+ scan = emit_label_after (as_a <rtx_insn *> (lab), scan);
+ }
+ }
+ if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
+ {
+ rtx_insn *align_insn = NULL;
+
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ need_align = false;
+
+ for (int i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ switch (p->mode)
+ {
+ case E_HImode:
+ break;
+ case E_SImode:
+ case E_SFmode:
+ if (align_insn && !p->part_of_sequence_p)
+ {
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ emit_label_before (lab, align_insn);
+ emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
+ align_insn);
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ emit_insn_before (gen_consttable_window_end (lab),
+ align_insn);
+ }
+ delete_insn (align_insn);
+ align_insn = NULL;
+ continue;
+ }
+ else
+ {
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value,
+ const0_rtx), scan);
+ need_align = ! need_align;
+ }
+ break;
+ case E_DFmode:
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ align_insn = scan;
+ need_align = false;
+ }
+ /* FALLTHRU */
+ case E_DImode:
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab),
+ scan);
+ }
+ }
+ }
+
+ pool_size = 0;
+ }
+
+ for (int i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ switch (p->mode)
+ {
+ case E_HImode:
+ break;
+ case E_SImode:
+ case E_SFmode:
+ if (need_align)
+ {
+ need_align = false;
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
+ scan);
+ break;
+ case E_DFmode:
+ case E_DImode:
+ if (need_align)
+ {
+ need_align = false;
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
+ }
+ }
+
+ scan = emit_insn_after (gen_consttable_end (), scan);
+ scan = emit_barrier_after (scan);
+ pool_size = 0;
+ pool_window_label = NULL;
+ pool_window_last = 0;
+}
+
+#define MOVA_LABELREF(mova) XVECEXP (SET_SRC (PATTERN (mova)), 0, 0)
+
+/* Nonzero if the insn is a move instruction which needs to be fixed. */
+
+/* ??? For a DImode/DFmode moves, we don't need to fix it if each half of the
+ CONST_DOUBLE input value is CONST_OK_FOR_I08. For a SFmode move, we don't
+ need to fix it if the input value is CONST_OK_FOR_I08. */
+static bool
+broken_move (rtx_insn *insn)
+{
+ if (NONJUMP_INSN_P (insn))
+ {
+ rtx pat = PATTERN (insn);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ if (GET_CODE (pat) == SET
+ /* We can load any 8-bit value if we don't care what the high
+ order bits end up as. */
+ && GET_MODE (SET_DEST (pat)) != QImode
+ && (CONSTANT_P (SET_SRC (pat))
+ || (GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
+ && XINT (SET_SRC (pat), 1) == UNSPECV_SP_SWITCH_B)
+ /* Match mova_const. */
+ || (GET_CODE (SET_SRC (pat)) == UNSPEC
+ && XINT (SET_SRC (pat), 1) == UNSPEC_MOVA
+ && GET_CODE (XVECEXP (SET_SRC (pat), 0, 0)) == CONST))
+ && ! (TARGET_SH2E
+ && GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
+ && (fp_zero_operand (SET_SRC (pat))
+ || fp_one_operand (SET_SRC (pat)))
+ /* In general we don't know the current setting of fpscr, so
+ disable fldi.
+ There is an exception if this was a register-register move
+ before reload - and hence it was ascertained that we have
+ single precision setting - and in a post-reload optimization
+ we changed this to do a constant load. In that case
+ we don't have an r0 clobber, hence we must use fldi. */
+ && (TARGET_FMOVD
+ || (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
+ == SCRATCH))
+ && REG_P (SET_DEST (pat))
+ && FP_REGISTER_P (REGNO (SET_DEST (pat))))
+ && ! (TARGET_SH2A
+ && GET_MODE (SET_DEST (pat)) == SImode
+ && (satisfies_constraint_I20 (SET_SRC (pat))
+ || satisfies_constraint_I28 (SET_SRC (pat))))
+ && ! satisfies_constraint_I08 (SET_SRC (pat)))
+ return true;
+ }
+
+ return false;
+}
+
+/* Return true if the specified insn is a mova insn. */
+static bool
+mova_p (rtx_insn *insn)
+{
+ return (NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
+ && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_MOVA
+ /* Don't match mova_const. */
+ && GET_CODE (MOVA_LABELREF (insn)) == LABEL_REF);
+}
+
+/* Fix up a mova from a switch that went out of range. */
+static void
+fixup_mova (rtx_insn *mova)
+{
+ PUT_MODE (XEXP (MOVA_LABELREF (mova), 0), QImode);
+ if (! flag_pic)
+ {
+ SET_SRC (PATTERN (mova)) = MOVA_LABELREF (mova);
+ INSN_CODE (mova) = -1;
+ }
+ else
+ {
+ rtx_insn *worker = mova;
+ rtx_code_label *lab = gen_label_rtx ();
+ rtx wpat, wpat0, wpat1, wsrc, target, base, diff;
+
+ do
+ {
+ worker = NEXT_INSN (worker);
+ gcc_assert (worker
+ && !LABEL_P (worker)
+ && !JUMP_P (worker));
+ } while (NOTE_P (worker)
+ || recog_memoized (worker) != CODE_FOR_casesi_worker_1);
+ wpat = PATTERN (worker);
+ wpat0 = XVECEXP (wpat, 0, 0);
+ wpat1 = XVECEXP (wpat, 0, 1);
+ wsrc = SET_SRC (wpat0);
+ PATTERN (worker) = (gen_casesi_worker_2
+ (SET_DEST (wpat0), XVECEXP (wsrc, 0, 1),
+ XEXP (XVECEXP (wsrc, 0, 2), 0), lab,
+ XEXP (wpat1, 0)));
+ INSN_CODE (worker) = -1;
+ target = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
+ base = gen_rtx_LABEL_REF (Pmode, lab);
+ diff = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, target, base), UNSPEC_SYMOFF);
+ SET_SRC (PATTERN (mova)) = gen_rtx_CONST (Pmode, diff);
+ INSN_CODE (mova) = -1;
+ }
+}
+
+/* NEW_MOVA is a mova we've just encountered while scanning forward. Update
+ *num_mova, and check if the new mova is not nested within the first one.
+ return 0 if *first_mova was replaced, 1 if new_mova was replaced,
+ 2 if new_mova has been assigned to *first_mova, -1 otherwise.. */
+static int
+untangle_mova (int *num_mova, rtx_insn **first_mova, rtx_insn *new_mova)
+{
+ int n_addr = 0; /* Initialization to shut up spurious warning. */
+ int f_target, n_target = 0; /* Likewise. */
+
+ if (optimize)
+ {
+ /* If NEW_MOVA has no address yet, it will be handled later. */
+ if (INSN_ADDRESSES_SIZE() <= (unsigned) INSN_UID (new_mova))
+ return -1;
+
+ n_addr = INSN_ADDRESSES (INSN_UID (new_mova));
+ n_target = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (new_mova), 0)));
+ if (n_addr > n_target || n_addr + 1022 < n_target)
+ {
+ /* Change the mova into a load.
+ broken_move will then return true for it. */
+ fixup_mova (new_mova);
+ return 1;
+ }
+ }
+ if (!(*num_mova)++)
+ {
+ *first_mova = new_mova;
+ return 2;
+ }
+ if (!optimize
+ || ((f_target
+ = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (*first_mova), 0))))
+ >= n_target))
+ return -1;
+
+ (*num_mova)--;
+ if (f_target - INSN_ADDRESSES (INSN_UID (*first_mova))
+ > n_target - n_addr)
+ {
+ fixup_mova (*first_mova);
+ return 0;
+ }
+ else
+ {
+ fixup_mova (new_mova);
+ return 1;
+ }
+}
+
+/* Find the last barrier from insn FROM which is close enough to hold the
+ constant pool. If we can't find one, then create one near the end of
+ the range. */
+static rtx_insn *
+find_barrier (int num_mova, rtx_insn *mova, rtx_insn *from)
+{
+ int count_si = 0;
+ int count_hi = 0;
+ int found_hi = 0;
+ int found_si = 0;
+ int hi_align = 2;
+ int si_align = 2;
+ int leading_mova = num_mova;
+ rtx_insn *barrier_before_mova = NULL;
+ rtx_insn *found_barrier = NULL;
+ rtx_insn *good_barrier = NULL;
+ int si_limit;
+ int hi_limit;
+ rtx_insn *orig = from;
+ rtx_insn *last_got = NULL;
+ rtx_insn *last_symoff = NULL;
+
+ /* For HImode: range is 510, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 for the jump instruction
+ that we may need to emit before the table, subtract 2 for the instruction
+ that fills the jump delay slot (in very rare cases, reorg will take an
+ instruction from after the constant pool or will leave the delay slot
+ empty). This gives 510.
+ For SImode: range is 1020, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 in case pc is 2 byte
+ aligned, subtract 2 for the jump instruction that we may need to emit
+ before the table, subtract 2 for the instruction that fills the jump
+ delay slot. This gives 1018. */
+
+ /* The branch will always be shortened now that the reference address for
+ forward branches is the successor address, thus we need no longer make
+ adjustments to the [sh]i_limit for -O0. */
+
+ si_limit = 1018;
+ hi_limit = 510;
+
+ while (from && count_si < si_limit && count_hi < hi_limit)
+ {
+ int inc = get_attr_length (from);
+ int new_align = 1;
+
+ /* If this is a label that existed at the time of the compute_alignments
+ call, determine the alignment. N.B. When find_barrier recurses for
+ an out-of-reach mova, we might see labels at the start of previously
+ inserted constant tables. */
+ if (LABEL_P (from)
+ && CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
+ {
+ if (optimize)
+ new_align = 1 << label_to_alignment (from).levels[0].log;
+ else if (BARRIER_P (prev_nonnote_insn (from)))
+ new_align = 1 << barrier_align (from);
+ else
+ new_align = 1;
+ inc = 0;
+ }
+ /* In case we are scanning a constant table because of recursion, check
+ for explicit alignments. If the table is long, we might be forced
+ to emit the new table in front of it; the length of the alignment
+ might be the last straw. */
+ else if (NONJUMP_INSN_P (from)
+ && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+ && XINT (PATTERN (from), 1) == UNSPECV_ALIGN)
+ new_align = INTVAL (XVECEXP (PATTERN (from), 0, 0));
+ /* When we find the end of a constant table, paste the new constant
+ at the end. That is better than putting it in front because
+ this way, we don't need extra alignment for adding a 4-byte-aligned
+ mov(a) label to a 2/4 or 8/4 byte aligned table. */
+ else if (NONJUMP_INSN_P (from)
+ && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+ && XINT (PATTERN (from), 1) == UNSPECV_CONST_END)
+ return from;
+
+ if (BARRIER_P (from))
+ {
+ rtx_insn *next;
+
+ found_barrier = from;
+
+ /* If we are at the end of the function, or in front of an alignment
+ instruction, we need not insert an extra alignment. We prefer
+ this kind of barrier. */
+ if (barrier_align (from) > 2)
+ good_barrier = from;
+
+ /* If we are at the end of a hot/cold block, dump the constants
+ here. */
+ next = NEXT_INSN (from);
+ if (next
+ && NOTE_P (next)
+ && NOTE_KIND (next) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
+ break;
+ }
+
+ if (broken_move (from))
+ {
+ rtx pat, src, dst;
+ machine_mode mode;
+
+ pat = PATTERN (from);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ src = SET_SRC (pat);
+ dst = SET_DEST (pat);
+ mode = GET_MODE (dst);
+
+ /* GOT pcrelat setting comes in pair of
+ mova .L8,r0
+ mov.l .L8,r12
+ instructions. (plus add r0,r12).
+ Remember if we see one without the other. */
+ if (GET_CODE (src) == UNSPEC && PIC_ADDR_P (XVECEXP (src, 0, 0)))
+ last_got = last_got ? NULL : from;
+ else if (PIC_ADDR_P (src))
+ last_got = last_got ? NULL : from;
+
+ /* We must explicitly check the mode, because sometimes the
+ front end will generate code to load unsigned constants into
+ HImode targets without properly sign extending them. */
+ if (mode == HImode
+ || (mode == SImode && satisfies_constraint_I16 (src)
+ && REGNO (dst) != FPUL_REG))
+ {
+ found_hi += 2;
+ /* We put the short constants before the long constants, so
+ we must count the length of short constants in the range
+ for the long constants. */
+ /* ??? This isn't optimal, but is easy to do. */
+ si_limit -= 2;
+ }
+ else
+ {
+ /* We dump DF/DI constants before SF/SI ones, because
+ the limit is the same, but the alignment requirements
+ are higher. We may waste up to 4 additional bytes
+ for alignment, and the DF/DI constant may have
+ another SF/SI constant placed before it. */
+ while (si_align > 2 && found_si + si_align - 2 > count_si)
+ si_align >>= 1;
+ if (found_si > count_si)
+ count_si = found_si;
+ found_si += GET_MODE_SIZE (mode);
+ if (num_mova)
+ si_limit -= GET_MODE_SIZE (mode);
+ }
+ }
+
+ if (mova_p (from))
+ {
+ switch (untangle_mova (&num_mova, &mova, from))
+ {
+ case 1:
+ if (flag_pic)
+ {
+ rtx src = SET_SRC (PATTERN (from));
+ if (GET_CODE (src) == CONST
+ && GET_CODE (XEXP (src, 0)) == UNSPEC
+ && XINT (XEXP (src, 0), 1) == UNSPEC_SYMOFF)
+ last_symoff = from;
+ }
+ break;
+ case 0: return find_barrier (0, 0, mova);
+ case 2:
+ {
+ leading_mova = 0;
+ barrier_before_mova
+ = good_barrier ? good_barrier : found_barrier;
+ }
+ default: break;
+ }
+ if (found_si > count_si)
+ count_si = found_si;
+ }
+ else if (JUMP_TABLE_DATA_P (from)
+ && GET_CODE (PATTERN (from)) == ADDR_DIFF_VEC)
+ {
+ if ((num_mova > 1 && GET_MODE (prev_nonnote_insn (from)) == VOIDmode)
+ || (num_mova
+ && (prev_nonnote_insn (from)
+ == XEXP (MOVA_LABELREF (mova), 0))))
+ num_mova--;
+ if (barrier_align (next_real_insn (from)) == align_jumps.levels[0].log)
+ {
+ /* We have just passed the barrier in front of the
+ ADDR_DIFF_VEC, which is stored in found_barrier. Since
+ the ADDR_DIFF_VEC is accessed as data, just like our pool
+ constants, this is a good opportunity to accommodate what
+ we have gathered so far.
+ If we waited any longer, we could end up at a barrier in
+ front of code, which gives worse cache usage for separated
+ instruction / data caches. */
+ good_barrier = found_barrier;
+ break;
+ }
+ else
+ {
+ rtx body = PATTERN (from);
+ inc = XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body));
+ }
+ }
+ /* For the SH1, we generate alignments even after jumps-around-jumps. */
+ else if (JUMP_P (from)
+ && ! TARGET_SH2
+ && ! optimize_size)
+ new_align = 4;
+
+ /* There is a possibility that a bf is transformed into a bf/s by the
+ delay slot scheduler. */
+ if (JUMP_P (from)
+ && get_attr_type (from) == TYPE_CBRANCH
+ && ! sequence_insn_p (from))
+ inc += 2;
+
+ if (found_si)
+ {
+ count_si += inc;
+ if (new_align > si_align)
+ {
+ si_limit -= (count_si - 1) & (new_align - si_align);
+ si_align = new_align;
+ }
+ count_si = (count_si + new_align - 1) & -new_align;
+ }
+ if (found_hi)
+ {
+ count_hi += inc;
+ if (new_align > hi_align)
+ {
+ hi_limit -= (count_hi - 1) & (new_align - hi_align);
+ hi_align = new_align;
+ }
+ count_hi = (count_hi + new_align - 1) & -new_align;
+ }
+ from = NEXT_INSN (from);
+ }
+
+ if (num_mova)
+ {
+ if (leading_mova)
+ {
+ /* Try as we might, the leading mova is out of range. Change
+ it into a load (which will become a pcload) and retry. */
+ fixup_mova (mova);
+ return find_barrier (0, 0, mova);
+ }
+ else
+ {
+ /* Insert the constant pool table before the mova instruction,
+ to prevent the mova label reference from going out of range. */
+ from = mova;
+ good_barrier = found_barrier = barrier_before_mova;
+ }
+ }
+
+ if (found_barrier)
+ {
+ if (good_barrier && next_real_insn (found_barrier))
+ found_barrier = good_barrier;
+ }
+ else
+ {
+ /* We didn't find a barrier in time to dump our stuff,
+ so we'll make one. */
+ rtx_code_label *label = gen_label_rtx ();
+
+ /* Don't emit a constant table in the middle of insns for
+ casesi_worker_2. This is a bit overkill but is enough
+ because casesi_worker_2 wouldn't appear so frequently. */
+ if (last_symoff)
+ from = last_symoff;
+
+ /* If we exceeded the range, then we must back up over the last
+ instruction we looked at. Otherwise, we just need to undo the
+ NEXT_INSN at the end of the loop. */
+ if (PREV_INSN (from) != orig
+ && (count_hi > hi_limit || count_si > si_limit))
+ from = PREV_INSN (PREV_INSN (from));
+ else
+ from = PREV_INSN (from);
+
+ /* Don't emit a constant table int the middle of global pointer setting,
+ since that that would move the addressing base GOT into another table.
+ We need the first mov instruction before the _GLOBAL_OFFSET_TABLE_
+ in the pool anyway, so just move up the whole constant pool.
+
+ However, avoid doing so when the last single GOT mov is the starting
+ insn itself. Going past above the start insn would create a negative
+ offset, causing errors. */
+ if (last_got && last_got != orig)
+ from = PREV_INSN (last_got);
+
+ /* Don't insert the constant pool table at the position which
+ may be the landing pad. */
+ if (flag_exceptions
+ && CALL_P (from)
+ && find_reg_note (from, REG_EH_REGION, NULL_RTX))
+ from = PREV_INSN (from);
+
+ /* Walk back to be just before any jump or label.
+ Putting it before a label reduces the number of times the branch
+ around the constant pool table will be hit. Putting it before
+ a jump makes it more likely that the bra delay slot will be
+ filled. */
+ while (NOTE_P (from) || JUMP_P (from) || LABEL_P (from))
+ from = PREV_INSN (from);
+
+ if (CALL_P (from))
+ {
+ bool sibcall_p = SIBLING_CALL_P (from);
+
+ /* If FROM was a sibling call, then we know that control
+ will not return. In fact, we were guaranteed to hit
+ a barrier before another real insn.
+
+ The jump around the constant pool is unnecessary. It
+ costs space, but more importantly it confuses dwarf2cfi
+ generation. */
+ if (sibcall_p)
+ return emit_barrier_after (from);
+ }
+
+ from = emit_jump_insn_after (gen_jump (label), from);
+ JUMP_LABEL (from) = label;
+ LABEL_NUSES (label) = 1;
+ found_barrier = emit_barrier_after (from);
+ emit_label_after (label, found_barrier);
+ }
+
+ return found_barrier;
+}
+
+/* If the instruction INSN is implemented by a special function, and we can
+ positively find the register that is used to call the sfunc, and this
+ register is not used anywhere else in this instruction - except as the
+ destination of a set, return this register; else, return 0. */
+rtx
+sfunc_uses_reg (rtx_insn *insn)
+{
+ int i;
+ rtx pattern, part, reg_part, reg;
+
+ if (!NONJUMP_INSN_P (insn))
+ return NULL_RTX;
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != PARALLEL || get_attr_type (insn) != TYPE_SFUNC)
+ return NULL_RTX;
+
+ for (reg_part = NULL_RTX, i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+ {
+ part = XVECEXP (pattern, 0, i);
+ if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == SImode)
+ reg_part = part;
+ }
+ if (! reg_part)
+ return NULL_RTX;
+ reg = XEXP (reg_part, 0);
+ for (int i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+ {
+ part = XVECEXP (pattern, 0, i);
+ if (part == reg_part || GET_CODE (part) == CLOBBER)
+ continue;
+ if (reg_mentioned_p (reg, ((GET_CODE (part) == SET
+ && REG_P (SET_DEST (part)))
+ ? SET_SRC (part) : part)))
+ return NULL_RTX;
+ }
+ return reg;
+}
+
+/* See if the only way in which INSN uses REG is by calling it, or by
+ setting it while calling it. Set *SET to a SET rtx if the register
+ is set by INSN. */
+static bool
+noncall_uses_reg (rtx reg, rtx_insn *insn, rtx *set)
+{
+ *set = NULL_RTX;
+
+ rtx reg2 = sfunc_uses_reg (insn);
+ if (reg2 && REGNO (reg2) == REGNO (reg))
+ {
+ rtx pattern = single_set (insn);
+ if (pattern
+ && REG_P (SET_DEST (pattern))
+ && REGNO (reg) == REGNO (SET_DEST (pattern)))
+ *set = pattern;
+ return false;
+ }
+ if (!CALL_P (insn))
+ {
+ /* We don't use rtx_equal_p because we don't care if the mode is
+ different. */
+ rtx pattern = single_set (insn);
+ if (pattern
+ && REG_P (SET_DEST (pattern))
+ && REGNO (reg) == REGNO (SET_DEST (pattern)))
+ {
+ rtx par, part;
+ int i;
+
+ *set = pattern;
+ par = PATTERN (insn);
+ if (GET_CODE (par) == PARALLEL)
+ for (i = XVECLEN (par, 0) - 1; i >= 0; i--)
+ {
+ part = XVECEXP (par, 0, i);
+ if (GET_CODE (part) != SET && reg_mentioned_p (reg, part))
+ return true;
+ }
+ return reg_mentioned_p (reg, SET_SRC (pattern));
+ }
+
+ return true;
+ }
+
+ rtx pattern = PATTERN (insn);
+
+ if (GET_CODE (pattern) == PARALLEL)
+ {
+ for (int i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+ if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
+ return true;
+ pattern = XVECEXP (pattern, 0, 0);
+ }
+
+ if (GET_CODE (pattern) == SET)
+ {
+ if (reg_mentioned_p (reg, SET_DEST (pattern)))
+ {
+ /* We don't use rtx_equal_p, because we don't care if the
+ mode is different. */
+ if (!REG_P (SET_DEST (pattern))
+ || REGNO (reg) != REGNO (SET_DEST (pattern)))
+ return true;
+
+ *set = pattern;
+ }
+
+ pattern = SET_SRC (pattern);
+ }
+
+ if (GET_CODE (pattern) != CALL
+ || !MEM_P (XEXP (pattern, 0))
+ || ! rtx_equal_p (reg, XEXP (XEXP (pattern, 0), 0)))
+ return true;
+
+ return false;
+}
+
+/* Given a X, a pattern of an insn or a part of it, return a mask of used
+ general registers. Bits 0..15 mean that the respective registers
+ are used as inputs in the instruction. Bits 16..31 mean that the
+ registers 0..15, respectively, are used as outputs, or are clobbered.
+ IS_DEST should be set to 16 if X is the destination of a SET, else to 0. */
+int
+regs_used (rtx x, int is_dest)
+{
+ enum rtx_code code;
+ const char *fmt;
+ int used = 0;
+
+ if (! x)
+ return used;
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case REG:
+ if (REGNO (x) < 16)
+ return (((1 << hard_regno_nregs (0, GET_MODE (x))) - 1)
+ << (REGNO (x) + is_dest));
+ return 0;
+ case SUBREG:
+ {
+ rtx y = SUBREG_REG (x);
+
+ if (!REG_P (y))
+ break;
+ if (REGNO (y) < 16)
+ return (((1 << hard_regno_nregs (0, GET_MODE (x))) - 1)
+ << (REGNO (y) +
+ subreg_regno_offset (REGNO (y),
+ GET_MODE (y),
+ SUBREG_BYTE (x),
+ GET_MODE (x)) + is_dest));
+ return 0;
+ }
+ case SET:
+ return regs_used (SET_SRC (x), 0) | regs_used (SET_DEST (x), 16);
+ case RETURN:
+ /* If there was a return value, it must have been indicated with USE. */
+ return 0x00ffff00;
+ case CLOBBER:
+ is_dest = 1;
+ break;
+ case MEM:
+ is_dest = 0;
+ break;
+ case CALL:
+ used |= 0x00ff00f0;
+ break;
+ default:
+ break;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+
+ for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
+ used |= regs_used (XVECEXP (x, i, j), is_dest);
+ }
+ else if (fmt[i] == 'e')
+ used |= regs_used (XEXP (x, i), is_dest);
+ }
+ return used;
+}
+
+/* Create an instruction that prevents redirection of a conditional branch
+ to the destination of the JUMP with address ADDR.
+ If the branch needs to be implemented as an indirect jump, try to find
+ a scratch register for it.
+ If NEED_BLOCK is 0, don't do anything unless we need a scratch register.
+ If any preceding insn that doesn't fit into a delay slot is good enough,
+ pass 1. Pass 2 if a definite blocking insn is needed.
+ -1 is used internally to avoid deep recursion.
+ If a blocking instruction is made or recognized, return it. */
+static rtx_insn *
+gen_block_redirect (rtx_insn *jump, int addr, int need_block)
+{
+ int dead = 0;
+ rtx_insn *prev = prev_nonnote_insn (jump);
+
+ /* First, check if we already have an instruction that satisfies our need. */
+ if (prev && NONJUMP_INSN_P (prev) && ! prev->deleted ())
+ {
+ if (INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
+ return prev;
+ if (GET_CODE (PATTERN (prev)) == USE
+ || GET_CODE (PATTERN (prev)) == CLOBBER
+ || get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
+ prev = jump;
+ else if ((need_block &= ~1) < 0)
+ return prev;
+ else if (recog_memoized (prev) == CODE_FOR_block_branch_redirect)
+ need_block = 0;
+ }
+ if (GET_CODE (PATTERN (jump)) == RETURN)
+ {
+ if (! need_block)
+ return prev;
+ /* Reorg even does nasty things with return insns that cause branches
+ to go out of range - see find_end_label and callers. */
+ return emit_insn_before (gen_block_branch_redirect (const0_rtx) , jump);
+ }
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0);
+ /* If the branch is out of range, try to find a scratch register for it. */
+ if (optimize
+ && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
+ > 4092 + 4098))
+ {
+ rtx_insn *scan;
+ /* Don't look for the stack pointer as a scratch register,
+ it would cause trouble if an interrupt occurred. */
+ unsigned attempt = 0x7fff, used;
+ int jump_left = flag_expensive_optimizations + 1;
+
+ /* It is likely that the most recent eligible instruction is wanted for
+ the delay slot. Therefore, find out which registers it uses, and
+ try to avoid using them. */
+
+ for (scan = jump; (scan = PREV_INSN (scan)); )
+ {
+ if (scan->deleted ())
+ continue;
+ rtx_code code = GET_CODE (scan);
+ if (code == CODE_LABEL || code == JUMP_INSN)
+ break;
+ if (code == INSN
+ && GET_CODE (PATTERN (scan)) != USE
+ && GET_CODE (PATTERN (scan)) != CLOBBER
+ && get_attr_in_delay_slot (scan) == IN_DELAY_SLOT_YES)
+ {
+ attempt &= ~regs_used (PATTERN (scan), 0);
+ break;
+ }
+ }
+ for (used = dead = 0, scan = JUMP_LABEL_AS_INSN (jump);
+ (scan = NEXT_INSN (scan)); )
+ {
+ if (scan->deleted ())
+ continue;
+ rtx_code code = GET_CODE (scan);
+ if (INSN_P (scan))
+ {
+ used |= regs_used (PATTERN (scan), 0);
+ if (code == CALL_INSN)
+ used |= regs_used (CALL_INSN_FUNCTION_USAGE (scan), 0);
+ dead |= (used >> 16) & ~used;
+ if (dead & attempt)
+ {
+ dead &= attempt;
+ break;
+ }
+ if (code == JUMP_INSN)
+ {
+ if (jump_left-- && simplejump_p (scan))
+ scan = JUMP_LABEL_AS_INSN (scan);
+ else
+ break;
+ }
+ }
+ }
+ /* Mask out the stack pointer again, in case it was
+ the only 'free' register we have found. */
+ dead &= 0x7fff;
+ }
+ /* If the immediate destination is still in range, check for possible
+ threading with a jump beyond the delay slot insn.
+ Don't check if we are called recursively; the jump has been or will be
+ checked in a different invocation then. */
+
+ else if (optimize && need_block >= 0)
+ {
+ rtx_insn *next = next_active_insn (as_a<rtx_insn *> (dest));
+ next = next_active_insn (next);
+ if (next && JUMP_P (next)
+ && GET_CODE (PATTERN (next)) == SET
+ && recog_memoized (next) == CODE_FOR_jump_compact)
+ {
+ dest = JUMP_LABEL (next);
+ if (dest
+ && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
+ > 4092 + 4098))
+ gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), -1);
+ }
+ }
+
+ if (dead)
+ {
+ rtx reg = gen_rtx_REG (SImode, exact_log2 (dead & -dead));
+
+ /* It would be nice if we could convert the jump into an indirect
+ jump / far branch right now, and thus exposing all constituent
+ instructions to further optimization. However, reorg uses
+ simplejump_p to determine if there is an unconditional jump where
+ it should try to schedule instructions from the target of the
+ branch; simplejump_p fails for indirect jumps even if they have
+ a JUMP_LABEL. */
+ rtx_insn *insn = emit_insn_before (gen_indirect_jump_scratch
+ (reg, GEN_INT (unspec_bbr_uid++)),
+ jump);
+ /* ??? We would like this to have the scope of the jump, but that
+ scope will change when a delay slot insn of an inner scope is added.
+ Hence, after delay slot scheduling, we'll have to expect
+ NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and
+ the jump. */
+
+ INSN_LOCATION (insn) = INSN_LOCATION (jump);
+ INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch;
+ return insn;
+ }
+ else if (need_block)
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ return emit_insn_before (gen_block_branch_redirect
+ (GEN_INT (unspec_bbr_uid++)),
+ jump);
+ return prev;
+}
+
+#define CONDJUMP_MIN -252
+#define CONDJUMP_MAX 262
+struct far_branch
+{
+ /* A label (to be placed) in front of the jump
+ that jumps to our ultimate destination. */
+ rtx_insn *near_label;
+ /* Where we are going to insert it if we cannot move the jump any farther,
+ or the jump itself if we have picked up an existing jump. */
+ rtx_insn *insert_place;
+ /* The ultimate destination. */
+ rtx_insn *far_label;
+ struct far_branch *prev;
+ /* If the branch has already been created, its address;
+ else the address of its first prospective user. */
+ int address;
+};
+
+enum mdep_reorg_phase_e mdep_reorg_phase;
+
+static void
+gen_far_branch (struct far_branch *bp)
+{
+ rtx_insn *insn = bp->insert_place;
+ rtx_jump_insn *jump;
+ rtx_code_label *label = gen_label_rtx ();
+
+ emit_label_after (label, insn);
+ if (bp->far_label)
+ {
+ jump = emit_jump_insn_after (gen_jump (bp->far_label), insn);
+ LABEL_NUSES (bp->far_label)++;
+ }
+ else
+ jump = emit_jump_insn_after (gen_return (), insn);
+
+ /* Emit a barrier so that reorg knows that any following instructions
+ are not reachable via a fall-through path.
+ But don't do this when not optimizing, since we wouldn't suppress the
+ alignment for the barrier then, and could end up with out-of-range
+ pc-relative loads. */
+ if (optimize)
+ emit_barrier_after (jump);
+ emit_label_after (bp->near_label, insn);
+
+ if (bp->far_label)
+ JUMP_LABEL (jump) = bp->far_label;
+ else
+ {
+ rtx pat = PATTERN (jump);
+ gcc_assert (ANY_RETURN_P (pat));
+ JUMP_LABEL (jump) = pat;
+ }
+
+ bool ok = invert_jump (as_a <rtx_jump_insn *> (insn), label, 1);
+ gcc_assert (ok);
+
+ /* If we are branching around a jump (rather than a return), prevent
+ reorg from using an insn from the jump target as the delay slot insn -
+ when reorg did this, it pessimized code (we rather hide the delay slot)
+ and it could cause branches to go out of range. */
+ if (bp->far_label)
+ (emit_insn_after
+ (gen_stuff_delay_slot
+ (GEN_INT (unspec_bbr_uid++),
+ GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)),
+ insn));
+ /* Prevent reorg from undoing our splits. */
+ gen_block_redirect (jump, bp->address += 2, 2);
+}
+
+/* Fix up ADDR_DIFF_VECs. */
+void
+fixup_addr_diff_vecs (rtx_insn *first)
+{
+ rtx_insn *insn;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx vec_lab, pat, prevpat, x, braf_label;
+ rtx_insn *prev;
+
+ if (! JUMP_TABLE_DATA_P (insn)
+ || GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)
+ continue;
+ pat = PATTERN (insn);
+ vec_lab = XEXP (XEXP (pat, 0), 0);
+
+ /* Search the matching casesi_jump_2. */
+ for (prev = as_a <rtx_insn *> (vec_lab); ; prev = PREV_INSN (prev))
+ {
+ if (!JUMP_P (prev))
+ continue;
+ prevpat = PATTERN (prev);
+ if (GET_CODE (prevpat) != PARALLEL || XVECLEN (prevpat, 0) != 2)
+ continue;
+ x = XVECEXP (prevpat, 0, 1);
+ if (GET_CODE (x) != USE)
+ continue;
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == LABEL_REF && XEXP (x, 0) == vec_lab)
+ break;
+ }
+ /* FIXME: This is a bug in the optimizer, but it seems harmless
+ to just avoid panicing. */
+ if (!prev)
+ continue;
+
+ /* Emit the reference label of the braf where it belongs, right after
+ the casesi_jump_2 (i.e. braf). */
+ braf_label = XEXP (XEXP (SET_SRC (XVECEXP (prevpat, 0, 0)), 1), 0);
+ emit_label_after (as_a <rtx_insn *> (braf_label), prev);
+
+ /* Fix up the ADDR_DIF_VEC to be relative
+ to the reference address of the braf. */
+ XEXP (XEXP (pat, 0), 0) = braf_label;
+ }
+}
+
+/* BARRIER_OR_LABEL is either a BARRIER or a CODE_LABEL immediately following
+ a barrier. Return the base 2 logarithm of the desired alignment. */
+int
+barrier_align (rtx_insn *barrier_or_label)
+{
+ if (! barrier_or_label)
+ return 0;
+
+ if (LABEL_P (barrier_or_label)
+ && NEXT_INSN (barrier_or_label)
+ && JUMP_TABLE_DATA_P (NEXT_INSN (barrier_or_label)))
+ return 2;
+
+ if (BARRIER_P (barrier_or_label)
+ && PREV_INSN (barrier_or_label)
+ && JUMP_TABLE_DATA_P (PREV_INSN (barrier_or_label)))
+ {
+ rtx pat = PATTERN (PREV_INSN (barrier_or_label));
+ /* If this is a very small table, we want to keep the alignment after
+ the table to the minimum for proper code alignment. */
+ return ((optimize_size
+ || ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
+ <= (unsigned) 1 << (CACHE_LOG - 2)))
+ ? 1 : align_jumps.levels[0].log);
+ }
+
+ rtx_insn *next = next_active_insn (barrier_or_label);
+
+ if (! next)
+ return 0;
+
+ rtx pat = PATTERN (next);
+
+ if (GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_ALIGN)
+ /* This is a barrier in front of a constant table. */
+ return 0;
+
+ if (optimize_size)
+ return 0;
+
+ if (! TARGET_SH2 || ! optimize)
+ return align_jumps.levels[0].log;
+
+ /* When fixing up pcloads, a constant table might be inserted just before
+ the basic block that ends with the barrier. Thus, we can't trust the
+ instruction lengths before that. */
+ if (mdep_reorg_phase > SH_FIXUP_PCLOAD)
+ {
+ /* Check if there is an immediately preceding branch to the insn beyond
+ the barrier. We must weight the cost of discarding useful information
+ from the current cache line when executing this branch and there is
+ an alignment, against that of fetching unneeded insn in front of the
+ branch target when there is no alignment. */
+
+ /* There are two delay_slot cases to consider. One is the simple case
+ where the preceding branch is to the insn beyond the barrier (simple
+ delay slot filling), and the other is where the preceding branch has
+ a delay slot that is a duplicate of the insn after the barrier
+ (fill_eager_delay_slots) and the branch is to the insn after the insn
+ after the barrier. */
+
+ int slot, credit;
+ bool jump_to_next = false;
+
+ /* Skip to the insn before the JUMP_INSN before the barrier under
+ investigation. */
+ rtx_insn *prev = prev_real_insn (prev_active_insn (barrier_or_label));
+
+ for (slot = 2, credit = (1 << (CACHE_LOG - 2)) + 2;
+ credit >= 0 && prev && NONJUMP_INSN_P (prev);
+ prev = prev_real_insn (prev))
+ {
+ jump_to_next = false;
+ if (GET_CODE (PATTERN (prev)) == USE
+ || GET_CODE (PATTERN (prev)) == CLOBBER)
+ continue;
+ if (rtx_sequence *prev_seq = dyn_cast <rtx_sequence *> (PATTERN (prev)))
+ {
+ prev = prev_seq->insn (1);
+ if (INSN_UID (prev) == INSN_UID (next))
+ {
+ /* Delay slot was filled with insn at jump target. */
+ jump_to_next = true;
+ continue;
+ }
+ }
+
+ if (slot &&
+ get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
+ slot = 0;
+ credit -= get_attr_length (prev);
+ }
+ if (prev && jump_to_label_p (prev))
+ {
+ rtx_insn *x;
+ if (jump_to_next
+ || next_real_insn (JUMP_LABEL_AS_INSN (prev)) == next
+ /* If relax_delay_slots() decides NEXT was redundant
+ with some previous instruction, it will have
+ redirected PREV's jump to the following insn. */
+ || JUMP_LABEL (prev) == next_nonnote_insn (next)
+ /* There is no upper bound on redundant instructions
+ that might have been skipped, but we must not put an
+ alignment where none had been before. */
+ || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),
+ (INSN_P (x)
+ && (INSN_CODE (x) == CODE_FOR_block_branch_redirect
+ || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch
+ || INSN_CODE (x) == CODE_FOR_stuff_delay_slot))))
+ {
+ rtx pat = PATTERN (prev);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
+ return 0;
+ }
+ }
+ }
+
+ return align_jumps.levels[0].log;
+}
+
+/* If we are inside a phony loop, almost any kind of label can turn up as the
+ first one in the loop. Aligning a braf label causes incorrect switch
+ destination addresses; we can detect braf labels because they are
+ followed by a BARRIER.
+ Applying loop alignment to small constant or switch tables is a waste
+ of space, so we suppress this too. */
+int
+sh_loop_align (rtx_insn *label)
+{
+ rtx_insn *next = label;
+
+ if (! optimize || optimize_size)
+ return 0;
+
+ do
+ next = next_nonnote_insn (next);
+ while (next && LABEL_P (next));
+
+ if (! next
+ || ! INSN_P (next)
+ || recog_memoized (next) == CODE_FOR_consttable_2)
+ return 0;
+
+ return align_loops.levels[0].log;
+}
+
+/* Do a final pass over the function, just before delayed branch
+ scheduling. */
+static void
+sh_reorg (void)
+{
+ rtx_insn *first, *insn, *mova = NULL;
+ int num_mova;
+ rtx r0_rtx = gen_rtx_REG (Pmode, 0);
+ rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
+
+ first = get_insns ();
+ max_labelno_before_reorg = max_label_num ();
+
+ /* We must split call insns before introducing `mova's. If we're
+ optimizing, they'll have already been split. Otherwise, make
+ sure we don't split them too late. */
+ if (! optimize)
+ split_all_insns_noflow ();
+
+ /* If relaxing, generate pseudo-ops to associate function calls with
+ the symbols they call. It does no harm to not generate these
+ pseudo-ops. However, when we can generate them, it enables the
+ linker to potentially relax the jsr to a bsr, and eliminate the
+ register load and, possibly, the constant pool entry. */
+
+ mdep_reorg_phase = SH_INSERT_USES_LABELS;
+ if (TARGET_RELAX)
+ {
+ /* Remove all REG_LABEL_OPERAND notes. We want to use them for our
+ own purposes. This works because none of the remaining passes
+ need to look at them.
+
+ ??? But it may break in the future. We should use a machine
+ dependent REG_NOTE, or some other approach entirely. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ {
+ rtx note;
+
+ while ((note = find_reg_note (insn, REG_LABEL_OPERAND,
+ NULL_RTX)) != 0)
+ remove_note (insn, note);
+ }
+ }
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx pattern, reg, set, dies;
+ rtx_code_label *label;
+ rtx_insn *link, *scan;
+ int rescan = 0, foundinsn = 0;
+
+ if (CALL_P (insn))
+ {
+ pattern = PATTERN (insn);
+
+ if (GET_CODE (pattern) == PARALLEL)
+ pattern = XVECEXP (pattern, 0, 0);
+ if (GET_CODE (pattern) == SET)
+ pattern = SET_SRC (pattern);
+
+ if (GET_CODE (pattern) != CALL
+ || !MEM_P (XEXP (pattern, 0)))
+ continue;
+
+ reg = XEXP (XEXP (pattern, 0), 0);
+ }
+ else
+ {
+ reg = sfunc_uses_reg (insn);
+ if (! reg)
+ continue;
+ }
+
+ if (!REG_P (reg))
+ continue;
+
+ /* Try scanning backward to find where the register is set. */
+ link = NULL;
+ for (scan = PREV_INSN (insn);
+ scan && !LABEL_P (scan);
+ scan = PREV_INSN (scan))
+ {
+ if (! INSN_P (scan))
+ continue;
+
+ if (! reg_mentioned_p (reg, scan))
+ continue;
+
+ if (noncall_uses_reg (reg, scan, &set))
+ break;
+
+ if (set)
+ {
+ link = scan;
+ break;
+ }
+ }
+
+ if (! link)
+ continue;
+
+ /* The register is set at LINK. */
+
+ /* We can only optimize the function call if the register is
+ being set to a symbol. In theory, we could sometimes
+ optimize calls to a constant location, but the assembler
+ and linker do not support that at present. */
+ if (GET_CODE (SET_SRC (set)) != SYMBOL_REF
+ && GET_CODE (SET_SRC (set)) != LABEL_REF)
+ continue;
+
+ /* Scan forward from LINK to the place where REG dies, and
+ make sure that the only insns which use REG are
+ themselves function calls. */
+
+ /* ??? This doesn't work for call targets that were allocated
+ by reload, since there may not be a REG_DEAD note for the
+ register. */
+
+ dies = NULL_RTX;
+ for (scan = NEXT_INSN (link); scan; scan = NEXT_INSN (scan))
+ {
+ rtx scanset;
+
+ /* Don't try to trace forward past a CODE_LABEL if we haven't
+ seen INSN yet. Ordinarily, we will only find the setting insn
+ if it is in the same basic block. However,
+ cross-jumping can insert code labels in between the load and
+ the call, and can result in situations where a single call
+ insn may have two targets depending on where we came from. */
+
+ if (LABEL_P (scan) && ! foundinsn)
+ break;
+
+ if (! INSN_P (scan))
+ continue;
+
+ /* Don't try to trace forward past a JUMP. To optimize
+ safely, we would have to check that all the
+ instructions at the jump destination did not use REG. */
+
+ if (JUMP_P (scan))
+ break;
+
+ if (! reg_mentioned_p (reg, scan))
+ continue;
+
+ if (noncall_uses_reg (reg, scan, &scanset))
+ break;
+
+ if (scan == insn)
+ foundinsn = 1;
+
+ if (scan != insn
+ && (CALL_P (scan) || sfunc_uses_reg (scan)))
+ {
+ /* There is a function call to this register other
+ than the one we are checking. If we optimize
+ this call, we need to rescan again below. */
+ rescan = 1;
+ }
+
+ /* ??? We shouldn't have to worry about SCANSET here.
+ We should just be able to check for a REG_DEAD note
+ on a function call. However, the REG_DEAD notes are
+ apparently not dependable around libcalls; c-torture
+ execute/920501-2 is a test case. If SCANSET is set,
+ then this insn sets the register, so it must have
+ died earlier. Unfortunately, this will only handle
+ the cases in which the register is, in fact, set in a
+ later insn. */
+
+ /* ??? We shouldn't have to use FOUNDINSN here.
+ This dates back to when we used LOG_LINKS to find
+ the most recent insn which sets the register. */
+
+ if (foundinsn
+ && (scanset
+ || find_reg_note (scan, REG_DEAD, reg)))
+ {
+ dies = scan;
+ break;
+ }
+ }
+
+ if (! dies)
+ {
+ /* Either there was a branch, or some insn used REG
+ other than as a function call address. */
+ continue;
+ }
+
+ /* Create a code label, and put it in a REG_LABEL_OPERAND note
+ on the insn which sets the register, and on each call insn
+ which uses the register. In final_prescan_insn we look for
+ the REG_LABEL_OPERAND notes, and output the appropriate label
+ or pseudo-op. */
+
+ label = gen_label_rtx ();
+ add_reg_note (link, REG_LABEL_OPERAND, label);
+ add_reg_note (insn, REG_LABEL_OPERAND, label);
+ if (rescan)
+ {
+ scan = link;
+ do
+ {
+ rtx reg2;
+
+ scan = NEXT_INSN (scan);
+ if (scan != insn
+ && ((CALL_P (scan)
+ && reg_mentioned_p (reg, scan))
+ || ((reg2 = sfunc_uses_reg (scan))
+ && REGNO (reg2) == REGNO (reg))))
+ add_reg_note (scan, REG_LABEL_OPERAND, label);
+ }
+ while (scan != dies);
+ }
+ }
+ }
+
+ if (TARGET_SH2)
+ fixup_addr_diff_vecs (first);
+
+ if (optimize)
+ {
+ mdep_reorg_phase = SH_SHORTEN_BRANCHES0;
+ shorten_branches (first);
+ }
+
+ /* Scan the function looking for move instructions which have to be
+ changed to pc-relative loads and insert the literal tables. */
+ mdep_reorg_phase = SH_FIXUP_PCLOAD;
+ for (insn = first, num_mova = 0; insn; insn = NEXT_INSN (insn))
+ {
+ if (mova_p (insn))
+ {
+ /* ??? basic block reordering can move a switch table dispatch
+ below the switch table. Check if that has happened.
+ We only have the addresses available when optimizing; but then,
+ this check shouldn't be needed when not optimizing. */
+ if (!untangle_mova (&num_mova, &mova, insn))
+ {
+ insn = mova;
+ num_mova = 0;
+ }
+ }
+ else if (JUMP_TABLE_DATA_P (insn)
+ && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+ && num_mova
+ /* ??? loop invariant motion can also move a mova out of a
+ loop. Since loop does this code motion anyway, maybe we
+ should wrap UNSPEC_MOVA into a CONST, so that reload can
+ move it back. */
+ && ((num_mova > 1
+ && GET_MODE (prev_nonnote_insn (insn)) == VOIDmode)
+ || (prev_nonnote_insn (insn)
+ == XEXP (MOVA_LABELREF (mova), 0))))
+ {
+ rtx_insn *scan;
+ int total;
+
+ num_mova--;
+
+ /* Some code might have been inserted between the mova and
+ its ADDR_DIFF_VEC. Check if the mova is still in range. */
+ for (scan = mova, total = 0; scan != insn; scan = NEXT_INSN (scan))
+ total += get_attr_length (scan);
+
+ /* range of mova is 1020, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 in case pc is 2
+ byte aligned. Possible alignment needed for the ADDR_DIFF_VEC
+ cancels out with alignment effects of the mova itself. */
+ if (total > 1022)
+ {
+ /* Change the mova into a load, and restart scanning
+ there. broken_move will then return true for mova. */
+ fixup_mova (mova);
+ insn = mova;
+ }
+ }
+ if (broken_move (insn)
+ || (NONJUMP_INSN_P (insn)
+ && recog_memoized (insn) == CODE_FOR_casesi_worker_2))
+ {
+ rtx_insn *scan;
+ /* Scan ahead looking for a barrier to stick the constant table
+ behind. */
+ rtx_insn *barrier = find_barrier (num_mova, mova, insn);
+ rtx_insn *last_float_move = NULL;
+ rtx last_float = 0, *last_float_addr = NULL;
+ int need_aligned_label = 0;
+
+ if (num_mova && ! mova_p (mova))
+ {
+ /* find_barrier had to change the first mova into a
+ pcload; thus, we have to start with this new pcload. */
+ insn = mova;
+ num_mova = 0;
+ }
+ /* Now find all the moves between the points and modify them. */
+ for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
+ {
+ if (LABEL_P (scan))
+ last_float = 0;
+ if (NONJUMP_INSN_P (scan)
+ && recog_memoized (scan) == CODE_FOR_casesi_worker_2)
+ need_aligned_label = 1;
+ if (broken_move (scan))
+ {
+ rtx *patp = &PATTERN (scan), pat = *patp;
+ rtx src, dst;
+ rtx lab;
+ rtx newsrc;
+ machine_mode mode;
+
+ if (GET_CODE (pat) == PARALLEL)
+ patp = &XVECEXP (pat, 0, 0), pat = *patp;
+ src = SET_SRC (pat);
+ dst = SET_DEST (pat);
+ mode = GET_MODE (dst);
+
+ if (mode == SImode && satisfies_constraint_I16 (src)
+ && REGNO (dst) != FPUL_REG)
+ {
+ int offset = 0;
+
+ mode = HImode;
+ while (GET_CODE (dst) == SUBREG)
+ {
+ offset += subreg_regno_offset (REGNO (SUBREG_REG (dst)),
+ GET_MODE (SUBREG_REG (dst)),
+ SUBREG_BYTE (dst),
+ GET_MODE (dst));
+ dst = SUBREG_REG (dst);
+ }
+ dst = gen_rtx_REG (HImode, REGNO (dst) + offset);
+ }
+ if (REG_P (dst) && FP_ANY_REGISTER_P (REGNO (dst)))
+ {
+ /* This must be an insn that clobbers r0. */
+ rtx *clobberp = &XVECEXP (PATTERN (scan), 0,
+ XVECLEN (PATTERN (scan), 0)
+ - 1);
+ rtx clobber = *clobberp;
+
+ gcc_assert (GET_CODE (clobber) == CLOBBER
+ && rtx_equal_p (XEXP (clobber, 0), r0_rtx));
+
+ if (last_float
+ && reg_set_between_p (r0_rtx, last_float_move, scan))
+ last_float = 0;
+ lab = add_constant (src, mode, last_float);
+ if (lab)
+ emit_insn_before (gen_mova (lab), scan);
+ else
+ {
+ /* There will be a REG_UNUSED note for r0 on
+ LAST_FLOAT_MOVE; we have to change it to REG_INC,
+ lest reorg:mark_target_live_regs will not
+ consider r0 to be used, and we end up with delay
+ slot insn in front of SCAN that clobbers r0. */
+ rtx note
+ = find_regno_note (last_float_move, REG_UNUSED, 0);
+
+ /* If we are not optimizing, then there may not be
+ a note. */
+ if (note)
+ PUT_REG_NOTE_KIND (note, REG_INC);
+
+ *last_float_addr = r0_inc_rtx;
+ }
+ last_float_move = scan;
+ last_float = src;
+ newsrc = gen_const_mem (mode,
+ (((TARGET_SH4 && ! TARGET_FMOVD)
+ || REGNO (dst) == FPUL_REG)
+ ? r0_inc_rtx
+ : r0_rtx));
+ last_float_addr = &XEXP (newsrc, 0);
+
+ /* Remove the clobber of r0. */
+ *clobberp = gen_rtx_CLOBBER (GET_MODE (clobber),
+ gen_rtx_SCRATCH (Pmode));
+ }
+ /* This is a mova needing a label. Create it. */
+ else if (GET_CODE (src) == UNSPEC
+ && XINT (src, 1) == UNSPEC_MOVA
+ && GET_CODE (XVECEXP (src, 0, 0)) == CONST)
+ {
+ lab = add_constant (XVECEXP (src, 0, 0), mode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_rtx_UNSPEC (SImode,
+ gen_rtvec (1, newsrc),
+ UNSPEC_MOVA);
+ }
+ else if (GET_CODE (src) == UNSPEC_VOLATILE
+ && XINT (src, 1) == UNSPECV_SP_SWITCH_B)
+ {
+ newsrc = XVECEXP (src, 0, 0);
+ XVECEXP (src, 0, 0) = gen_const_mem (mode, newsrc);
+ INSN_CODE (scan) = -1;
+ continue;
+ }
+ else
+ {
+ lab = add_constant (src, mode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_const_mem (mode, newsrc);
+ }
+ *patp = gen_rtx_SET (dst, newsrc);
+ INSN_CODE (scan) = -1;
+ }
+ }
+ dump_table (need_aligned_label ? insn : 0, barrier);
+ insn = barrier;
+ }
+ }
+ label_ref_list_d_pool.release ();
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ PUT_MODE (insn, VOIDmode);
+
+ mdep_reorg_phase = SH_SHORTEN_BRANCHES1;
+ INSN_ADDRESSES_FREE ();
+ split_branches (first);
+
+ /* The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
+ also has an effect on the register that holds the address of the sfunc.
+ Insert an extra dummy insn in front of each sfunc that pretends to
+ use this register. */
+ if (flag_delayed_branch)
+ {
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx reg = sfunc_uses_reg (insn);
+
+ if (! reg)
+ continue;
+ emit_insn_before (gen_use_sfunc_addr (reg), insn);
+ }
+ }
+ mdep_reorg_phase = SH_AFTER_MDEP_REORG;
+}
+
+/* Return the UID of the insn that follows the specified label. */
+int
+get_dest_uid (rtx_insn *label, int max_uid)
+{
+ rtx_insn *dest = next_real_insn (label);
+
+ if (! dest)
+ /* This can happen for an undefined label. */
+ return 0;
+ int dest_uid = INSN_UID (dest);
+ /* If this is a newly created branch redirection blocking instruction,
+ we cannot index the branch_uid or insn_addresses arrays with its
+ uid. But then, we won't need to, because the actual destination is
+ the following branch. */
+ while (dest_uid >= max_uid)
+ {
+ dest = NEXT_INSN (dest);
+ dest_uid = INSN_UID (dest);
+ }
+ if (JUMP_P (dest) && GET_CODE (PATTERN (dest)) == RETURN)
+ return 0;
+ return dest_uid;
+}
+
+/* Split condbranches that are out of range. Also add clobbers for
+ scratch registers that are needed in far jumps.
+ We do this before delay slot scheduling, so that it can take our
+ newly created instructions into account. It also allows us to
+ find branches with common targets more easily. */
+static void
+split_branches (rtx_insn *first)
+{
+ rtx_insn *insn;
+ struct far_branch **uid_branch, *far_branch_list = 0;
+ int max_uid = get_max_uid ();
+ int ok;
+
+ /* Find out which branches are out of range. */
+ shorten_branches (first);
+
+ uid_branch = (struct far_branch **) alloca (max_uid * sizeof *uid_branch);
+ memset ((char *) uid_branch, 0, max_uid * sizeof *uid_branch);
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (! INSN_P (insn))
+ continue;
+ else if (insn->deleted ())
+ {
+ /* Shorten_branches would split this instruction again,
+ so transform it into a note. */
+ SET_INSN_DELETED (insn);
+ }
+ else if (JUMP_P (insn))
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_CBRANCH)
+ {
+ rtx_insn *next, *beyond;
+
+ if (get_attr_length (insn) > 4)
+ {
+ rtx src = SET_SRC (PATTERN (insn));
+ rtx_insn *olabel = safe_as_a <rtx_insn *> (XEXP (XEXP (src, 1), 0));
+ int addr = INSN_ADDRESSES (INSN_UID (insn));
+ rtx_insn *label = 0;
+ int dest_uid = get_dest_uid (olabel, max_uid);
+ struct far_branch *bp = uid_branch[dest_uid];
+
+ /* redirect_jump needs a valid JUMP_LABEL, and it might delete
+ the label if the LABEL_NUSES count drops to zero. There is
+ always a jump_optimize pass that sets these values, but it
+ proceeds to delete unreferenced code, and then if not
+ optimizing, to un-delete the deleted instructions, thus
+ leaving labels with too low uses counts. */
+ if (! optimize)
+ {
+ JUMP_LABEL (insn) = olabel;
+ LABEL_NUSES (olabel)++;
+ }
+ if (! bp)
+ {
+ bp = (struct far_branch *) alloca (sizeof *bp);
+ uid_branch[dest_uid] = bp;
+ bp->prev = far_branch_list;
+ far_branch_list = bp;
+ bp->far_label = as_a <rtx_insn *> (
+ XEXP (XEXP (SET_SRC (PATTERN (insn)), 1),
+ 0));
+ LABEL_NUSES (bp->far_label)++;
+ }
+ else
+ {
+ label = bp->near_label;
+ if (! label && bp->address - addr >= CONDJUMP_MIN)
+ {
+ rtx_insn *block = bp->insert_place;
+
+ if (GET_CODE (PATTERN (block)) == RETURN)
+ block = PREV_INSN (block);
+ else
+ block = gen_block_redirect (block,
+ bp->address, 2);
+ label = emit_label_after (gen_label_rtx (),
+ PREV_INSN (block));
+ bp->near_label = label;
+ }
+ else if (label && ! NEXT_INSN (label))
+ {
+ if (addr + 2 - bp->address <= CONDJUMP_MAX)
+ bp->insert_place = insn;
+ else
+ gen_far_branch (bp);
+ }
+ }
+ if (! label
+ || (NEXT_INSN (label) && bp->address - addr < CONDJUMP_MIN))
+ {
+ bp->near_label = label = gen_label_rtx ();
+ bp->insert_place = insn;
+ bp->address = addr;
+ }
+ ok = redirect_jump (as_a <rtx_jump_insn *> (insn), label, 0);
+ gcc_assert (ok);
+ }
+ else
+ {
+ /* get_attr_length (insn) == 2 */
+ /* Check if we have a pattern where reorg wants to redirect
+ the branch to a label from an unconditional branch that
+ is too far away. */
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ /* A syntax error might cause beyond to be NULL_RTX. */
+ rtx temp = XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0);
+ beyond = next_active_insn (as_a<rtx_insn *> (temp));
+
+ if (beyond
+ && (JUMP_P (beyond)
+ || ((beyond = next_active_insn (beyond))
+ && JUMP_P (beyond)))
+ && GET_CODE (PATTERN (beyond)) == SET
+ && recog_memoized (beyond) == CODE_FOR_jump_compact
+ && ((INSN_ADDRESSES
+ (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
+ - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
+ > 252 + 258 + 2))
+ gen_block_redirect (beyond,
+ INSN_ADDRESSES (INSN_UID (beyond)), 1);
+ }
+
+ next = next_active_insn (insn);
+
+ if (next
+ && (JUMP_P (next)
+ || ((next = next_active_insn (next))
+ && JUMP_P (next)))
+ && GET_CODE (PATTERN (next)) == SET
+ && recog_memoized (next) == CODE_FOR_jump_compact
+ && ((INSN_ADDRESSES
+ (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
+ - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
+ > 252 + 258 + 2))
+ gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), 1);
+ }
+ else if (type == TYPE_JUMP || type == TYPE_RETURN)
+ {
+ int addr = INSN_ADDRESSES (INSN_UID (insn));
+ rtx_insn *far_label = 0;
+ int dest_uid = 0;
+ struct far_branch *bp;
+
+ if (type == TYPE_JUMP)
+ {
+ if (CROSSING_JUMP_P (insn))
+ {
+ emit_insn_before (gen_block_branch_redirect (const0_rtx),
+ insn);
+ continue;
+ }
+
+ far_label = as_a <rtx_insn *> (
+ XEXP (SET_SRC (PATTERN (insn)), 0));
+ dest_uid = get_dest_uid (far_label, max_uid);
+ if (! dest_uid)
+ {
+ /* Parse errors can lead to labels outside
+ the insn stream. */
+ if (! NEXT_INSN (far_label))
+ continue;
+
+ if (! optimize)
+ {
+ JUMP_LABEL (insn) = far_label;
+ LABEL_NUSES (far_label)++;
+ }
+ redirect_jump (as_a <rtx_jump_insn *> (insn), ret_rtx, 1);
+ far_label = 0;
+ }
+ }
+ bp = uid_branch[dest_uid];
+ if (! bp)
+ {
+ bp = (struct far_branch *) alloca (sizeof *bp);
+ uid_branch[dest_uid] = bp;
+ bp->prev = far_branch_list;
+ far_branch_list = bp;
+ bp->near_label = 0;
+ bp->far_label = far_label;
+ if (far_label)
+ LABEL_NUSES (far_label)++;
+ }
+ else if (bp->near_label && ! NEXT_INSN (bp->near_label))
+ if (addr - bp->address <= CONDJUMP_MAX)
+ emit_label_after (bp->near_label, PREV_INSN (insn));
+ else
+ {
+ gen_far_branch (bp);
+ bp->near_label = 0;
+ }
+ else
+ bp->near_label = 0;
+ bp->address = addr;
+ bp->insert_place = insn;
+ if (! far_label)
+ emit_insn_before (gen_block_branch_redirect (const0_rtx), insn);
+ else
+ gen_block_redirect (insn, addr, bp->near_label ? 2 : 0);
+ }
+ }
+ /* Generate all pending far branches,
+ and free our references to the far labels. */
+ while (far_branch_list)
+ {
+ if (far_branch_list->near_label
+ && ! NEXT_INSN (far_branch_list->near_label))
+ gen_far_branch (far_branch_list);
+ if (optimize
+ && far_branch_list->far_label
+ && ! --LABEL_NUSES (far_branch_list->far_label))
+ delete_insn (far_branch_list->far_label);
+ far_branch_list = far_branch_list->prev;
+ }
+
+ /* Instruction length information is no longer valid due to the new
+ instructions that have been generated. */
+ init_insn_lengths ();
+}
+
+/* Dump out instruction addresses, which is useful for debugging the
+ constant pool table stuff.
+
+ If relaxing, output the label and pseudo-ops used to link together
+ calls and the instruction which set the registers.
+
+ ??? The addresses printed by this routine for insns are nonsense for
+ insns which are inside of a sequence where none of the inner insns have
+ variable length. This is because the second pass of shorten_branches
+ does not bother to update them. */
+void
+final_prescan_insn (rtx_insn *insn, rtx *opvec ATTRIBUTE_UNUSED,
+ int noperands ATTRIBUTE_UNUSED)
+{
+ if (TARGET_DUMPISIZE)
+ fprintf (asm_out_file, "\n! at %04x\n", INSN_ADDRESSES (INSN_UID (insn)));
+
+ if (TARGET_RELAX)
+ {
+ if (rtx note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX))
+ {
+ rtx pattern = PATTERN (insn);
+ if (GET_CODE (pattern) == PARALLEL)
+ pattern = XVECEXP (pattern, 0, 0);
+ switch (GET_CODE (pattern))
+ {
+ case SET:
+ if (GET_CODE (SET_SRC (pattern)) != CALL
+ && get_attr_type (insn) != TYPE_SFUNC)
+ {
+ targetm.asm_out.internal_label
+ (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
+ break;
+ }
+ /* FALLTHROUGH */
+ case CALL:
+ asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
+ CODE_LABEL_NUMBER (XEXP (note, 0)));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ }
+}
+
+/* Dump out any constants accumulated in the final pass. These will
+ only be labels. */
+const char *
+output_jump_label_table (void)
+{
+ if (pool_size)
+ {
+ fprintf (asm_out_file, "\t.align 2\n");
+ for (int i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (p->label));
+ output_asm_insn (".long %O0", &p->value);
+ }
+ pool_size = 0;
+ }
+
+ return "";
+}
+
+/* A full frame looks like:
+
+ arg-5
+ arg-4
+ [ if current_function_anonymous_args
+ arg-3
+ arg-2
+ arg-1
+ arg-0 ]
+ saved-fp
+ saved-r10
+ saved-r11
+ saved-r12
+ saved-pr
+ local-n
+ ..
+ local-1
+ local-0 <- fp points here.
+
+ Number of bytes pushed for anonymous args, used to pass information
+ between expand_prologue and expand_epilogue.
+
+ Adjust the stack by SIZE bytes. REG holds the rtl of the register to be
+ adjusted. If epilogue_p is zero, this is for a prologue; otherwise, it's
+ for an epilogue and a negative value means that it's for a sibcall
+ epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET of
+ all the registers that are about to be restored, and hence dead. */
+static void
+output_stack_adjust (int size, rtx reg, int epilogue_p,
+ HARD_REG_SET *live_regs_mask, bool frame_p)
+{
+ rtx_insn *(*emit_fn) (rtx) = frame_p ? &emit_frame_insn : &emit_insn;
+ if (size)
+ {
+ HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+/* This test is bogus, as output_stack_adjust is used to re-align the
+ stack. */
+#if 0
+ gcc_assert (!(size % align));
+#endif
+
+ if (CONST_OK_FOR_ADD (size))
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size)));
+ /* Try to do it with two partial adjustments; however, we must make
+ sure that the stack is properly aligned at all times, in case
+ an interrupt occurs between the two partial adjustments. */
+ else if (CONST_OK_FOR_ADD (size / 2 & -align)
+ && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
+ {
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
+ }
+ else
+ {
+ rtx const_reg;
+ rtx insn;
+ int temp = epilogue_p ? 7 : 1;
+ int i;
+
+ /* If TEMP is invalid, we could temporarily save a general
+ register to MACL. However, there is currently no need
+ to handle this case, so just die when we see it. */
+ if (epilogue_p < 0
+ || current_function_interrupt
+ || ! call_used_regs[temp] || fixed_regs[temp])
+ temp = -1;
+ if (temp < 0 && ! current_function_interrupt && epilogue_p >= 0)
+ {
+ HARD_REG_SET temps = (regs_invalidated_by_call
+ & ~fixed_reg_set
+ & savable_regs);
+ if (epilogue_p > 0)
+ {
+ int nreg = 0;
+ if (crtl->return_rtx)
+ {
+ machine_mode mode;
+ mode = GET_MODE (crtl->return_rtx);
+ if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
+ nreg = hard_regno_nregs (FIRST_RET_REG, mode);
+ }
+ for (i = 0; i < nreg; i++)
+ CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
+ if (crtl->calls_eh_return)
+ {
+ CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO);
+ for (i = 0; i <= 3; i++)
+ CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
+ }
+ }
+ if (epilogue_p <= 0)
+ {
+ for (i = FIRST_PARM_REG;
+ i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
+ CLEAR_HARD_REG_BIT (temps, i);
+ if (cfun->static_chain_decl != NULL)
+ CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
+ }
+ temp = scavenge_reg (&temps);
+ }
+ if (temp < 0 && live_regs_mask)
+ {
+ HARD_REG_SET temps;
+
+ temps = *live_regs_mask;
+ CLEAR_HARD_REG_BIT (temps, REGNO (reg));
+ temp = scavenge_reg (&temps);
+ }
+ if (temp < 0)
+ {
+ rtx adj_reg, tmp_reg, mem;
+
+ /* If we reached here, the most likely case is the (sibcall)
+ epilogue. Put a special push/pop sequence for such case as
+ the last resort. This looks lengthy but would not be problem
+ because it seems to be very rare. */
+ gcc_assert (epilogue_p);
+
+ /* ??? There is still the slight possibility that r4 or
+ r5 have been reserved as fixed registers or assigned
+ as global registers, and they change during an
+ interrupt. There are possible ways to handle this:
+
+ - If we are adjusting the frame pointer (r14), we can do
+ with a single temp register and an ordinary push / pop
+ on the stack.
+ - Grab any call-used or call-saved registers (i.e. not
+ fixed or globals) for the temps we need. We might
+ also grab r14 if we are adjusting the stack pointer.
+ If we can't find enough available registers, issue
+ a diagnostic and die - the user must have reserved
+ way too many registers.
+ But since all this is rather unlikely to happen and
+ would require extra testing, we just die if r4 / r5
+ are not available. */
+ gcc_assert (!fixed_regs[4] && !fixed_regs[5]
+ && !global_regs[4] && !global_regs[5]);
+
+ adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
+ tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
+ emit_move_insn (gen_tmp_stack_mem (Pmode, reg), adj_reg);
+ emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
+ emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (tmp_reg, gen_tmp_stack_mem (Pmode, reg));
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (reg, adj_reg);
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (adj_reg, mem);
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (tmp_reg, mem);
+ /* Tell flow the insns that pop r4/r5 aren't dead. */
+ emit_use (tmp_reg);
+ emit_use (adj_reg);
+ return;
+ }
+ const_reg = gen_rtx_REG (GET_MODE (reg), temp);
+
+ /* If SIZE is negative, subtract the positive value.
+ This sometimes allows a constant pool entry to be shared
+ between prologue and epilogue code. */
+ if (size < 0)
+ {
+ emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
+ insn = emit_fn (GEN_SUB3 (reg, reg, const_reg));
+ }
+ else
+ {
+ emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
+ insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
+ }
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (reg, gen_rtx_PLUS (SImode, reg,
+ GEN_INT (size))));
+ }
+ }
+}
+
+/* Emit the specified insn and mark it as frame related. */
+static rtx_insn *
+emit_frame_insn (rtx x)
+{
+ rtx_insn *insn = emit_insn (x);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ return insn;
+}
+
+/* Output RTL to push register RN onto the stack. */
+static rtx
+push (int rn)
+{
+ rtx x;
+ if (rn == FPUL_REG)
+ x = gen_push_fpul ();
+ else if (rn == FPSCR_REG)
+ x = gen_push_fpscr ();
+ else if (TARGET_FPU_DOUBLE && TARGET_FMOVD
+ && ! TARGET_FPU_SINGLE && FP_OR_XD_REGISTER_P (rn))
+ {
+ if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
+ return NULL_RTX;
+ x = gen_push_4 (gen_rtx_REG (DFmode, rn));
+ }
+ else if (TARGET_SH2E && FP_REGISTER_P (rn))
+ x = gen_push_e (gen_rtx_REG (SFmode, rn));
+ else
+ x = gen_push (gen_rtx_REG (SImode, rn));
+
+ x = emit_frame_insn (x);
+ add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
+ return x;
+}
+
+/* Output RTL to pop register RN from the stack. */
+static void
+pop (int rn)
+{
+ rtx x, sp_reg, reg;
+ if (rn == FPUL_REG)
+ x = gen_pop_fpul ();
+ else if (rn == FPSCR_REG)
+ x = gen_pop_fpscr ();
+ else if (TARGET_FPU_DOUBLE && TARGET_FMOVD
+ && ! TARGET_FPU_SINGLE && FP_OR_XD_REGISTER_P (rn))
+ {
+ if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
+ return;
+ x = gen_pop_4 (gen_rtx_REG (DFmode, rn));
+ }
+ else if (TARGET_SH2E && FP_REGISTER_P (rn))
+ x = gen_pop_e (gen_rtx_REG (SFmode, rn));
+ else
+ x = gen_pop (gen_rtx_REG (SImode, rn));
+
+ x = emit_insn (x);
+
+ sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
+ reg = copy_rtx (GET_CODE (PATTERN (x)) == PARALLEL
+ ? SET_DEST (XVECEXP (PATTERN (x), 0, 0))
+ : SET_DEST (PATTERN (x)));
+ add_reg_note (x, REG_CFA_RESTORE, reg);
+ add_reg_note (x, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (sp_reg,
+ plus_constant (SImode, sp_reg,
+ GET_MODE_SIZE (GET_MODE (reg)))));
+ add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
+ RTX_FRAME_RELATED_P (x) = 1;
+}
+
+/* Generate code to push the regs specified in the mask. */
+static void
+push_regs (HARD_REG_SET *mask, bool interrupt_handler)
+{
+ bool skip_fpscr = false;
+
+ /* Push PR last; this gives better latencies after the prologue, and
+ candidates for the return delay slot when there are no general
+ registers pushed. */
+ for (int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
+ i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ /* If this is an interrupt handler, and the SZ bit varies,
+ and we have to push any floating point register, we need
+ to switch to the correct precision first. */
+ if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
+ && hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
+ {
+ push (FPSCR_REG);
+ fpscr_set_from_mem (NORMAL_MODE (FP_MODE), ~*mask);
+ skip_fpscr = true;
+ }
+ if (i != PR_REG
+ && (i != FPSCR_REG || ! skip_fpscr)
+ && TEST_HARD_REG_BIT (*mask, i))
+ {
+ /* If the ISR has RESBANK attribute assigned, don't push any of
+ the following registers - R0-R14, MACH, MACL and GBR. */
+ if (! (sh_cfun_resbank_handler_p ()
+ && ((i >= FIRST_GENERAL_REG && i < LAST_GENERAL_REG)
+ || i == MACH_REG
+ || i == MACL_REG
+ || i == GBR_REG)))
+ push (i);
+ }
+ }
+
+ /* Push banked registers last to improve delay slot opportunities. */
+ if (interrupt_handler)
+ {
+ bool use_movml = false;
+
+ if (TARGET_SH2A)
+ {
+ unsigned int count = 0;
+
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (*mask, i))
+ count++;
+ else
+ break;
+
+ /* Use movml when all banked registers are pushed. */
+ if (count == LAST_BANKED_REG - FIRST_BANKED_REG + 1)
+ use_movml = true;
+ }
+
+ if (sh_cfun_resbank_handler_p ())
+ ; /* Do nothing. */
+ else if (use_movml)
+ {
+ rtx x, mem, reg, set;
+ rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
+
+ /* We must avoid scheduling multiple store insn with another
+ insns. */
+ emit_insn (gen_blockage ());
+ x = gen_movml_push_banked (sp_reg);
+ x = emit_frame_insn (x);
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ {
+ mem = gen_rtx_MEM (SImode, plus_constant (Pmode, sp_reg, i * 4));
+ reg = gen_rtx_REG (SImode, i);
+ add_reg_note (x, REG_CFA_OFFSET, gen_rtx_SET (mem, reg));
+ }
+
+ set = gen_rtx_SET (sp_reg, plus_constant (Pmode, sp_reg, - 32));
+ add_reg_note (x, REG_CFA_ADJUST_CFA, set);
+ emit_insn (gen_blockage ());
+ }
+ else
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (*mask, i))
+ push (i);
+ }
+
+ /* Don't push PR register for an ISR with RESBANK attribute assigned. */
+ if (TEST_HARD_REG_BIT (*mask, PR_REG) && !sh_cfun_resbank_handler_p ())
+ push (PR_REG);
+}
+
+/* Work out the registers which need to be saved, both as a mask and a
+ count of saved words. Return the count.
+
+ If doing a pragma interrupt function, then push all regs used by the
+ function, and if we call another function (we can tell by looking at PR),
+ make sure that all the regs it clobbers are safe too. */
+static int
+calc_live_regs (HARD_REG_SET *live_regs_mask)
+{
+ unsigned int reg;
+ tree attrs;
+ bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
+ bool nosave_low_regs;
+
+ attrs = DECL_ATTRIBUTES (current_function_decl);
+ interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
+ trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE;
+ interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler;
+ nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE;
+
+ CLEAR_HARD_REG_SET (*live_regs_mask);
+ if (TARGET_FPU_DOUBLE && TARGET_FMOVD && interrupt_handler
+ && df_regs_ever_live_p (FPSCR_REG))
+ target_flags &= ~MASK_FPU_SINGLE;
+ /* If we can save a lot of saves by switching to double mode, do that. */
+ else if (TARGET_FPU_DOUBLE && TARGET_FMOVD && TARGET_FPU_SINGLE)
+ for (int count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
+ if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
+ && (! call_used_regs[reg]
+ || interrupt_handler)
+ && ++count > 2)
+ {
+ target_flags &= ~MASK_FPU_SINGLE;
+ break;
+ }
+
+
+ rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
+ bool pr_live = (pr_initial
+ ? (!REG_P (pr_initial)
+ || REGNO (pr_initial) != (PR_REG))
+ : df_regs_ever_live_p (PR_REG));
+ /* For Shcompact, if not optimizing, we end up with a memory reference
+ using the return address pointer for __builtin_return_address even
+ though there is no actual need to put the PR register on the stack. */
+ pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
+
+ /* Force PR to be live if the prologue has to call the SHmedia
+ argument decoder or register saver. */
+ bool has_call = pr_live;
+
+ int count;
+ for (count = 0, reg = FIRST_PSEUDO_REGISTER; reg-- != 0; )
+ {
+ if (reg == PR_REG
+ ? pr_live
+ : interrupt_handler
+ ? (/* Need to save all the regs ever live. */
+ (df_regs_ever_live_p (reg)
+ || (call_used_regs[reg]
+ && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
+ || reg == PIC_OFFSET_TABLE_REGNUM)
+ && has_call))
+ && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
+ && reg != RETURN_ADDRESS_POINTER_REGNUM
+ && reg != T_REG && reg != GBR_REG
+ && reg != FPSCR_MODES_REG && reg != FPSCR_STAT_REG
+ /* Push fpscr only on targets which have FPU */
+ && (reg != FPSCR_REG || TARGET_FPU_ANY))
+ : (/* Only push those regs which are used and need to be saved. */
+ (false)
+ || (df_regs_ever_live_p (reg)
+ && ((!call_used_regs[reg]
+ && !(reg != PIC_OFFSET_TABLE_REGNUM
+ && fixed_regs[reg]
+ && call_used_or_fixed_reg_p (reg)))
+ || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
+ || (crtl->calls_eh_return
+ && (reg == EH_RETURN_DATA_REGNO (0)
+ || reg == EH_RETURN_DATA_REGNO (1)
+ || reg == EH_RETURN_DATA_REGNO (2)
+ || reg == EH_RETURN_DATA_REGNO (3)))
+ || ((reg == MACL_REG || reg == MACH_REG)
+ && df_regs_ever_live_p (reg)
+ && sh_cfun_attr_renesas_p ())
+ ))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, reg);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+
+ if (TARGET_FPU_DOUBLE && TARGET_FMOVD
+ && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
+ {
+ if (FP_REGISTER_P (reg))
+ {
+ if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1));
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
+ }
+ }
+ else if (XD_REGISTER_P (reg))
+ {
+ /* Must switch to double mode to access these registers. */
+ target_flags &= ~MASK_FPU_SINGLE;
+ }
+ }
+ }
+ if (nosave_low_regs && reg == R8_REG)
+ break;
+ }
+
+ return count;
+}
+
+/* Code to generate prologue and epilogue sequences */
+
+/* PUSHED is the number of bytes that are being pushed on the
+ stack for register saves. Return the frame size, padded
+ appropriately so that the stack stays properly aligned. */
+static HOST_WIDE_INT
+rounded_frame_size (int pushed)
+{
+ HOST_WIDE_INT size = get_frame_size ();
+ HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+ if (ACCUMULATE_OUTGOING_ARGS)
+ size += crtl->outgoing_args_size;
+
+ return ((size + pushed + align - 1) & -align) - pushed;
+}
+
+/* Expand code for the function prologue. */
+void
+sh_expand_prologue (void)
+{
+ int save_flags = target_flags;
+ tree sp_switch_attr
+ = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
+
+ current_function_interrupt = sh_cfun_interrupt_handler_p ();
+
+ /* We have pretend args if we had an object sent partially in registers
+ and partially on the stack, e.g. a large structure. */
+ int pretend_args = crtl->args.pretend_args_size;
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
+ && (NPARM_REGS(SImode)
+ > crtl->args.info.arg_count[(int) SH_ARG_INT]))
+ pretend_args = 0;
+
+ output_stack_adjust (-pretend_args, stack_pointer_rtx, 0, NULL, true);
+ int stack_usage = pretend_args;
+
+ /* Emit the code for SETUP_VARARGS. */
+ if (cfun->stdarg)
+ {
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
+ {
+ /* Push arg regs as if they'd been provided by caller in stack. */
+ for (int i = 0; i < NPARM_REGS(SImode); i++)
+ {
+ int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
+
+ if (i >= (NPARM_REGS(SImode)
+ - crtl->args.info.arg_count[(int) SH_ARG_INT]
+ ))
+ break;
+ push (rn);
+ stack_usage += GET_MODE_SIZE (SImode);
+ }
+ }
+ }
+
+ /* If we're supposed to switch stacks at function entry, do so now. */
+ if (sp_switch_attr)
+ {
+ rtx lab, newsrc;
+ /* The argument specifies a variable holding the address of the
+ stack the interrupt function should switch to/from at entry/exit. */
+ tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
+ const char* s = ggc_strdup (TREE_STRING_POINTER (arg));
+ rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
+
+ lab = add_constant (sp_switch, SImode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+
+ emit_insn (gen_sp_switch_1 (newsrc));
+ }
+
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
+ /* ??? Maybe we could save some switching if we can move a mode switch
+ that already happens to be at the function start into the prologue. */
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+
+ push_regs (&live_regs_mask, current_function_interrupt);
+ stack_usage += d;
+
+ if (flag_pic && !TARGET_FDPIC
+ && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+ emit_insn (gen_GOTaddr2picreg (const0_rtx));
+
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+
+ target_flags = save_flags;
+
+ output_stack_adjust (-rounded_frame_size (d),
+ stack_pointer_rtx, 0, NULL, true);
+ stack_usage += rounded_frame_size (d);
+
+ if (frame_pointer_needed)
+ emit_frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. Similarly if some call instructions are swapped
+ before frame related insns, it'll confuse the unwinder because
+ currently SH has no unwind info for function epilogues. */
+ if (crtl->profile || flag_exceptions || flag_unwind_tables)
+ emit_insn (gen_blockage ());
+
+ if (flag_stack_usage_info)
+ current_function_static_stack_size = stack_usage;
+}
+
+/* Expand code for the function epilogue. */
+void
+sh_expand_epilogue (bool sibcall_p)
+{
+ int save_flags = target_flags;
+ bool fpscr_deferred = false;
+ int e = sibcall_p ? -1 : 1;
+
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
+
+ int save_size = d;
+ int frame_size = rounded_frame_size (d);
+
+ if (frame_pointer_needed)
+ {
+ /* We must avoid scheduling the epilogue with previous basic blocks.
+ See PR/18032 and PR/40313. */
+ emit_insn (gen_blockage ());
+ output_stack_adjust (frame_size, hard_frame_pointer_rtx, e,
+ &live_regs_mask, true);
+
+ /* We must avoid moving the stack pointer adjustment past code
+ which reads from the local frame, else an interrupt could
+ occur after the SP adjustment and clobber data in the local
+ frame. */
+ emit_insn (gen_blockage ());
+ emit_frame_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
+ }
+ else if (frame_size)
+ {
+ /* We must avoid moving the stack pointer adjustment past code
+ which reads from the local frame, else an interrupt could
+ occur after the SP adjustment and clobber data in the local
+ frame. */
+ emit_insn (gen_blockage ());
+ output_stack_adjust (frame_size, stack_pointer_rtx, e,
+ &live_regs_mask, true);
+ }
+
+ /* Pop all the registers. */
+
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+
+ {
+ int last_reg;
+
+ save_size = 0;
+ /* For an ISR with RESBANK attribute assigned, don't pop PR
+ register. */
+ if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)
+ && !sh_cfun_resbank_handler_p ())
+ {
+ if (!frame_pointer_needed)
+ emit_insn (gen_blockage ());
+ pop (PR_REG);
+ }
+
+ /* Banked registers are popped first to avoid being scheduled in the
+ delay slot. RTE switches banks before the ds instruction. */
+ if (current_function_interrupt)
+ {
+ bool use_movml = false;
+
+ if (TARGET_SH2A)
+ {
+ unsigned int count = 0;
+
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (live_regs_mask, i))
+ count++;
+ else
+ break;
+
+ /* Use movml when all banked register are poped. */
+ if (count == LAST_BANKED_REG - FIRST_BANKED_REG + 1)
+ use_movml = true;
+ }
+
+ if (sh_cfun_resbank_handler_p ())
+ ; /* Do nothing. */
+ else if (use_movml)
+ {
+ rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
+
+ /* We must avoid scheduling multiple load insn with another
+ insns. */
+ emit_insn (gen_blockage ());
+ emit_insn (gen_movml_pop_banked (sp_reg));
+ emit_insn (gen_blockage ());
+ }
+ else
+ for (int i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
+ if (TEST_HARD_REG_BIT (live_regs_mask, i))
+ pop (i);
+
+ last_reg = FIRST_PSEUDO_REGISTER - LAST_BANKED_REG - 1;
+ }
+ else
+ last_reg = FIRST_PSEUDO_REGISTER;
+
+ for (int i = 0; i < last_reg; i++)
+ {
+ int j = (FIRST_PSEUDO_REGISTER - 1) - i;
+
+ if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
+ && hard_reg_set_intersect_p (live_regs_mask,
+ reg_class_contents[DF_REGS]))
+ fpscr_deferred = true;
+ /* For an ISR with RESBANK attribute assigned, don't pop
+ following registers, R0-R14, MACH, MACL and GBR. */
+ else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)
+ && ! (sh_cfun_resbank_handler_p ()
+ && ((j >= FIRST_GENERAL_REG
+ && j < LAST_GENERAL_REG)
+ || j == MACH_REG
+ || j == MACL_REG
+ || j == GBR_REG)))
+ pop (j);
+
+ if (j == FIRST_FP_REG && fpscr_deferred)
+ pop (FPSCR_REG);
+ }
+ }
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+ target_flags = save_flags;
+
+ output_stack_adjust (crtl->args.pretend_args_size + save_size,
+ stack_pointer_rtx, e, NULL, true);
+
+ if (crtl->calls_eh_return)
+ emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
+
+ /* Switch back to the normal stack if necessary. */
+ if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)))
+ emit_insn (gen_sp_switch_2 ());
+
+ /* Tell flow the insn that pops PR isn't dead. */
+ if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
+ emit_use (gen_rtx_REG (SImode, PR_REG));
+}
+
+/* Emit code to change the current function's return address to RA.
+ TEMP is available as a scratch register, if needed. */
+void
+sh_set_return_address (rtx ra, rtx tmp)
+{
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
+
+ /* If pr_reg isn't life, we can set it directly. */
+ if (! TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
+ {
+ rtx rr = gen_rtx_REG (SImode, PR_REG);
+ emit_insn (GEN_MOV (rr, ra));
+ /* Tell flow the register for return isn't dead. */
+ emit_use (rr);
+ return;
+ }
+
+ int pr_offset = rounded_frame_size (d);
+
+ emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
+
+ if (frame_pointer_needed)
+ emit_insn (GEN_ADD3 (tmp, tmp, hard_frame_pointer_rtx));
+ else
+ emit_insn (GEN_ADD3 (tmp, tmp, stack_pointer_rtx));
+
+ tmp = gen_frame_mem (Pmode, tmp);
+ emit_insn (GEN_MOV (tmp, ra));
+ /* Tell this store isn't dead. */
+ emit_use (tmp);
+}
+
+/* Clear variables at function end. */
+static void
+sh_output_function_epilogue (FILE *)
+{
+}
+
+static rtx
+sh_builtin_saveregs (void)
+{
+ /* First unnamed integer register. */
+ int first_intreg = crtl->args.info.arg_count[(int) SH_ARG_INT];
+ /* Number of integer registers we need to save. */
+ int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
+ /* First unnamed SFmode float reg */
+ int first_floatreg = crtl->args.info.arg_count[(int) SH_ARG_FLOAT];
+ /* Number of SFmode float regs to save. */
+ int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
+ rtx regbuf, fpregs;
+ int bufsize, regno;
+ alias_set_type alias_set;
+
+ if (!TARGET_FPU_ANY)
+ {
+ error ("%<__builtin_saveregs%> not supported by this subtarget");
+ return const0_rtx;
+ }
+
+ /* Allocate block of memory for the regs. */
+ /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
+ Or can assign_stack_local accept a 0 SIZE argument? */
+ bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
+
+ if (n_floatregs & 1)
+ {
+ rtx addr;
+
+ regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+ addr = copy_to_mode_reg (Pmode, XEXP (regbuf, 0));
+ emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
+ regbuf = change_address (regbuf, BLKmode, addr);
+ }
+ else if (STACK_BOUNDARY < 64 && TARGET_FPU_DOUBLE && n_floatregs)
+ {
+ rtx addr, mask;
+
+ regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+ addr = copy_to_mode_reg (Pmode, plus_constant (Pmode,
+ XEXP (regbuf, 0), 4));
+ mask = copy_to_mode_reg (Pmode, GEN_INT (-8));
+ emit_insn (gen_andsi3 (addr, addr, mask));
+ regbuf = change_address (regbuf, BLKmode, addr);
+ }
+ else
+ regbuf = assign_stack_local (BLKmode, bufsize, TARGET_FPU_DOUBLE ? 64 : 0);
+ alias_set = get_varargs_alias_set ();
+ set_mem_alias_set (regbuf, alias_set);
+
+ /* Save int args.
+ This is optimized to only save the regs that are necessary. Explicitly
+ named args need not be saved. */
+ if (n_intregs > 0)
+ move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
+ adjust_address (regbuf, BLKmode,
+ n_floatregs * UNITS_PER_WORD),
+ n_intregs);
+
+ /* Save float args.
+ This is optimized to only save the regs that are necessary. Explicitly
+ named args need not be saved.
+ We explicitly build a pointer to the buffer because it halves the insn
+ count when not optimizing (otherwise the pointer is built for each reg
+ saved).
+ We emit the moves in reverse order so that we can use predecrement. */
+
+ fpregs = copy_to_mode_reg (Pmode,
+ plus_constant (Pmode, XEXP (regbuf, 0),
+ n_floatregs * UNITS_PER_WORD));
+ if (TARGET_FPU_DOUBLE)
+ {
+ rtx mem;
+ for (regno = NPARM_REGS (DFmode) - 2; regno >= first_floatreg; regno -= 2)
+ {
+ emit_insn (gen_addsi3 (fpregs, fpregs,
+ GEN_INT (-2 * UNITS_PER_WORD)));
+ mem = change_address (regbuf, DFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (DFmode, BASE_ARG_REG (DFmode) + regno));
+ }
+ regno = first_floatreg;
+ if (regno & 1)
+ {
+ emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
+ mem = change_address (regbuf, SFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode)
+ + regno - SH_REG_MSW_OFFSET));
+ }
+ }
+ else
+ for (regno = NPARM_REGS (SFmode) - 1; regno >= first_floatreg; regno--)
+ {
+ rtx mem;
+
+ emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
+ mem = change_address (regbuf, SFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
+ }
+
+ /* Return the address of the regbuf. */
+ return XEXP (regbuf, 0);
+}
+
+/* Define the `__builtin_va_list' type for the ABI. */
+static tree
+sh_build_builtin_va_list (void)
+{
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree record, type_decl;
+
+ if ((! TARGET_SH2E && ! TARGET_SH4)
+ || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
+ return ptr_type_node;
+
+ record = (*lang_hooks.types.make_type) (RECORD_TYPE);
+ type_decl = build_decl (BUILTINS_LOCATION,
+ TYPE_DECL, get_identifier ("__va_list_tag"), record);
+
+ f_next_o = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_o"),
+ ptr_type_node);
+ f_next_o_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
+ get_identifier ("__va_next_o_limit"),
+ ptr_type_node);
+ f_next_fp = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_fp"),
+ ptr_type_node);
+ f_next_fp_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
+ get_identifier ("__va_next_fp_limit"),
+ ptr_type_node);
+ f_next_stack = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_stack"),
+ ptr_type_node);
+
+ DECL_FIELD_CONTEXT (f_next_o) = record;
+ DECL_FIELD_CONTEXT (f_next_o_limit) = record;
+ DECL_FIELD_CONTEXT (f_next_fp) = record;
+ DECL_FIELD_CONTEXT (f_next_fp_limit) = record;
+ DECL_FIELD_CONTEXT (f_next_stack) = record;
+
+ TYPE_STUB_DECL (record) = type_decl;
+ TYPE_NAME (record) = type_decl;
+ TYPE_FIELDS (record) = f_next_o;
+ DECL_CHAIN (f_next_o) = f_next_o_limit;
+ DECL_CHAIN (f_next_o_limit) = f_next_fp;
+ DECL_CHAIN (f_next_fp) = f_next_fp_limit;
+ DECL_CHAIN (f_next_fp_limit) = f_next_stack;
+
+ layout_type (record);
+
+ return record;
+}
+
+/* Implement `va_start' for varargs and stdarg. */
+static void
+sh_va_start (tree valist, rtx nextarg)
+{
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
+ tree t, u;
+ int nfp, nint;
+
+ if ((! TARGET_SH2E && ! TARGET_SH4)
+ || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
+ {
+ std_expand_builtin_va_start (valist, nextarg);
+ return;
+ }
+
+ f_next_o = TYPE_FIELDS (va_list_type_node);
+ f_next_o_limit = DECL_CHAIN (f_next_o);
+ f_next_fp = DECL_CHAIN (f_next_o_limit);
+ f_next_fp_limit = DECL_CHAIN (f_next_fp);
+ f_next_stack = DECL_CHAIN (f_next_fp_limit);
+
+ next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
+ next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+ valist, f_next_o_limit, NULL_TREE);
+ next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
+ NULL_TREE);
+ next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+ valist, f_next_fp_limit, NULL_TREE);
+ next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+ valist, f_next_stack, NULL_TREE);
+
+ /* Call __builtin_saveregs. */
+ u = make_tree (sizetype, expand_builtin_saveregs ());
+ u = fold_convert (ptr_type_node, u);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_fp, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ nfp = crtl->args.info.arg_count[SH_ARG_FLOAT];
+ if (nfp < 8)
+ nfp = 8 - nfp;
+ else
+ nfp = 0;
+ u = fold_build_pointer_plus_hwi (u, UNITS_PER_WORD * nfp);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_fp_limit, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_o, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ nint = crtl->args.info.arg_count[SH_ARG_INT];
+ if (nint < 4)
+ nint = 4 - nint;
+ else
+ nint = 0;
+ u = fold_build_pointer_plus_hwi (u, UNITS_PER_WORD * nint);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_o_limit, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ u = make_tree (ptr_type_node, nextarg);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_stack, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+/* TYPE is a RECORD_TYPE. If there is only a single nonzero-sized
+ member, return it. */
+static tree
+find_sole_member (tree type)
+{
+ tree field, member = NULL_TREE;
+
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ if (!DECL_SIZE (field))
+ return NULL_TREE;
+ if (integer_zerop (DECL_SIZE (field)))
+ continue;
+ if (member)
+ return NULL_TREE;
+ member = field;
+ }
+ return member;
+}
+
+/* Implement `va_arg'. */
+static tree
+sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+ tree tmp;
+ tree addr, lab_over = NULL, result = NULL;
+ tree eff_type;
+
+ const bool pass_by_ref
+ = !VOID_TYPE_P (type) && must_pass_va_arg_in_stack (type);
+
+ if (pass_by_ref)
+ type = build_pointer_type (type);
+
+ HOST_WIDE_INT size = int_size_in_bytes (type);
+ HOST_WIDE_INT rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+ tree pptr_type_node = build_pointer_type (ptr_type_node);
+
+ if ((TARGET_SH2E || TARGET_SH4)
+ && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ()))
+ {
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
+ tree lab_false;
+ tree member;
+
+ f_next_o = TYPE_FIELDS (va_list_type_node);
+ f_next_o_limit = DECL_CHAIN (f_next_o);
+ f_next_fp = DECL_CHAIN (f_next_o_limit);
+ f_next_fp_limit = DECL_CHAIN (f_next_fp);
+ f_next_stack = DECL_CHAIN (f_next_fp_limit);
+
+ next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
+ next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+ valist, f_next_o_limit, NULL_TREE);
+ next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp),
+ valist, f_next_fp, NULL_TREE);
+ next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+ valist, f_next_fp_limit, NULL_TREE);
+ next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+ valist, f_next_stack, NULL_TREE);
+
+ /* Structures with a single member with a distinct mode are passed
+ like their member. This is relevant if the latter has a REAL_TYPE
+ or COMPLEX_TYPE type. */
+ eff_type = type;
+ while (TREE_CODE (eff_type) == RECORD_TYPE
+ && (member = find_sole_member (eff_type))
+ && (TREE_CODE (TREE_TYPE (member)) == REAL_TYPE
+ || TREE_CODE (TREE_TYPE (member)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE))
+ {
+ tree field_type = TREE_TYPE (member);
+
+ if (TYPE_MODE (eff_type) == TYPE_MODE (field_type))
+ eff_type = field_type;
+ else
+ {
+ gcc_assert ((TYPE_ALIGN (eff_type)
+ < GET_MODE_ALIGNMENT (TYPE_MODE (field_type)))
+ || (TYPE_ALIGN (eff_type)
+ > GET_MODE_BITSIZE (TYPE_MODE (field_type))));
+ break;
+ }
+ }
+
+ bool pass_as_float;
+ if (TARGET_FPU_DOUBLE)
+ {
+ pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
+ || (TREE_CODE (eff_type) == COMPLEX_TYPE
+ && TREE_CODE (TREE_TYPE (eff_type)) == REAL_TYPE
+ && size <= 16));
+ }
+ else
+ {
+ pass_as_float = (TREE_CODE (eff_type) == REAL_TYPE && size == 4);
+ }
+
+ addr = create_tmp_var (pptr_type_node);
+ lab_false = create_artificial_label (UNKNOWN_LOCATION);
+ lab_over = create_artificial_label (UNKNOWN_LOCATION);
+
+ valist = build_simple_mem_ref (addr);
+
+ if (pass_as_float)
+ {
+ tree next_fp_tmp = create_tmp_var (TREE_TYPE (f_next_fp));
+ tree cmp;
+ bool is_double = size == 8 && TREE_CODE (eff_type) == REAL_TYPE;
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_fp));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+
+ gimplify_assign (unshare_expr (next_fp_tmp), valist, pre_p);
+ tmp = next_fp_limit;
+ if (size > 4 && !is_double)
+ tmp = fold_build_pointer_plus_hwi (unshare_expr (tmp), 4 - size);
+ tmp = build2 (GE_EXPR, boolean_type_node,
+ unshare_expr (next_fp_tmp), unshare_expr (tmp));
+ cmp = build3 (COND_EXPR, void_type_node, tmp,
+ build1 (GOTO_EXPR, void_type_node,
+ unshare_expr (lab_false)), NULL_TREE);
+ if (!is_double)
+ gimplify_and_add (cmp, pre_p);
+
+ if (TYPE_ALIGN (eff_type) > BITS_PER_WORD
+ || (is_double || size == 16))
+ {
+ tmp = fold_convert (sizetype, next_fp_tmp);
+ tmp = build2 (BIT_AND_EXPR, sizetype, tmp,
+ size_int (UNITS_PER_WORD));
+ tmp = fold_build_pointer_plus (unshare_expr (next_fp_tmp), tmp);
+ gimplify_assign (unshare_expr (next_fp_tmp), tmp, pre_p);
+ }
+ if (is_double)
+ gimplify_and_add (cmp, pre_p);
+
+#ifdef FUNCTION_ARG_SCmode_WART
+ if (TYPE_MODE (eff_type) == SCmode
+ && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
+ {
+ tree subtype = TREE_TYPE (eff_type);
+ tree real, imag;
+
+ imag
+ = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
+ imag = get_initialized_tmp_var (imag, pre_p, NULL);
+
+ real
+ = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
+ real = get_initialized_tmp_var (real, pre_p, NULL);
+
+ result = build2 (COMPLEX_EXPR, eff_type, real, imag);
+ if (type != eff_type)
+ result = build1 (VIEW_CONVERT_EXPR, type, result);
+ result = get_initialized_tmp_var (result, pre_p, NULL);
+ }
+#endif /* FUNCTION_ARG_SCmode_WART */
+
+ tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+ gimplify_assign (unshare_expr (next_fp_tmp),
+ unshare_expr (valist), pre_p);
+
+ gimplify_assign (unshare_expr (valist),
+ unshare_expr (next_fp_tmp), post_p);
+ valist = next_fp_tmp;
+ }
+ else
+ {
+ tmp = fold_build_pointer_plus_hwi (unshare_expr (next_o), rsize);
+ tmp = build2 (GT_EXPR, boolean_type_node, tmp,
+ unshare_expr (next_o_limit));
+ tmp = build3 (COND_EXPR, void_type_node, tmp,
+ build1 (GOTO_EXPR, void_type_node,
+ unshare_expr (lab_false)),
+ NULL_TREE);
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_o));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+
+ tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
+ gimplify_and_add (tmp, pre_p);
+
+ if (size > 4 && ! (TARGET_SH4 || TARGET_SH2A))
+ gimplify_assign (unshare_expr (next_o),
+ unshare_expr (next_o_limit), pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+ }
+
+ if (!result)
+ {
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+ }
+ }
+
+ /* ??? In va-sh.h, there had been code to make values larger than
+ size 8 indirect. This does not match the FUNCTION_ARG macros. */
+
+ tmp = std_gimplify_va_arg_expr (valist, type, pre_p, NULL);
+ if (result)
+ {
+ gimplify_assign (result, tmp, pre_p);
+ result = build1 (NOP_EXPR, TREE_TYPE (result), result);
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+ }
+ else
+ result = tmp;
+
+ if (pass_by_ref)
+ result = build_va_arg_indirect_ref (result);
+
+ return result;
+}
+
+/* 64 bit floating points memory transfers are paired single precision loads
+ or store. So DWARF information needs fixing in little endian (unless
+ PR=SZ=1 in FPSCR). */
+rtx
+sh_dwarf_register_span (rtx reg)
+{
+ unsigned regno = REGNO (reg);
+
+ if (WORDS_BIG_ENDIAN || GET_MODE (reg) != DFmode)
+ return NULL_RTX;
+
+ return
+ gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2,
+ gen_rtx_REG (SFmode, regno + 1),
+ gen_rtx_REG (SFmode, regno)));
+}
+
+static machine_mode
+sh_promote_function_mode (const_tree type, machine_mode mode,
+ int *punsignedp, const_tree funtype,
+ int for_return)
+{
+ if (sh_promote_prototypes (funtype))
+ return promote_mode (type, mode, punsignedp);
+ else
+ return default_promote_function_mode (type, mode, punsignedp, funtype,
+ for_return);
+}
+
+static bool
+sh_promote_prototypes (const_tree type)
+{
+ if (TARGET_HITACHI)
+ return false;
+ if (! type)
+ return true;
+ return ! sh_attr_renesas_p (type);
+}
+
+static bool
+sh_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+ if (targetm.calls.must_pass_in_stack (arg))
+ return true;
+
+ /* ??? std_gimplify_va_arg_expr passes NULL for cum. That function
+ wants to know about pass-by-reference semantics for incoming
+ arguments. */
+ if (! cum)
+ return false;
+
+ return false;
+}
+
+static bool
+sh_callee_copies (cumulative_args_t cum, const function_arg_info &arg)
+{
+ /* ??? How can it possibly be correct to return true only on the
+ caller side of the equation? Is there someplace else in the
+ sh backend that's magically producing the copies? */
+ return (get_cumulative_args (cum)->outgoing
+ && ((arg.mode == BLKmode
+ ? TYPE_ALIGN (arg.type)
+ : GET_MODE_ALIGNMENT (arg.mode))
+ % SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
+}
+
+static sh_arg_class
+get_sh_arg_class (machine_mode mode)
+{
+ if (TARGET_FPU_ANY && mode == SFmode)
+ return SH_ARG_FLOAT;
+
+ if (TARGET_FPU_DOUBLE
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT))
+ return SH_ARG_FLOAT;
+
+ return SH_ARG_INT;
+}
+
+/* Round a register number up to a proper boundary for an arg of mode
+ MODE.
+ The SH doesn't care about double alignment, so we only
+ round doubles to even regs when asked to explicitly. */
+static int
+sh_round_reg (const CUMULATIVE_ARGS& cum, machine_mode mode)
+{
+ /* FIXME: This used to be a macro and has been copy pasted into this
+ function as is. Make this more readable. */
+ return
+ (((TARGET_ALIGN_DOUBLE
+ || (TARGET_FPU_DOUBLE
+ && (mode == DFmode || mode == DCmode)
+ && cum.arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (mode)))
+ && GET_MODE_UNIT_SIZE (mode) > UNITS_PER_WORD)
+ ? (cum.arg_count[(int) get_sh_arg_class (mode)]
+ + (cum.arg_count[(int) get_sh_arg_class (mode)] & 1))
+ : cum.arg_count[(int) get_sh_arg_class (mode)]);
+}
+
+/* Return true if arg of the specified mode should be passed in a register
+ or false otherwise. */
+static bool
+sh_pass_in_reg_p (const CUMULATIVE_ARGS& cum, machine_mode mode,
+ const_tree type)
+{
+ /* FIXME: This used to be a macro and has been copy pasted into this
+ function as is. Make this more readable. */
+ return
+ ((type == 0
+ || (! TREE_ADDRESSABLE (type)
+ && (! (TARGET_HITACHI || cum.renesas_abi)
+ || ! (AGGREGATE_TYPE_P (type)
+ || (!TARGET_FPU_ANY
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (SFmode)))))))
+ && ! cum.force_mem
+ && (TARGET_SH2E
+ ? ((mode) == BLKmode
+ ? ((cum.arg_count[(int) SH_ARG_INT] * UNITS_PER_WORD
+ + int_size_in_bytes (type))
+ <= NPARM_REGS (SImode) * UNITS_PER_WORD)
+ : ((sh_round_reg (cum, mode)
+ + sh_hard_regno_nregs (BASE_ARG_REG (mode), mode))
+ <= NPARM_REGS (mode)))
+ : sh_round_reg (cum, mode) < NPARM_REGS (mode)));
+}
+
+static int
+sh_arg_partial_bytes (cumulative_args_t cum_v, const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+ int words = 0;
+
+ if (sh_pass_in_reg_p (*cum, arg.mode, arg.type)
+ && !TARGET_FPU_DOUBLE
+ && (sh_round_reg (*cum, arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (), UNITS_PER_WORD)
+ > NPARM_REGS (arg.mode)))
+ words = NPARM_REGS (arg.mode) - sh_round_reg (*cum, arg.mode);
+
+ return words * UNITS_PER_WORD;
+}
+
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ ARG is a description of the argument.
+
+ On SH the first args are normally in registers
+ and the rest are pushed. Any arg that starts within the first
+ NPARM_REGS words is at least partially passed in a register unless
+ its data type forbids. */
+static rtx
+sh_function_arg (cumulative_args_t ca_v, const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *ca = get_cumulative_args (ca_v);
+ machine_mode mode = arg.mode;
+
+ if (arg.end_marker_p ())
+ return ca->renesas_abi ? const1_rtx : const0_rtx;
+
+ if (sh_pass_in_reg_p (*ca, mode, arg.type)
+ && (arg.named || ! (TARGET_HITACHI || ca->renesas_abi)))
+ {
+ int regno;
+
+ if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN
+ && (! FUNCTION_ARG_SCmode_WART || (sh_round_reg (*ca, mode) & 1)))
+ {
+ rtx r1 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + (sh_round_reg (*ca, mode) ^ 1)),
+ const0_rtx);
+ rtx r2 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + ((sh_round_reg (*ca, mode) + 1) ^ 1)),
+ GEN_INT (4));
+ return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2));
+ }
+
+ /* If the alignment of a DF value causes an SF register to be
+ skipped, we will use that skipped register for the next SF
+ value. */
+ if ((TARGET_HITACHI || ca->renesas_abi)
+ && ca->free_single_fp_reg
+ && mode == SFmode)
+ return gen_rtx_REG (mode, ca->free_single_fp_reg);
+
+ regno = (BASE_ARG_REG (mode) + sh_round_reg (*ca, mode))
+ ^ (mode == SFmode && TARGET_SH4
+ && TARGET_LITTLE_ENDIAN
+ && ! TARGET_HITACHI && ! ca->renesas_abi);
+ return gen_rtx_REG (mode, regno);
+
+ }
+
+ return NULL_RTX;
+}
+
+/* Update the data in CUM to advance over argument ARG. */
+static void
+sh_function_arg_advance (cumulative_args_t ca_v,
+ const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *ca = get_cumulative_args (ca_v);
+
+ if (ca->force_mem)
+ ca->force_mem = false;
+
+ if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE)
+ {
+ /* Note that we've used the skipped register. */
+ if (arg.mode == SFmode && ca->free_single_fp_reg)
+ {
+ ca->free_single_fp_reg = 0;
+ return;
+ }
+ /* When we have a DF after an SF, there's an SF register that get
+ skipped in order to align the DF value. We note this skipped
+ register, because the next SF value will use it, and not the
+ SF that follows the DF. */
+ if (arg.mode == DFmode
+ && sh_round_reg (*ca, DFmode) != sh_round_reg (*ca, SFmode))
+ {
+ ca->free_single_fp_reg = (sh_round_reg (*ca, SFmode)
+ + BASE_ARG_REG (arg.mode));
+ }
+ }
+
+ if (! ((TARGET_SH4 || TARGET_SH2A) || ca->renesas_abi)
+ || sh_pass_in_reg_p (*ca, arg.mode, arg.type))
+ (ca->arg_count[(int) get_sh_arg_class (arg.mode)]
+ = (sh_round_reg (*ca, arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (), UNITS_PER_WORD)));
+}
+
+/* The Renesas calling convention doesn't quite fit into this scheme since
+ the address is passed like an invisible argument, but one that is always
+ passed in memory. */
+static rtx
+sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED)
+{
+ if (TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ return NULL_RTX;
+ return gen_rtx_REG (Pmode, 2);
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE.
+
+ For the SH, this is like LIBCALL_VALUE, except that we must change the
+ mode like PROMOTE_MODE does.
+ ??? PROMOTE_MODE is ignored for non-scalar types. The set of types
+ tested here has to be kept in sync with the one in
+ explow.c:promote_mode. */
+static rtx
+sh_function_value (const_tree valtype,
+ const_tree fn_decl_or_type,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ if (fn_decl_or_type
+ && !DECL_P (fn_decl_or_type))
+ fn_decl_or_type = NULL;
+
+ return gen_rtx_REG (
+ ((GET_MODE_CLASS (TYPE_MODE (valtype)) == MODE_INT
+ && GET_MODE_SIZE (TYPE_MODE (valtype)) < 4
+ && (TREE_CODE (valtype) == INTEGER_TYPE
+ || TREE_CODE (valtype) == ENUMERAL_TYPE
+ || TREE_CODE (valtype) == BOOLEAN_TYPE
+ || TREE_CODE (valtype) == REAL_TYPE
+ || TREE_CODE (valtype) == OFFSET_TYPE))
+ && sh_promote_prototypes (fn_decl_or_type)
+ ? SImode : TYPE_MODE (valtype)),
+ BASE_RETURN_VALUE_REG (TYPE_MODE (valtype)));
+}
+
+/* Worker function for TARGET_LIBCALL_VALUE. */
+static rtx
+sh_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG (mode));
+}
+
+/* Return true if N is a possible register number of function value. */
+static bool
+sh_function_value_regno_p (const unsigned int regno)
+{
+ return regno == FIRST_RET_REG || (TARGET_SH2E && regno == FIRST_FP_RET_REG);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+static bool
+sh_return_in_memory (const_tree type, const_tree fndecl)
+{
+ return TYPE_MODE (type) == BLKmode
+ || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ && TREE_CODE (type) == RECORD_TYPE);
+}
+
+/* We actually emit the code in sh_expand_prologue. We used to use
+ a static variable to flag that we need to emit this code, but that
+ doesn't when inlining, when functions are deferred and then emitted
+ later. Fortunately, we already have two flags that are part of struct
+ function that tell if a function uses varargs or stdarg. */
+static void
+sh_setup_incoming_varargs (cumulative_args_t ca,
+ const function_arg_info &arg,
+ int *pretend_arg_size,
+ int second_time ATTRIBUTE_UNUSED)
+{
+ gcc_assert (cfun->stdarg);
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
+ {
+ int named_parm_regs, anon_parm_regs;
+
+ named_parm_regs = (sh_round_reg (*get_cumulative_args (ca), arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (),
+ UNITS_PER_WORD));
+ anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
+ if (anon_parm_regs > 0)
+ *pretend_arg_size = anon_parm_regs * 4;
+ }
+}
+
+static bool
+sh_strict_argument_naming (cumulative_args_t ca ATTRIBUTE_UNUSED)
+{
+ return false;
+}
+
+static bool
+sh_pretend_outgoing_varargs_named (cumulative_args_t ca_v)
+{
+ CUMULATIVE_ARGS *ca = get_cumulative_args (ca_v);
+
+ return ! (TARGET_HITACHI || ca->renesas_abi);
+}
+
+
+/* Define the offset between two registers, one to be eliminated, and
+ the other its replacement, at the start of a routine. */
+int
+initial_elimination_offset (int from, int to)
+{
+ const int regs_saved_rounding = 0;
+ int save_flags = target_flags;
+ HARD_REG_SET live_regs_mask;
+
+ int regs_saved = calc_live_regs (&live_regs_mask);
+
+ int total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
+ target_flags = save_flags;
+
+ int total_saved_regs_space = regs_saved + regs_saved_rounding;
+
+ if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ return total_saved_regs_space + total_auto_space;
+
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return total_saved_regs_space + total_auto_space;
+
+ /* Initial gap between fp and sp is 0. */
+ if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return 0;
+
+ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return rounded_frame_size (0);
+
+ if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ return rounded_frame_size (0);
+
+ gcc_assert (from == RETURN_ADDRESS_POINTER_REGNUM
+ && (to == HARD_FRAME_POINTER_REGNUM
+ || to == STACK_POINTER_REGNUM));
+ return total_auto_space;
+}
+
+/* Parse the -mfixed-range= option string. */
+void
+sh_fix_range (const char *const_str)
+{
+ /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
+ REG2 are either register names or register numbers. The effect
+ of this option is to mark the registers in the range from REG1 to
+ REG2 as ``fixed'' so they won't be used by the compiler. */
+
+ char* str = strcpy ((char*)alloca (strlen (const_str) + 1), const_str);
+
+ while (1)
+ {
+ char* dash = strchr (str, '-');
+ if (!dash)
+ {
+ warning (0, "value of %<-mfixed-range%> must have form REG1-REG2");
+ return;
+ }
+ *dash = '\0';
+ char* comma = strchr (dash + 1, ',');
+ if (comma)
+ *comma = '\0';
+
+ int first = decode_reg_name (str);
+ if (first < 0)
+ {
+ warning (0, "unknown register name: %s", str);
+ return;
+ }
+
+ int last = decode_reg_name (dash + 1);
+ if (last < 0)
+ {
+ warning (0, "unknown register name: %s", dash + 1);
+ return;
+ }
+
+ *dash = '-';
+
+ if (first > last)
+ {
+ warning (0, "%s-%s is an empty range", str, dash + 1);
+ return;
+ }
+
+ for (int i = first; i <= last; ++i)
+ fixed_regs[i] = 1;
+
+ if (!comma)
+ break;
+
+ *comma = ',';
+ str = comma + 1;
+ }
+}
+
+/* Insert any deferred function attributes from earlier pragmas. */
+static void
+sh_insert_attributes (tree node, tree *attributes)
+{
+ if (TREE_CODE (node) != FUNCTION_DECL)
+ return;
+
+ /* We are only interested in fields. */
+ if (!DECL_P (node))
+ return;
+
+ /* Append the attributes to the deferred attributes. */
+ *sh_deferred_function_attributes_tail = *attributes;
+ tree attrs = sh_deferred_function_attributes;
+ if (!attrs)
+ return;
+
+ /* Some attributes imply or require the interrupt attribute. */
+ if (!lookup_attribute ("interrupt_handler", attrs)
+ && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node)))
+ {
+ /* If we have a trapa_handler, but no interrupt_handler attribute,
+ insert an interrupt_handler attribute. */
+ if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE)
+ /* We can't use sh_pr_interrupt here because that's not in the
+ java frontend. */
+ attrs
+ = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs);
+ /* However, for sp_switch, trap_exit, nosave_low_regs and resbank,
+ if the interrupt attribute is missing, we ignore the attribute
+ and warn. */
+ else if (lookup_attribute ("sp_switch", attrs)
+ || lookup_attribute ("trap_exit", attrs)
+ || lookup_attribute ("nosave_low_regs", attrs)
+ || lookup_attribute ("resbank", attrs))
+ {
+ tree *tail;
+
+ for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs))
+ {
+ if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs))
+ || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs))
+ || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs))
+ || is_attribute_p ("resbank", TREE_PURPOSE (attrs)))
+ warning (OPT_Wattributes,
+ "%qE attribute only applies to interrupt functions",
+ TREE_PURPOSE (attrs));
+ else
+ {
+ *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE,
+ NULL_TREE);
+ tail = &TREE_CHAIN (*tail);
+ }
+ }
+ attrs = *attributes;
+ }
+ }
+
+ /* Install the processed list. */
+ *attributes = attrs;
+
+ /* Clear deferred attributes. */
+ sh_deferred_function_attributes = NULL_TREE;
+ sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
+
+ return;
+}
+
+/*------------------------------------------------------------------------------
+ Target specific attributes
+ Supported attributes are:
+
+ * interrupt_handler
+ Specifies this function is an interrupt handler.
+
+ * trapa_handler
+ Like interrupt_handler, but don't save all registers.
+
+ * sp_switch
+ Specifies an alternate stack for an interrupt handler to run on.
+
+ * trap_exit
+ Use a trapa to exit an interrupt function instead of rte.
+
+ * nosave_low_regs
+ Don't save r0..r7 in an interrupt handler function.
+ This is useful on SH3* and SH4*, which have a separate set of low
+ regs for user and privileged modes.
+ This is mainly to be used for non-reentrant interrupt handlers (i.e.
+ those that run with interrupts disabled and thus can't be
+ interrupted thenselves).
+
+ * renesas
+ Use Renesas calling/layout conventions (functions and structures).
+
+ * resbank
+ In case of an interrupt handler function, use a register bank to
+ save registers R0-R14, MACH, MACL, GBR and PR.
+ This is available only on SH2A targets.
+
+ * function_vector
+ Declares a function to be called using the TBR relative addressing
+ mode. Takes an argument that specifies the slot number in the table
+ where this function can be looked up by the JSR/N @@(disp8,TBR) insn.
+*/
+
+/* Handle a 'resbank' attribute. */
+static tree
+sh_handle_resbank_handler_attribute (tree * node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool * no_add_attrs)
+{
+ if (!TARGET_SH2A)
+ {
+ warning (OPT_Wattributes, "%qE attribute is supported only for SH2A",
+ name);
+ *no_add_attrs = true;
+ }
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an "interrupt_handler" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_interrupt_handler_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an 'function_vector' attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh2a_handle_function_vector_handler_attribute (tree * node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool * no_add_attrs)
+{
+ if (!TARGET_SH2A)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to SH2A",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+ {
+ /* The argument must be a constant integer. */
+ warning (OPT_Wattributes,
+ "%qE attribute argument not an integer constant",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_INT_CST_LOW (TREE_VALUE (args)) > 255)
+ {
+ /* The argument value must be between 0 to 255. */
+ warning (OPT_Wattributes,
+ "%qE attribute argument should be between 0 to 255",
+ name);
+ *no_add_attrs = true;
+ }
+ return NULL_TREE;
+}
+
+/* Returns true if current function has been assigned the attribute
+ 'function_vector'. */
+bool
+sh2a_is_function_vector_call (rtx x)
+{
+ if (GET_CODE (x) == SYMBOL_REF
+ && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
+ {
+ tree tr = SYMBOL_REF_DECL (x);
+
+ if (sh2a_function_vector_p (tr))
+ return true;
+ }
+
+ return false;
+}
+
+/* Returns the function vector number, if the attribute
+ 'function_vector' is assigned, otherwise returns zero. */
+int
+sh2a_get_function_vector_number (rtx x)
+{
+ if ((GET_CODE (x) == SYMBOL_REF)
+ && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
+ {
+ tree t = SYMBOL_REF_DECL (x);
+
+ if (TREE_CODE (t) != FUNCTION_DECL)
+ return 0;
+
+ for (tree list = SH_ATTRIBUTES (t); list; list = TREE_CHAIN (list))
+ if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
+ return TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
+
+ return 0;
+ }
+ else
+ return 0;
+}
+
+/* Handle an "sp_switch" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_sp_switch_attribute (tree *node, tree name, tree args,
+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ /* The argument must be a constant string. */
+ warning (OPT_Wattributes, "%qE attribute argument not a string constant",
+ name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an "trap_exit" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_trap_exit_attribute (tree *node, tree name, tree args,
+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ /* The argument specifies a trap number to be used in a trapa instruction
+ at function exit (instead of an rte instruction). */
+ else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+ {
+ /* The argument must be a constant integer. */
+ warning (OPT_Wattributes, "%qE attribute argument not an "
+ "integer constant", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+static tree
+sh_handle_renesas_attribute (tree *node ATTRIBUTE_UNUSED,
+ tree name ATTRIBUTE_UNUSED,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs ATTRIBUTE_UNUSED)
+{
+ return NULL_TREE;
+}
+
+/* True if __attribute__((renesas)) or -mrenesas. */
+bool
+sh_attr_renesas_p (const_tree td)
+{
+ if (TARGET_HITACHI)
+ return true;
+ if (td == NULL_TREE)
+ return false;
+ if (DECL_P (td))
+ td = TREE_TYPE (td);
+ if (td == error_mark_node)
+ return false;
+ return lookup_attribute ("renesas", TYPE_ATTRIBUTES (td)) != NULL_TREE;
+}
+
+/* True if __attribute__((renesas)) or -mrenesas, for the current
+ function. */
+bool
+sh_cfun_attr_renesas_p (void)
+{
+ return sh_attr_renesas_p (current_function_decl);
+}
+
+/* Returns true if the current function has the "interrupt_handler"
+ attribute set. */
+bool
+sh_cfun_interrupt_handler_p (void)
+{
+ return (lookup_attribute ("interrupt_handler",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE);
+}
+
+/* Returns true if FUNC has been assigned the attribute
+ "function_vector". */
+bool
+sh2a_function_vector_p (tree func)
+{
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return false;
+
+ for (tree list = SH_ATTRIBUTES (func); list; list = TREE_CHAIN (list))
+ if (is_attribute_p ("function_vector", get_attribute_name (list)))
+ return true;
+
+ return false;
+}
+
+/* Returns true if given tree has the "resbank" attribute set. */
+bool
+sh_cfun_resbank_handler_p (void)
+{
+ return ((lookup_attribute ("resbank",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE)
+ && (lookup_attribute ("interrupt_handler",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE) && TARGET_SH2A);
+}
+
+/* Returns true if the current function has a "trap_exit" attribute set. */
+bool
+sh_cfun_trap_exit_p (void)
+{
+ return lookup_attribute ("trap_exit", DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE;
+}
+
+/* Implement TARGET_CHECK_PCH_TARGET_FLAGS. */
+static const char *
+sh_check_pch_target_flags (int old_flags)
+{
+ if ((old_flags ^ target_flags) & (MASK_SH1 | MASK_SH2 | MASK_SH3
+ | MASK_SH_E | MASK_HARD_SH4
+ | MASK_FPU_SINGLE | MASK_SH4))
+ return _("created and used with different architectures / ABIs");
+ if ((old_flags ^ target_flags) & MASK_HITACHI)
+ return _("created and used with different ABIs");
+ if ((old_flags ^ target_flags) & MASK_LITTLE_ENDIAN)
+ return _("created and used with different endianness");
+ return NULL;
+}
+
+/* Predicates used by the templates. */
+
+/* Returns true if OP is MACL, MACH or PR. The input must be a REG rtx.
+ Used only in general_movsrc_operand. */
+bool
+system_reg_operand (rtx op, machine_mode mode ATTRIBUTE_UNUSED)
+{
+ switch (REGNO (op))
+ {
+ case PR_REG:
+ case MACL_REG:
+ case MACH_REG:
+ return true;
+ }
+ return false;
+}
+
+/* Returns true if OP is a floating point value with value 0.0. */
+bool
+fp_zero_operand (rtx op)
+{
+ if (GET_MODE (op) != SFmode)
+ return false;
+
+ const REAL_VALUE_TYPE* r = CONST_DOUBLE_REAL_VALUE (op);
+ return real_equal (r, &dconst0) && ! REAL_VALUE_MINUS_ZERO (*r);
+}
+
+/* Returns true if OP is a floating point value with value 1.0. */
+bool
+fp_one_operand (rtx op)
+{
+ if (GET_MODE (op) != SFmode)
+ return false;
+
+ return real_equal (CONST_DOUBLE_REAL_VALUE (op), &dconst1);
+}
+
+/* Return the TLS type for TLS symbols. */
+enum tls_model
+tls_symbolic_operand (rtx op, machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (op) != SYMBOL_REF)
+ return TLS_MODEL_NONE;
+ return SYMBOL_REF_TLS_MODEL (op);
+}
+
+/* Return the destination address of a branch. */
+static int
+branch_dest (rtx branch)
+{
+ rtx dest = SET_SRC (PATTERN (branch));
+
+ if (GET_CODE (dest) == IF_THEN_ELSE)
+ dest = XEXP (dest, 1);
+
+ return INSN_ADDRESSES (INSN_UID (XEXP (dest, 0)));
+}
+
+/* Return nonzero if REG is not used after INSN.
+ We assume REG is a reload reg, and therefore does
+ not live past labels. It may live past calls or jumps though. */
+bool
+reg_unused_after (rtx reg, rtx_insn *insn)
+{
+ /* If the reg is set by this instruction, then it is safe for our
+ case. Disregard the case where this is a store to memory, since
+ we are checking a register used in the store address. */
+ rtx set = single_set (insn);
+ if (set && !MEM_P (SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ return true;
+
+ while ((insn = NEXT_INSN (insn)))
+ {
+ if (!INSN_P (insn))
+ continue;
+
+ rtx_code code = GET_CODE (insn);
+
+#if 0
+ /* If this is a label that existed before reload, then the register
+ is dead here. However, if this is a label added by reorg, then
+ the register may still be live here. We can't tell the difference,
+ so we just ignore labels completely. */
+ if (code == CODE_LABEL)
+ return 1;
+ /* else */
+#endif
+
+ if (code == JUMP_INSN)
+ return false;
+
+ /* If this is a sequence, we must handle them all at once.
+ We could have for instance a call that sets the target register,
+ and an insn in a delay slot that uses the register. In this case,
+ we must return 0. */
+ else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ rtx_sequence *seq = as_a <rtx_sequence *> (PATTERN (insn));
+ bool retval = false;
+
+ for (int i = 0; i < seq->len (); i++)
+ {
+ rtx_insn *this_insn = seq->insn (i);
+ rtx set = single_set (this_insn);
+
+ if (CALL_P (this_insn))
+ code = CALL_INSN;
+ else if (JUMP_P (this_insn))
+ {
+ if (INSN_ANNULLED_BRANCH_P (this_insn))
+ return false;
+ code = JUMP_INSN;
+ }
+
+ if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ return false;
+ if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ {
+ if (!MEM_P (SET_DEST (set)))
+ retval = true;
+ else
+ return false;
+ }
+ if (set == NULL_RTX
+ && reg_overlap_mentioned_p (reg, PATTERN (this_insn)))
+ return false;
+ }
+ if (retval)
+ return true;
+ else if (code == JUMP_INSN)
+ return false;
+ }
+
+ rtx set = single_set (insn);
+ if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ return false;
+ if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ return !MEM_P (SET_DEST (set));
+ if (set == NULL && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+ return false;
+
+ if (code == CALL_INSN && call_used_regs[REGNO (reg)])
+ return true;
+ }
+ return true;
+}
+
+
+static GTY(()) rtx t_reg_rtx;
+rtx
+get_t_reg_rtx (void)
+{
+ if (! t_reg_rtx)
+ t_reg_rtx = gen_rtx_REG (SImode, T_REG);
+ return t_reg_rtx;
+}
+
+static GTY(()) tree fpscr_values;
+
+static void
+emit_fpu_switch (rtx scratch, int index)
+{
+ if (fpscr_values == NULL)
+ {
+ tree t = build_index_type (integer_one_node);
+ t = build_array_type (integer_type_node, t);
+ t = build_decl (BUILTINS_LOCATION,
+ VAR_DECL, get_identifier ("__fpscr_values"), t);
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+ DECL_EXTERNAL (t) = 1;
+ TREE_STATIC (t) = 1;
+ TREE_PUBLIC (t) = 1;
+ TREE_USED (t) = 1;
+
+ fpscr_values = t;
+ }
+
+ rtx src = DECL_RTL (fpscr_values);
+ if (!can_create_pseudo_p ())
+ {
+ emit_move_insn (scratch, XEXP (src, 0));
+ if (index != 0)
+ emit_insn (gen_addsi3 (scratch, scratch, GEN_INT (index * 4)));
+ src = adjust_automodify_address (src, SImode, scratch, index * 4);
+ }
+ else
+ src = adjust_address (src, SImode, index * 4);
+
+ emit_insn (gen_lds_fpscr (src));
+}
+
+static rtx get_free_reg (HARD_REG_SET);
+
+/* This function returns a register to use to load the address to load
+ the fpscr from. Currently it always returns r1 or r7, but when we are
+ able to use pseudo registers after combine, or have a better mechanism
+ for choosing a register, it should be done here. */
+/* REGS_LIVE is the liveness information for the point for which we
+ need this allocation. In some bare-bones exit blocks, r1 is live at the
+ start. We can even have all of r0..r3 being live:
+__complex__ long long f (double d) { if (d == 0) return 2; else return 3; }
+ INSN before which new insns are placed with will clobber the register
+ we return. If a basic block consists only of setting the return value
+ register to a pseudo and using that register, the return value is not
+ live before or after this block, yet we we'll insert our insns right in
+ the middle. */
+static rtx
+get_free_reg (HARD_REG_SET regs_live)
+{
+ if (! TEST_HARD_REG_BIT (regs_live, 1))
+ return gen_rtx_REG (Pmode, 1);
+
+ /* Hard reg 1 is live; since this is a small register classes target,
+ there shouldn't be anything but a jump before the function end. */
+ gcc_assert (!TEST_HARD_REG_BIT (regs_live, 7));
+ return gen_rtx_REG (Pmode, 7);
+}
+
+/* This function will set the fpscr from memory.
+ MODE is the mode we are setting it to. */
+void
+fpscr_set_from_mem (int mode, HARD_REG_SET regs_live)
+{
+ enum attr_fp_mode fp_mode = (enum attr_fp_mode) mode;
+ enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
+
+ rtx addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
+ emit_fpu_switch (addr_reg, fp_mode == norm_mode);
+}
+
+/* Is the given character a logical line separator for the assembler? */
+#ifndef IS_ASM_LOGICAL_LINE_SEPARATOR
+#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) ((C) == ';')
+#endif
+
+static bool
+sequence_insn_p (rtx_insn *insn)
+{
+ rtx_insn* prev = PREV_INSN (insn);
+ if (prev == NULL)
+ return false;
+
+ rtx_insn* next = NEXT_INSN (prev);
+ if (next == NULL)
+ return false;
+
+ return INSN_P (next) && GET_CODE (PATTERN (next)) == SEQUENCE;
+}
+
+int
+sh_insn_length_adjustment (rtx_insn *insn)
+{
+ /* Instructions with unfilled delay slots take up an extra two bytes for
+ the nop in the delay slot. */
+ if (((NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ || CALL_P (insn) || JUMP_P (insn))
+ && ! sequence_insn_p (insn)
+ && get_attr_needs_delay_slot (insn) == NEEDS_DELAY_SLOT_YES)
+ return 2;
+
+ /* Increase the insn length of a cbranch without a delay slot insn to
+ force a delay slot which will be stuffed with a nop. */
+ if (TARGET_CBRANCH_FORCE_DELAY_SLOT && TARGET_SH2
+ && JUMP_P (insn) && get_attr_type (insn) == TYPE_CBRANCH
+ && ! sequence_insn_p (insn))
+ return 2;
+
+ /* sh-dsp parallel processing insn take four bytes instead of two. */
+
+ if (NONJUMP_INSN_P (insn))
+ {
+ int sum = 0;
+ rtx body = PATTERN (insn);
+ const char *templ;
+ char c;
+ bool maybe_label = true;
+
+ if (GET_CODE (body) == ASM_INPUT)
+ templ = XSTR (body, 0);
+ else if (asm_noperands (body) >= 0)
+ templ
+ = decode_asm_operands (body, NULL, NULL, NULL, NULL, NULL);
+ else
+ return 0;
+ do
+ {
+ int ppi_adjust = 0;
+
+ do
+ c = *templ++;
+ while (c == ' ' || c == '\t');
+ /* all sh-dsp parallel-processing insns start with p.
+ The only non-ppi sh insn starting with p is pref.
+ The only ppi starting with pr is prnd. */
+ if ((c == 'p' || c == 'P') && strncasecmp ("re", templ, 2))
+ ppi_adjust = 2;
+ /* The repeat pseudo-insn expands two three insns, a total of
+ six bytes in size. */
+ else if ((c == 'r' || c == 'R')
+ && ! strncasecmp ("epeat", templ, 5))
+ ppi_adjust = 4;
+ while (c && c != '\n'
+ && ! IS_ASM_LOGICAL_LINE_SEPARATOR (c, templ))
+ {
+ /* If this is a label, it is obviously not a ppi insn. */
+ if (c == ':' && maybe_label)
+ {
+ ppi_adjust = 0;
+ break;
+ }
+ else if (c == '\'' || c == '"')
+ maybe_label = false;
+ c = *templ++;
+ }
+ sum += ppi_adjust;
+ maybe_label = c != ':';
+ }
+ while (c);
+ return sum;
+ }
+ return 0;
+}
+
+/* Return TRUE for a valid displacement for the REG+disp addressing
+ with MODE. */
+bool
+sh_legitimate_index_p (machine_mode mode, rtx op, bool consider_sh2a,
+ bool allow_zero)
+{
+ if (! CONST_INT_P (op))
+ return false;
+
+ {
+ const HOST_WIDE_INT offset = INTVAL (op);
+ const int max_disp = sh_max_mov_insn_displacement (mode, consider_sh2a);
+ const int align_mask = mov_insn_alignment_mask (mode, consider_sh2a);
+
+ /* If the mode does not support any displacement always return false.
+ Even though an index of '0' is actually always valid, it will cause
+ troubles when e.g. a DFmode move is split into two SFmode moves,
+ where one SFmode move will have index '0' and the other move will
+ have index '4'. */
+ if (!allow_zero && max_disp < 1)
+ return false;
+
+ return offset >= 0 && offset <= max_disp && (offset & align_mask) == 0;
+ }
+}
+
+/* Recognize an RTL expression that is a valid memory address for
+ an instruction.
+ The MODE argument is the machine mode for the MEM expression
+ that wants to use this address.
+ Allow REG
+ REG+disp
+ REG+r0
+ REG++
+ --REG
+ GBR
+ GBR+disp */
+static bool
+sh_legitimate_address_p (machine_mode mode, rtx x, bool strict)
+{
+ if (REG_P (x) && REGNO (x) == GBR_REG)
+ return true;
+
+ if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
+ return true;
+ else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (x, 0), strict))
+ return true;
+ else if (GET_CODE (x) == PLUS)
+ {
+ rtx xop0 = XEXP (x, 0);
+ rtx xop1 = XEXP (x, 1);
+
+ if (REG_P (xop0) && REGNO (xop0) == GBR_REG)
+ return gbr_displacement (xop1, mode);
+
+ if (GET_MODE_SIZE (mode) <= 8
+ && MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
+ && sh_legitimate_index_p (mode, xop1, TARGET_SH2A, false))
+ return true;
+
+ if (GET_MODE_SIZE (mode) <= 4
+ || (TARGET_FPU_DOUBLE && TARGET_FMOVD && mode == DFmode))
+ {
+ if (MAYBE_BASE_REGISTER_RTX_P (xop1, strict)
+ && MAYBE_INDEX_REGISTER_RTX_P (xop0, strict))
+ return true;
+ if (MAYBE_INDEX_REGISTER_RTX_P (xop1, strict)
+ && MAYBE_BASE_REGISTER_RTX_P (xop0, strict))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec. */
+bool
+nonpic_symbol_mentioned_p (rtx x)
+{
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
+ || GET_CODE (x) == PC)
+ return true;
+
+ /* We don't want to look into the possible MEM location of a
+ CONST_DOUBLE, since we're not going to use it, in general. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return false;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_GOTPLT
+ || XINT (x, 1) == UNSPEC_GOTTPOFF
+ || XINT (x, 1) == UNSPEC_DTPOFF
+ || XINT (x, 1) == UNSPEC_TPOFF
+ || XINT (x, 1) == UNSPEC_PLT
+ || XINT (x, 1) == UNSPEC_PCREL
+ || XINT (x, 1) == UNSPEC_SYMOFF
+ || XINT (x, 1) == UNSPEC_PCREL_SYMOFF
+ || XINT (x, 1) == UNSPEC_GOTFUNCDESC
+ || XINT (x, 1) == UNSPEC_GOTOFFFUNCDESC))
+ return false;
+
+ const char* fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (int i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
+ return true;
+ }
+ else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
+ return true;
+ }
+
+ return false;
+}
+
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED, rtx reg)
+{
+ if (tls_symbolic_operand (orig, Pmode) != TLS_MODEL_NONE)
+ return orig;
+
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
+ {
+ if (reg == NULL_RTX)
+ reg = gen_reg_rtx (Pmode);
+
+ if (TARGET_FDPIC
+ && GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (orig))
+ {
+ /* Weak functions may be NULL which doesn't work with
+ GOTOFFFUNCDESC because the runtime offset is not known. */
+ if (SYMBOL_REF_WEAK (orig))
+ emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOTOFFFUNCDESC2reg (reg, orig));
+ }
+ else if (TARGET_FDPIC
+ && (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_DECL (orig)
+ && (TREE_READONLY (SYMBOL_REF_DECL (orig))
+ || SYMBOL_REF_EXTERNAL_P (orig)
+ || DECL_SECTION_NAME(SYMBOL_REF_DECL (orig))))))
+ /* In FDPIC, GOTOFF can only be used for writable data. */
+ emit_insn (gen_symGOT2reg (reg, orig));
+ else
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == NULL_RTX)
+ reg = gen_reg_rtx (Pmode);
+
+ if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P (orig))
+ emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Given a (logical) mode size and an offset in bytes, try to find a the
+ appropriate displacement value for a mov insn. On SH the displacements
+ are limited to max. 60 bytes for SImode, max. 30 bytes in HImode and max.
+ 15 bytes in QImode. To compensate this we create a new base address by
+ adding an adjustment value to it.
+
+ If the originally requested offset is greater than 127 we prefer using
+ values 124..127 over 128..131 to increase opportunities to use the
+ add #imm, Rn insn.
+
+ In some cases it is possible that a requested offset might seem unaligned
+ or inappropriate for the mode size, like offset = 2 and mode size = 4.
+ This is compensated by adjusting the base address so that the effective
+ address of the displacement move insn will be aligned.
+
+ This is not the best possible way of rebasing the base address, as it
+ does not look at other present displacement addressings around it.
+ In some cases this can create more base address adjustments than would
+ actually be necessary. */
+struct disp_adjust
+{
+ rtx offset_adjust;
+ rtx mov_disp;
+};
+
+static struct disp_adjust
+sh_find_mov_disp_adjust (machine_mode mode, HOST_WIDE_INT offset)
+{
+ struct disp_adjust res = { NULL_RTX, NULL_RTX };
+
+ /* Do not try to use SH2A's large displacements here, because this would
+ effectively disable the small displacement insns. */
+ const int mode_sz = GET_MODE_SIZE (mode);
+ const int mov_insn_sz = mov_insn_size (mode, false);
+ const int max_disp = sh_max_mov_insn_displacement (mode, false);
+ const int max_disp_next = max_disp + mov_insn_sz;
+ HOST_WIDE_INT align_modifier = offset > 127 ? mov_insn_sz : 0;
+ HOST_WIDE_INT offset_adjust;
+
+ /* In some cases this actually does happen and we must check for it. */
+ if (mode_sz < 1 || mode_sz > 8 || max_disp < 1)
+ return res;
+
+ /* Keeps the previous behavior for QImode displacement addressing.
+ This just decides how the offset is re-based. Removing this special
+ case will result in slightly bigger code on average, but it's not that
+ bad actually. */
+ if (mov_insn_sz == 1)
+ align_modifier = 0;
+
+ offset_adjust = ((offset + align_modifier) & ~max_disp) - align_modifier;
+
+ if (mode_sz + offset - offset_adjust <= max_disp_next)
+ {
+ res.offset_adjust = GEN_INT (offset_adjust);
+ res.mov_disp = GEN_INT (offset - offset_adjust);
+ }
+
+ return res;
+}
+
+/* Try to modify an illegitimate address and make it legitimate.
+ If we find one, return the new, valid address.
+ Otherwise, return the original address. */
+static rtx
+sh_legitimize_address (rtx x, rtx oldx, machine_mode mode)
+{
+ if (flag_pic)
+ x = legitimize_pic_address (oldx, mode, NULL_RTX);
+
+ if ((TARGET_FPU_DOUBLE && mode == DFmode)
+ || (TARGET_SH2E && mode == SFmode))
+ return x;
+
+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))
+ && BASE_REGISTER_RTX_P (XEXP (x, 0)))
+ {
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode,
+ INTVAL (XEXP (x, 1)));
+
+ if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
+ {
+ rtx sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
+ adj.offset_adjust, NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+ return gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
+ }
+ }
+ return x;
+}
+
+/* Attempt to replace *p, which is an address that needs reloading, with
+ a valid memory address for an operand of mode MODE.
+ Like for sh_legitimize_address, for the SH we try to get a normal form
+ of the address. That will allow inheritance of the address reloads. */
+bool
+sh_legitimize_reload_address (rtx *p, machine_mode mode, int opnum,
+ int itype)
+{
+ enum reload_type type = (enum reload_type) itype;
+ const int mode_sz = GET_MODE_SIZE (mode);
+
+ if (sh_lra_p ())
+ return false;
+
+ if (GET_CODE (*p) == PLUS && CONST_INT_P (XEXP (*p, 1))
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (*p, 0), true))
+ {
+ const HOST_WIDE_INT offset = INTVAL (XEXP (*p, 1));
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode, offset);
+
+ if (TARGET_SH2A && mode == DFmode && (offset & 0x7))
+ {
+ push_reload (*p, NULL_RTX, p, NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ return true;
+ }
+
+ if (TARGET_SH2E && mode == SFmode)
+ {
+ *p = copy_rtx (*p);
+ push_reload (*p, NULL_RTX, p, NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ return true;
+ }
+
+ /* FIXME: Do not allow to legitimize QImode and HImode displacement
+ moves because then reload has a problem figuring the constraint
+ that the move insn target/source reg must be R0.
+ Or maybe some handling is wrong in sh_secondary_reload for this
+ to work properly? */
+ if ((mode_sz == 4 || mode_sz == 8)
+ && ! (TARGET_SH4 && mode == DFmode)
+ && adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
+ {
+ rtx sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), adj.offset_adjust);
+ *p = gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
+ push_reload (sum, NULL_RTX, &XEXP (*p, 0), NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ return true;
+ }
+ }
+
+ /* We must re-recognize what we created before. */
+ if (GET_CODE (*p) == PLUS
+ && (mode_sz == 4 || mode_sz == 8)
+ && GET_CODE (XEXP (*p, 0)) == PLUS
+ && CONST_INT_P (XEXP (XEXP (*p, 0), 1))
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true)
+ && CONST_INT_P (XEXP (*p, 1))
+ && ! (TARGET_SH2E && mode == SFmode))
+ {
+ /* Because this address is so complex, we know it must have
+ been created by LEGITIMIZE_RELOAD_ADDRESS before; thus,
+ it is already unshared, and needs no further unsharing. */
+ push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ return true;
+ }
+
+ return false;
+}
+
+/* In the name of slightly smaller debug output, and to cater to
+ general assembler lossage, recognize various UNSPEC sequences
+ and turn them back into a direct symbol reference. */
+static rtx
+sh_delegitimize_address (rtx orig_x)
+{
+ orig_x = delegitimize_mem_from_attrs (orig_x);
+
+ rtx x = orig_x;
+ if (MEM_P (x))
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == CONST)
+ {
+ rtx y = XEXP (x, 0);
+ if (GET_CODE (y) == UNSPEC)
+ {
+ if (XINT (y, 1) == UNSPEC_GOT
+ || XINT (y, 1) == UNSPEC_GOTOFF
+ || XINT (y, 1) == UNSPEC_SYMOFF)
+ return XVECEXP (y, 0, 0);
+ else if (XINT (y, 1) == UNSPEC_PCREL_SYMOFF)
+ {
+ if (GET_CODE (XVECEXP (y, 0, 0)) == CONST)
+ {
+ rtx symplt = XEXP (XVECEXP (y, 0, 0), 0);
+
+ if (GET_CODE (symplt) == UNSPEC
+ && (XINT (symplt, 1) == UNSPEC_PLT
+ || XINT (symplt, 1) == UNSPEC_PCREL))
+ return XVECEXP (symplt, 0, 0);
+ }
+ }
+ }
+ }
+
+ return orig_x;
+}
+
+/* Mark the use of a constant in the literal table. If the constant
+ has multiple labels, make it unique. */
+static rtx
+mark_constant_pool_use (rtx x)
+{
+ if (x == NULL_RTX)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ case LABEL_REF:
+ x = XEXP (x, 0);
+ case CODE_LABEL:
+ break;
+ default:
+ return x;
+ }
+
+ /* Get the first label in the list of labels for the same constant
+ and delete another labels in the list. */
+ rtx_insn* lab = as_a <rtx_insn*> (x);
+ for (rtx_insn* insn = PREV_INSN (lab); insn; insn = PREV_INSN (insn))
+ {
+ if (!LABEL_P (insn)
+ || LABEL_REFS (insn) != NEXT_INSN (insn))
+ break;
+ lab = insn;
+ }
+
+ for (rtx insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
+ as_a<rtx_insn *> (insn)->set_deleted ();
+
+ /* Mark constants in a window. */
+ for (rtx_insn* insn = NEXT_INSN (as_a <rtx_insn *> (x)); insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (!NONJUMP_INSN_P (insn))
+ continue;
+
+ rtx pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != UNSPEC_VOLATILE)
+ continue;
+
+ switch (XINT (pattern, 1))
+ {
+ case UNSPECV_CONST2:
+ case UNSPECV_CONST4:
+ case UNSPECV_CONST8:
+ XVECEXP (pattern, 0, 1) = const1_rtx;
+ break;
+ case UNSPECV_WINDOW_END:
+ if (XVECEXP (pattern, 0, 0) == x)
+ return lab;
+ break;
+ case UNSPECV_CONST_END:
+ return lab;
+ default:
+ break;
+ }
+ }
+
+ return lab;
+}
+
+/* Return true if it's possible to redirect BRANCH1 to the destination
+ of an unconditional jump BRANCH2. We only want to do this if the
+ resulting branch will have a short displacement. */
+static bool
+sh_can_follow_jump (const rtx_insn *branch1, const rtx_insn *branch2)
+{
+ /* Don't follow if BRANCH2 is possible to be a jump crossing between
+ hot and cold partitions. */
+ if (flag_reorder_blocks_and_partition
+ && simplejump_p (branch2)
+ && CROSSING_JUMP_P (branch2))
+ return false;
+
+ if (flag_expensive_optimizations && simplejump_p (branch2))
+ {
+ rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
+ rtx_insn *insn;
+ int distance;
+
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = PREV_INSN (insn))
+ {
+ if (insn == dest)
+ return true;
+ else
+ distance += get_attr_length (insn);
+ }
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = NEXT_INSN (insn))
+ {
+ if (insn == dest)
+ return true;
+ else
+ distance += get_attr_length (insn);
+ }
+ }
+ return false;
+}
+
+/* Return nonzero if register old_reg can be renamed to register new_reg. */
+bool
+sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+ unsigned int new_reg)
+{
+ /* Interrupt functions can only use registers that have already been
+ saved by the prologue, even if they would normally be
+ call-clobbered. */
+ if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
+ return false;
+
+ return true;
+}
+
+/* Function to update the integer COST
+ based on the relationship between INSN that is dependent on
+ DEP_INSN through the dependence LINK. The default is to make no
+ adjustment to COST. This can be used for example to specify to
+ the scheduler that an output- or anti-dependence does not incur
+ the same cost as a data-dependence. The return value should be
+ the new value for COST. */
+static int
+sh_adjust_cost (rtx_insn *insn, int dep_type, rtx_insn *dep_insn, int cost,
+ unsigned int)
+{
+ rtx reg, use_pat;
+
+ if (dep_type == 0)
+ {
+ if (recog_memoized (insn) < 0
+ || recog_memoized (dep_insn) < 0)
+ return cost;
+
+ rtx dep_set = single_set (dep_insn);
+
+ /* The latency that we specify in the scheduling description refers
+ to the actual output, not to an auto-increment register; for that,
+ the latency is one. */
+ if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
+ {
+ rtx set = single_set (insn);
+
+ if (set
+ && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
+ && (!MEM_P (SET_DEST (set))
+ || !reg_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_DEST (set), 0))))
+ cost = 1;
+ }
+ /* The only input for a call that is timing-critical is the
+ function's address. */
+ if (CALL_P (insn))
+ {
+ rtx call = get_call_rtx_from (insn);
+ if (call
+ /* sibcalli_thunk uses a symbol_ref in an unspec. */
+ && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
+ || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
+ cost -= TARGET_SH4_300 ? 3 : 6;
+ }
+ /* Likewise, the most timing critical input for an sfuncs call
+ is the function address. However, sfuncs typically start
+ using their arguments pretty quickly.
+ Assume a four cycle delay for SH4 before they are needed.
+ Cached ST40-300 calls are quicker, so assume only a one
+ cycle delay there.
+ ??? Maybe we should encode the delays till input registers
+ are needed by sfuncs into the sfunc call insn. */
+ /* All sfunc calls are parallels with at least four components.
+ Exploit this to avoid unnecessary calls to sfunc_uses_reg. */
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && XVECLEN (PATTERN (insn), 0) >= 4
+ && (reg = sfunc_uses_reg (insn)))
+ {
+ if (! reg_set_p (reg, dep_insn))
+ cost -= TARGET_SH4_300 ? 1 : 4;
+ }
+ if (TARGET_HARD_SH4 && !TARGET_SH4_300)
+ {
+ attr_type dep_type = get_attr_type (dep_insn);
+ attr_type type;
+ if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
+ cost--;
+ else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
+ && (type = get_attr_type (insn)) != TYPE_CALL
+ && type != TYPE_SFUNC)
+ cost--;
+ /* When the preceding instruction loads the shift amount of
+ the following SHAD/SHLD, the latency of the load is increased
+ by 1 cycle. */
+ if (get_attr_type (insn) == TYPE_DYN_SHIFT
+ && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
+ && reg_overlap_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_SRC (single_set (insn)),
+ 1)))
+ cost++;
+ /* When an LS group instruction with a latency of less than
+ 3 cycles is followed by a double-precision floating-point
+ instruction, FIPR, or FTRV, the latency of the first
+ instruction is increased to 3 cycles. */
+ else if (cost < 3
+ && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
+ && get_attr_dfp_comp (insn) == DFP_COMP_YES)
+ cost = 3;
+ /* The lsw register of a double-precision computation is ready one
+ cycle earlier. */
+ else if (reload_completed
+ && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
+ && (use_pat = single_set (insn))
+ && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
+ SET_SRC (use_pat)))
+ cost -= 1;
+
+ if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
+ && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
+ cost -= 1;
+ }
+ else if (TARGET_SH4_300)
+ {
+ /* Stores need their input register two cycles later. */
+ attr_type type;
+ if (dep_set && cost >= 1
+ && ((type = get_attr_type (insn)) == TYPE_STORE
+ || type == TYPE_PSTORE
+ || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
+ {
+ rtx set = single_set (insn);
+
+ if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
+ && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
+ {
+ cost -= 2;
+ /* But don't reduce the cost below 1 if the address depends
+ on a side effect of dep_insn. */
+ if (cost < 1
+ && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
+ cost = 1;
+ }
+ }
+ }
+ }
+ /* An anti-dependence penalty of two applies if the first insn is a double
+ precision fadd / fsub / fmul. */
+ else if (!TARGET_SH4_300
+ && dep_type == REG_DEP_ANTI
+ && recog_memoized (dep_insn) >= 0
+ && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
+ || get_attr_type (dep_insn) == TYPE_DFP_MUL)
+ /* A lot of alleged anti-flow dependences are fake,
+ so check this one is real. */
+ && flow_dependent_p (dep_insn, insn))
+ cost = 2;
+
+ return cost;
+}
+
+/* Check if INSN is flow-dependent on DEP_INSN. Can also be used to check
+ if DEP_INSN is anti-flow dependent on INSN. */
+static bool
+flow_dependent_p (rtx_insn *insn, rtx_insn *dep_insn)
+{
+ rtx tmp = PATTERN (insn);
+
+ note_stores (dep_insn, flow_dependent_p_1, &tmp);
+ return tmp == NULL_RTX;
+}
+
+/* A helper function for flow_dependent_p called through note_stores. */
+static void
+flow_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx * pinsn = (rtx *) data;
+
+ if (*pinsn && reg_referenced_p (x, *pinsn))
+ *pinsn = NULL_RTX;
+}
+
+/* For use by sh_allocate_initial_value. Note that sh.md contains some
+ 'special function' patterns (type sfunc) that clobber pr, but that
+ do not look like function calls to leaf_function_p. Hence we must
+ do this extra check. */
+static int
+sh_pr_n_sets (void)
+{
+ return DF_REG_DEF_COUNT (PR_REG);
+}
+
+/* Return where to allocate pseudo for a given hard register initial
+ value. */
+static rtx
+sh_allocate_initial_value (rtx hard_reg)
+{
+ if (REGNO (hard_reg) == PR_REG)
+ {
+ if (crtl->is_leaf && ! sh_pr_n_sets ())
+ return hard_reg;
+ else
+ return gen_frame_mem (Pmode, return_address_pointer_rtx);
+ }
+
+ return NULL_RTX;
+}
+
+/* This function returns "2" to indicate dual issue for the SH4
+ processor. To be used by the DFA pipeline description. */
+static int
+sh_issue_rate (void)
+{
+ if (TARGET_SUPERSCALAR)
+ return 2;
+ else
+ return 1;
+}
+
+/* Functions for ready queue reordering for sched1. */
+
+/* Get weight for mode for a set x. */
+static short
+find_set_regmode_weight (rtx x, machine_mode mode)
+{
+ if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode))
+ return 1;
+ if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode))
+ {
+ if (REG_P (SET_DEST (x)))
+ {
+ if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
+ return 1;
+ else
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* Get regmode weight for insn. */
+static short
+find_insn_regmode_weight (rtx insn, machine_mode mode)
+{
+ /* Increment weight for each register born here. */
+ rtx x = PATTERN (insn);
+ short reg_weight = find_set_regmode_weight (x, mode);
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
+ {
+ x = XVECEXP (PATTERN (insn), 0, j);
+ reg_weight += find_set_regmode_weight (x, mode);
+ }
+ }
+ /* Decrement weight for each register that dies here. */
+ for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ {
+ if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED)
+ {
+ rtx note = XEXP (x, 0);
+ if (REG_P (note) && GET_MODE (note) == mode)
+ reg_weight--;
+ }
+ }
+ return reg_weight;
+}
+
+/* Calculate regmode weights for all insns of a basic block. */
+static void
+find_regmode_weight (basic_block b, machine_mode mode)
+{
+ rtx_insn *insn, *next_tail, *head, *tail;
+
+ get_ebb_head_tail (b, b, &head, &tail);
+ next_tail = NEXT_INSN (tail);
+
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ {
+ /* Handle register life information. */
+ if (!INSN_P (insn))
+ continue;
+
+ if (mode == SFmode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode)
+ + 2 * find_insn_regmode_weight (insn, DFmode);
+ else if (mode == SImode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode)
+ + 2 * find_insn_regmode_weight (insn, DImode);
+ }
+}
+
+/* Comparison function for ready queue sorting. */
+static int
+rank_for_reorder (const void *x, const void *y)
+{
+ rtx_insn *tmp = *(rtx_insn * const *) y;
+ rtx_insn *tmp2 = *(rtx_insn * const *) x;
+
+ /* The insn in a schedule group should be issued the first. */
+ if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
+ return SCHED_GROUP_P (tmp2) ? 1 : -1;
+
+ /* If insns are equally good, sort by INSN_LUID (original insn order), This
+ minimizes instruction movement, thus minimizing sched's effect on
+ register pressure. */
+ return INSN_LUID (tmp) - INSN_LUID (tmp2);
+}
+
+/* Resort the array A in which only element at index N may be out of order. */
+static void
+swap_reorder (rtx_insn **a, int n)
+{
+ rtx_insn *insn = a[n - 1];
+ int i = n - 2;
+
+ while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0)
+ {
+ a[i + 1] = a[i];
+ i -= 1;
+ }
+ a[i + 1] = insn;
+}
+
+/* Sort the ready list by ascending priority. */
+static void
+ready_reorder (rtx_insn **ready, int nready)
+{
+ if (nready == 2)
+ swap_reorder (ready, nready);
+ else if (nready > 2)
+ qsort (ready, nready, sizeof (rtx_insn *), rank_for_reorder);
+}
+
+/* Count life regions of r0 for a block. */
+static int
+find_r0_life_regions (basic_block b)
+{
+ bool live;
+ int set;
+ int death = 0;
+
+ if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
+ {
+ set = 1;
+ live = true;
+ }
+ else
+ {
+ set = 0;
+ live = false;
+ }
+
+ rtx_insn* insn = BB_HEAD (b);
+ rtx_insn* end = BB_END (b);
+ rtx r0_reg = gen_rtx_REG (SImode, R0_REG);
+ while (1)
+ {
+ if (INSN_P (insn))
+ {
+ if (find_regno_note (insn, REG_DEAD, R0_REG))
+ {
+ death++;
+ live = false;
+ }
+
+ rtx pset;
+ if (!live
+ && (pset = single_set (insn))
+ && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
+ && !find_regno_note (insn, REG_UNUSED, R0_REG))
+ {
+ set++;
+ live = true;
+ }
+ }
+ if (insn == end)
+ break;
+ insn = NEXT_INSN (insn);
+ }
+ return set - death;
+}
+
+/* Calculate regmode weights for all insns of all basic block. */
+static void
+sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int old_max_uid)
+{
+ basic_block b;
+
+ regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
+ regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
+ r0_life_regions = 0;
+
+ FOR_EACH_BB_REVERSE_FN (b, cfun)
+ {
+ find_regmode_weight (b, SImode);
+ find_regmode_weight (b, SFmode);
+ if (!reload_completed)
+ r0_life_regions += find_r0_life_regions (b);
+ }
+
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+}
+
+/* Cleanup. */
+static void
+sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED)
+{
+ if (regmode_weight[0])
+ {
+ free (regmode_weight[0]);
+ regmode_weight[0] = NULL;
+ }
+ if (regmode_weight[1])
+ {
+ free (regmode_weight[1]);
+ regmode_weight[1] = NULL;
+ }
+}
+
+/* Cache the can_issue_more so that we can return it from reorder2. Also,
+ keep count of register pressures on SImode and SFmode. */
+static int
+sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx_insn *insn,
+ int can_issue_more)
+{
+ if (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ cached_can_issue_more = can_issue_more - 1;
+ else
+ cached_can_issue_more = can_issue_more;
+
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode);
+ CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode);
+
+ return cached_can_issue_more;
+}
+
+static void
+sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int veclen ATTRIBUTE_UNUSED)
+{
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+}
+
+/* Some magic numbers. */
+/* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+#define R0_MAX_LIFE_REGIONS 2
+/* Register Pressure thresholds for SImode and SFmode registers. */
+#define SIMODE_MAX_WEIGHT 5
+#define SFMODE_MAX_WEIGHT 10
+
+/* Return true if the pressure is high for MODE. */
+static bool
+high_pressure (machine_mode mode)
+{
+ /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+ if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
+ return true;
+
+ if (mode == SFmode)
+ return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
+ else
+ return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT);
+}
+
+/* Reorder ready queue if register pressure is high. */
+static int
+sh_reorder (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx_insn **ready,
+ int *n_readyp,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return sh_issue_rate ();
+
+ if (high_pressure (SFmode) || high_pressure (SImode))
+ {
+ ready_reorder (ready, *n_readyp);
+ }
+
+ return sh_issue_rate ();
+}
+
+/* Skip cycles if the current register pressure is high. */
+static int
+sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx_insn **ready ATTRIBUTE_UNUSED,
+ int *n_readyp ATTRIBUTE_UNUSED,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ if (high_pressure(SFmode) || high_pressure (SImode))
+ skip_cycles = 1;
+
+ return cached_can_issue_more;
+}
+
+/* Skip cycles without sorting the ready queue. This will move insn from
+ Q->R. If this is the last cycle we are skipping; allow sorting of ready
+ queue by sh_reorder. */
+
+/* Generally, skipping these many cycles are sufficient for all insns to move
+ from Q -> R. */
+#define MAX_SKIPS 8
+
+static int
+sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx_insn *insn ATTRIBUTE_UNUSED,
+ int last_clock_var,
+ int clock_var,
+ int *sort_p)
+{
+ if (reload_completed)
+ return 0;
+
+ if (skip_cycles)
+ {
+ if ((clock_var - last_clock_var) < MAX_SKIPS)
+ {
+ *sort_p = 0;
+ return 1;
+ }
+ /* If this is the last cycle we are skipping, allow reordering of R. */
+ if ((clock_var - last_clock_var) == MAX_SKIPS)
+ {
+ *sort_p = 1;
+ return 1;
+ }
+ }
+
+ skip_cycles = 0;
+
+ return 0;
+}
+
+static bool
+sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
+{
+ return TARGET_HITACHI || sh_attr_renesas_p (record_type);
+}
+
+/*
+ On the SH1..SH4, the trampoline looks like
+ 2 0002 D202 mov.l l2,r2
+ 1 0000 D301 mov.l l1,r3
+ 3 0004 422B jmp @r2
+ 4 0006 0009 nop
+ 5 0008 00000000 l1: .long area
+ 6 000c 00000000 l2: .long function
+
+ FDPIC needs a form that includes a function descriptor and
+ code to load the GOT register:
+ 0 0000 00000000 .long l0
+ 1 0004 00000000 .long gotval
+ 2 0008 D302 l0: mov.l l1,r3
+ 3 000a D203 mov.l l2,r2
+ 4 000c 6122 mov.l @r2,r1
+ 5 000e 5C21 mov.l @(4,r2),r12
+ 6 0010 412B jmp @r1
+ 7 0012 0009 nop
+ 8 0014 00000000 l1: .long area
+ 9 0018 00000000 l2: .long function
+
+ SH5 (compact) uses r1 instead of r3 for the static chain. */
+
+/* Emit insns to store a value at memory address + offset. */
+static void
+sh_emit_storesi (rtx addr, HOST_WIDE_INT offset, rtx value)
+{
+ gcc_assert ((offset & 3) == 0);
+ emit_move_insn (offset == 0
+ ? change_address (addr, SImode, NULL_RTX)
+ : adjust_address (addr, SImode, offset), value);
+}
+
+/* Emit insns to store w0 at addr + offset and w1 at addr + offset + 2. */
+static void
+sh_emit_storehi (rtx addr, HOST_WIDE_INT offset, uint16_t w0, uint16_t w1)
+{
+ sh_emit_storesi (addr, offset, gen_int_mode (TARGET_LITTLE_ENDIAN
+ ? (w0 | (w1 << 16))
+ : (w1 | (w0 << 16)), SImode));
+}
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+ FNADDR is an RTX for the address of the function's pure code.
+ CXT is an RTX for the static chain value for the function. */
+static void
+sh_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
+{
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx tramp = force_reg (Pmode, XEXP (tramp_mem, 0));
+
+ if (TARGET_FDPIC)
+ {
+ rtx a = force_reg (Pmode, plus_constant (Pmode, XEXP (tramp_mem, 0), 8));
+
+ sh_emit_storesi (tramp_mem, 0, a);
+ sh_emit_storesi (tramp_mem, 4, sh_get_fdpic_reg_initial_val ());
+
+ sh_emit_storehi (tramp_mem, 8, 0xd302, 0xd203);
+ sh_emit_storehi (tramp_mem, 12, 0x6122, 0x5c21);
+ sh_emit_storehi (tramp_mem, 16, 0x412b, 0x0009);
+
+ sh_emit_storesi (tramp_mem, 20, cxt);
+ sh_emit_storesi (tramp_mem, 24, fnaddr);
+ }
+ else
+ {
+ sh_emit_storehi (tramp_mem, 0, 0xd202, 0xd301);
+ sh_emit_storehi (tramp_mem, 4, 0x422b, 0x0009);
+
+ sh_emit_storesi (tramp_mem, 8, cxt);
+ sh_emit_storesi (tramp_mem, 12, fnaddr);
+ }
+ if (TARGET_HARD_SH4)
+ {
+ if (!TARGET_INLINE_IC_INVALIDATE
+ || (!(TARGET_SH4A || TARGET_SH4_300) && TARGET_USERMODE))
+ emit_library_call (function_symbol (NULL, "__ic_invalidate",
+ FUNCTION_ORDINARY).sym,
+ LCT_NORMAL, VOIDmode, tramp, SImode);
+ else
+ emit_insn (gen_ic_invalidate_line (tramp));
+ }
+}
+
+/* On SH5, trampolines are SHmedia code, so add 1 to the address. */
+static rtx
+sh_trampoline_adjust_address (rtx tramp)
+{
+ return tramp;
+}
+
+/* If PIC, we cannot make sibling calls to global functions
+ because the PLT requires r12 to be live. */
+static bool
+sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+ return (1
+ && ! sh_cfun_interrupt_handler_p ()
+ && (! flag_pic || TARGET_FDPIC
+ || (decl && ! (TREE_PUBLIC (decl) || DECL_WEAK (decl)))
+ || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
+}
+
+/* Expand to appropriate sym*_label2reg for SYM and SIBCALL_P. */
+void
+sh_expand_sym_label2reg (rtx reg, rtx sym, rtx lab, bool sibcall_p)
+{
+ const_tree decl = SYMBOL_REF_DECL (sym);
+ bool is_weak = (decl && DECL_P (decl) && DECL_WEAK (decl));
+
+ if (!is_weak && SYMBOL_REF_LOCAL_P (sym))
+ emit_insn (gen_sym_label2reg (reg, sym, lab));
+ else if (sibcall_p && SYMBOL_REF_LOCAL_P (sym))
+ emit_insn (gen_symPCREL_label2reg (reg, sym, lab));
+ else
+ emit_insn (gen_symPLT_label2reg (reg, sym, lab));
+}
+
+/* Machine specific built-in functions. */
+
+struct builtin_description
+{
+ bool (* const is_enabled) (void);
+ const enum insn_code icode;
+ const char *const name;
+ int signature;
+ tree fndecl;
+};
+
+/* This function can be used if there are any built-ins that are not for
+ SHmedia. It's commented out to avoid the defined-but-unused warning. */
+static bool
+sh1_builtin_p (void)
+{
+ return TARGET_SH1;
+}
+
+/* describe number and signedness of arguments; arg[0] == result
+ (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
+/* 9: 64-bit pointer, 10: 32-bit pointer */
+static const char signature_args[][4] =
+{
+#define SH_BLTIN_V2SI2 0
+ { 4, 4 },
+#define SH_BLTIN_V4HI2 1
+ { 4, 4 },
+#define SH_BLTIN_V2SI3 2
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI3 3
+ { 4, 4, 4 },
+#define SH_BLTIN_V8QI3 4
+ { 4, 4, 4 },
+#define SH_BLTIN_MAC_HISI 5
+ { 1, 4, 4, 1 },
+#define SH_BLTIN_SH_HI 6
+ { 4, 4, 1 },
+#define SH_BLTIN_SH_SI 7
+ { 4, 4, 1 },
+#define SH_BLTIN_V4HI2V2SI 8
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI2V8QI 9
+ { 4, 4, 4 },
+#define SH_BLTIN_SISF 10
+ { 4, 2 },
+#define SH_BLTIN_LDUA_L 11
+ { 2, 10 },
+#define SH_BLTIN_LDUA_Q 12
+ { 1, 10 },
+#define SH_BLTIN_STUA_L 13
+ { 0, 10, 2 },
+#define SH_BLTIN_STUA_Q 14
+ { 0, 10, 1 },
+#define SH_BLTIN_LDUA_L64 15
+ { 2, 9 },
+#define SH_BLTIN_LDUA_Q64 16
+ { 1, 9 },
+#define SH_BLTIN_STUA_L64 17
+ { 0, 9, 2 },
+#define SH_BLTIN_STUA_Q64 18
+ { 0, 9, 1 },
+#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
+#define SH_BLTIN_2 19
+#define SH_BLTIN_SU 19
+ { 1, 2 },
+#define SH_BLTIN_3 20
+#define SH_BLTIN_SUS 20
+ { 2, 2, 1 },
+#define SH_BLTIN_PSSV 21
+ { 0, 8, 2, 2 },
+#define SH_BLTIN_XXUU 22
+#define SH_BLTIN_UUUU 22
+ { 1, 1, 1, 1 },
+#define SH_BLTIN_PV 23
+ { 0, 8 },
+#define SH_BLTIN_VP 24
+ { 8, 0 },
+#define SH_BLTIN_UV 25
+ { 1, 0 },
+#define SH_BLTIN_VU 26
+ { 0, 1 },
+};
+/* mcmv: operands considered unsigned. */
+/* mmulsum_wq, msad_ubq: result considered unsigned long long. */
+/* mperm: control value considered unsigned int. */
+/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */
+/* mshards_q: returns signed short. */
+/* nsb: takes long long arg, returns unsigned char. */
+static struct builtin_description bdesc[] =
+{
+ { sh1_builtin_p,
+ CODE_FOR_sts_fpscr, "__builtin_sh_get_fpscr", SH_BLTIN_UV, 0 },
+ { sh1_builtin_p,
+ CODE_FOR_set_fpscr, "__builtin_sh_set_fpscr", SH_BLTIN_VU, 0 },
+};
+
+static tree sh_builtin_get_fpscr;
+static tree sh_builtin_set_fpscr;
+
+static void
+sh_init_builtins (void)
+{
+ tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
+ memset (shared, 0, sizeof shared);
+
+ for (unsigned int di = 0; di < ARRAY_SIZE (bdesc); ++di)
+ {
+ builtin_description* d = &bdesc[di];
+
+ if (!d->is_enabled ())
+ continue;
+
+ tree type, arg_type = NULL_TREE;
+ int signature = d->signature;
+
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
+ type = shared[signature];
+ else
+ {
+ int has_result = signature_args[signature][0] != 0;
+ tree args[3];
+
+ if (! TARGET_FPU_ANY
+ && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
+ continue;
+ for (unsigned int i = 0; i < ARRAY_SIZE (args); i++)
+ args[i] = NULL_TREE;
+ for (int i = 3; ; i--)
+ {
+ int arg = signature_args[signature][i];
+ int opno = i - 1 + has_result;
+
+ if (arg & 8)
+ arg_type = ptr_type_node;
+ else if (arg)
+ arg_type = (*lang_hooks.types.type_for_mode)
+ (insn_data[d->icode].operand[opno].mode, (arg & 1));
+ else if (i)
+ continue;
+ else
+ arg_type = void_type_node;
+ if (i == 0)
+ break;
+ args[i-1] = arg_type;
+ }
+ type = build_function_type_list (arg_type, args[0], args[1],
+ args[2], NULL_TREE);
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
+ shared[signature] = type;
+ }
+ d->fndecl =
+ add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
+ NULL, NULL_TREE);
+ /* Recode {sts,set}_fpscr decls for sh_atomic_assign_expand_fenv. */
+ if (d->icode == CODE_FOR_sts_fpscr)
+ sh_builtin_get_fpscr = d->fndecl;
+ else if (d->icode == CODE_FOR_set_fpscr)
+ sh_builtin_set_fpscr = d->fndecl;
+ }
+}
+
+/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */
+
+static void
+sh_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+ const unsigned SH_FE_INVALID = 64;
+ const unsigned SH_FE_DIVBYZERO = 32;
+ const unsigned SH_FE_OVERFLOW = 16;
+ const unsigned SH_FE_UNDERFLOW = 8;
+ const unsigned SH_FE_INEXACT = 4;
+ const unsigned HOST_WIDE_INT SH_FE_ALL_EXCEPT = (SH_FE_INVALID
+ | SH_FE_DIVBYZERO
+ | SH_FE_OVERFLOW
+ | SH_FE_UNDERFLOW
+ | SH_FE_INEXACT);
+ const unsigned HOST_WIDE_INT SH_FE_EXCEPT_SHIFT = 5;
+ tree fenv_var, mask, ld_fenv, masked_fenv;
+ tree new_fenv_var, reload_fenv, restore_fnenv;
+ tree update_call, atomic_feraiseexcept, hold_fnclex;
+
+ if (! TARGET_FPU_ANY)
+ return;
+
+ /* Generate the equivalent of :
+ unsigned int fenv_var;
+ fenv_var = __builtin_sh_get_fpscr ();
+
+ unsigned int masked_fenv;
+ masked_fenv = fenv_var & mask;
+
+ __builtin_sh_set_fpscr (masked_fenv); */
+
+ fenv_var = create_tmp_var_raw (unsigned_type_node);
+ mask = build_int_cst (unsigned_type_node,
+ ~((SH_FE_ALL_EXCEPT << SH_FE_EXCEPT_SHIFT)
+ | SH_FE_ALL_EXCEPT));
+ ld_fenv = build2 (MODIFY_EXPR, unsigned_type_node,
+ fenv_var, build_call_expr (sh_builtin_get_fpscr, 0));
+ masked_fenv = build2 (BIT_AND_EXPR, unsigned_type_node, fenv_var, mask);
+ hold_fnclex = build_call_expr (sh_builtin_set_fpscr, 1, masked_fenv);
+ fenv_var = build4 (TARGET_EXPR, unsigned_type_node, fenv_var,
+ build2 (COMPOUND_EXPR, void_type_node, masked_fenv,
+ ld_fenv),
+ NULL_TREE, NULL_TREE);
+ *hold = build2 (COMPOUND_EXPR, void_type_node, fenv_var, hold_fnclex);
+
+ /* Store the value of masked_fenv to clear the exceptions:
+ __builtin_sh_set_fpscr (masked_fenv); */
+
+ *clear = build_call_expr (sh_builtin_set_fpscr, 1, masked_fenv);
+
+ /* Generate the equivalent of :
+ unsigned int new_fenv_var;
+ new_fenv_var = __builtin_sh_get_fpscr ();
+
+ __builtin_sh_set_fpscr (fenv_var);
+
+ __atomic_feraiseexcept (new_fenv_var); */
+
+ new_fenv_var = create_tmp_var_raw (unsigned_type_node);
+ reload_fenv = build2 (MODIFY_EXPR, unsigned_type_node, new_fenv_var,
+ build_call_expr (sh_builtin_get_fpscr, 0));
+ restore_fnenv = build_call_expr (sh_builtin_set_fpscr, 1, fenv_var);
+ atomic_feraiseexcept = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
+ update_call = build_call_expr (atomic_feraiseexcept, 1,
+ fold_convert (integer_type_node,
+ new_fenv_var));
+ *update = build2 (COMPOUND_EXPR, void_type_node,
+ build2 (COMPOUND_EXPR, void_type_node,
+ reload_fenv, restore_fnenv), update_call);
+}
+
+/* Implements target hook vector_mode_supported_p. */
+bool
+sh_vector_mode_supported_p (machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return false;
+}
+
+bool
+sh_frame_pointer_required (void)
+{
+/* If needed override this in other tm.h files to cope with various OS
+ lossage requiring a frame pointer. */
+ if (SUBTARGET_FRAME_POINTER_REQUIRED)
+ return true;
+
+ if (crtl->profile)
+ return true;
+
+ return false;
+}
+
+/* Implements target hook dwarf_calling_convention. Return an enum
+ of dwarf_calling_convention. */
+int
+sh_dwarf_calling_convention (const_tree func)
+{
+ if (sh_attr_renesas_p (func))
+ return DW_CC_GNU_renesas_sh;
+
+ return DW_CC_normal;
+}
+
+/* Returns the sh builtin decl for CODE. */
+static tree
+sh_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= ARRAY_SIZE (bdesc))
+ return error_mark_node;
+
+ if (!bdesc[code].is_enabled ())
+ return error_mark_node;
+
+ return bdesc[code].fndecl;
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+static rtx
+sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+ machine_mode mode ATTRIBUTE_UNUSED, int ignore)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_MD_FUNCTION_CODE (fndecl);
+ const struct builtin_description *d = &bdesc[fcode];
+ enum insn_code icode = d->icode;
+ int signature = d->signature;
+ int nop = 0;
+ rtx op[4];
+
+ if (signature_args[signature][0])
+ {
+ if (ignore)
+ return NULL_RTX;
+
+ machine_mode tmode = insn_data[icode].operand[0].mode;
+ if (! target || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
+ op[nop++] = target;
+ }
+ else
+ target = NULL_RTX;
+
+ for (int i = 1; i <= 3; i++, nop++)
+ {
+ if (! signature_args[signature][i])
+ break;
+ tree arg = CALL_EXPR_ARG (exp, i - 1);
+ if (arg == error_mark_node)
+ return const0_rtx;
+
+ machine_mode opmode;
+ tree optype;
+ if (signature_args[signature][i] & 8)
+ {
+ opmode = ptr_mode;
+ optype = ptr_type_node;
+ }
+ else
+ {
+ opmode = insn_data[icode].operand[nop].mode;
+ optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
+ }
+
+ machine_mode argmode = TYPE_MODE (TREE_TYPE (arg));
+ if (argmode != opmode)
+ arg = build1 (NOP_EXPR, optype, arg);
+ op[nop] = expand_expr (arg, NULL_RTX, opmode, EXPAND_NORMAL);
+ if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
+ op[nop] = copy_to_mode_reg (opmode, op[nop]);
+ }
+
+ rtx pat = NULL_RTX;
+
+ switch (nop)
+ {
+ case 1:
+ pat = (*insn_data[d->icode].genfun) (op[0]);
+ break;
+ case 2:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
+ break;
+ case 3:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
+ break;
+ case 4:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (! pat)
+ return NULL_RTX;
+ emit_insn (pat);
+ return target;
+}
+
+/* Implement TARGET_HARD_REGNO_NREGS. On the SH all but the XD regs are
+ UNITS_PER_WORD bits wide. */
+
+static unsigned int
+sh_hard_regno_nregs (unsigned int regno, machine_mode mode)
+{
+ if (XD_REGISTER_P (regno))
+ return CEIL (GET_MODE_SIZE (mode), 2 * UNITS_PER_WORD);
+ return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK.
+
+ We can allow any mode in any general register. The special registers
+ only allow SImode. Don't allow any mode in the PR.
+
+ We cannot hold DCmode values in the XD registers because alter_reg
+ handles subregs of them incorrectly. We could work around this by
+ spacing the XD registers like the DR registers, but this would require
+ additional memory in every compilation to hold larger register vectors.
+ We could hold SFmode / SCmode values in XD registers, but that
+ would require a tertiary reload when reloading from / to memory,
+ and a secondary reload to reload from / to general regs; that
+ seems to be a losing proposition.
+
+ We want to allow TImode FP regs so that when V4SFmode is loaded as TImode,
+ it won't be ferried through GP registers first. */
+static bool
+sh_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
+{
+ if (SPECIAL_REGISTER_P (regno))
+ return mode == SImode;
+
+ if (regno == FPUL_REG)
+ return (mode == SImode || mode == SFmode);
+
+ if (FP_REGISTER_P (regno) && mode == SFmode)
+ return true;
+
+ if (mode == V2SFmode)
+ {
+ if (((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 2 == 0)
+ || GENERAL_REGISTER_P (regno)))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V4SFmode)
+ {
+ if ((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 4 == 0)
+ || GENERAL_REGISTER_P (regno))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V16SFmode)
+ return regno == FIRST_XD_REG;
+
+ if (FP_REGISTER_P (regno))
+ {
+ if (mode == SFmode
+ || mode == SImode
+ || ((TARGET_SH2E) && mode == SCmode)
+ || (((TARGET_FPU_DOUBLE && mode == DFmode) || mode == DCmode)
+ && ((regno - FIRST_FP_REG) & 1) == 0)
+ || (TARGET_SH4 && mode == TImode
+ && ((regno - FIRST_FP_REG) & 3) == 0))
+ return true;
+ else
+ return false;
+ }
+
+ if (XD_REGISTER_P (regno))
+ return mode == DFmode;
+
+ if (regno == PR_REG)
+ return mode == SImode;
+
+ if (regno == FPSCR_REG)
+ return mode == SImode;
+
+ return true;
+}
+
+/* Implement TARGET_MODES_TIEABLE_P.
+
+ If TARGET_HARD_REGNO_MODE_OK could produce different values for MODE1
+ and MODE2, for any hard reg, then this must be false for correct output.
+ That's the case for xd registers: we don't hold SFmode values in
+ them, so we can't tie an SFmode pseudos with one in another
+ floating-point mode. */
+
+static bool
+sh_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+ return (mode1 == mode2
+ || (GET_MODE_CLASS (mode1) == GET_MODE_CLASS (mode2)
+ && (mode1 != SFmode && mode2 != SFmode)));
+}
+
+/* Specify the modes required to caller save a given hard regno.
+ choose_hard_reg_mode chooses mode based on TARGET_HARD_REGNO_MODE_OK
+ and returns ?Imode for float regs when sh_hard_regno_mode_ok
+ permits integer modes on them. That makes LRA's split process
+ unhappy. See PR55212.
+ */
+machine_mode
+sh_hard_regno_caller_save_mode (unsigned int regno, unsigned int nregs,
+ machine_mode mode)
+{
+ if (FP_REGISTER_P (regno)
+ && (mode == SFmode
+ || mode == SCmode
+ || ((mode == DFmode || mode == DCmode)
+ && ((regno - FIRST_FP_REG) & 1) == 0)))
+ return mode;
+
+ return choose_hard_reg_mode (regno, nregs, NULL);
+}
+
+/* Implement TARGET_CAN_CHANGE_MODE_CLASS. */
+static bool
+sh_can_change_mode_class (machine_mode from, machine_mode to,
+ reg_class_t rclass)
+{
+ /* We want to enable the use of SUBREGs as a means to
+ VEC_SELECT a single element of a vector. */
+
+ /* This effectively disallows using GENERAL_REGS for SFmode vector subregs.
+ This can be problematic when SFmode vector subregs need to be accessed
+ on the stack with displacement addressing, as it happens with -O0.
+ Thus we disallow the mode change for -O0. */
+ if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
+ return optimize ? !reg_classes_intersect_p (GENERAL_REGS, rclass) : true;
+
+ if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
+ {
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
+ return !reg_classes_intersect_p (DF_REGS, rclass);
+ }
+ else
+ {
+ if (GET_MODE_SIZE (from) < 8)
+ return !reg_classes_intersect_p (DF_REGS, rclass);
+ }
+ }
+ return true;
+}
+
+/* Return true if registers in machine mode MODE will likely be
+ allocated to registers in small register classes. */
+bool
+sh_small_register_classes_for_mode_p (machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return true;
+}
+
+/* If ADDRESS refers to a CODE_LABEL, add NUSES to the number of times
+ that label is used. */
+void
+sh_mark_label (rtx address, int nuses)
+{
+ if (GOTOFF_P (address))
+ {
+ /* Extract the label or symbol. */
+ address = XEXP (address, 0);
+ if (GET_CODE (address) == PLUS)
+ address = XEXP (address, 0);
+ address = XVECEXP (address, 0, 0);
+ }
+ if (GET_CODE (address) == LABEL_REF
+ && LABEL_P (XEXP (address, 0)))
+ LABEL_NUSES (XEXP (address, 0)) += nuses;
+}
+
+/* Compute extra cost of moving data between one register class
+ and another.
+
+ If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
+ uses this information. Hence, the general register <-> floating point
+ register information here is not used for SFmode. */
+static int
+sh_register_move_cost (machine_mode mode,
+ reg_class_t srcclass, reg_class_t dstclass)
+{
+ if (dstclass == T_REGS || dstclass == PR_REGS)
+ return 10;
+
+ if (dstclass == MAC_REGS && srcclass == MAC_REGS)
+ return 4;
+
+ if (mode == SImode && TARGET_FMOVD
+ && REGCLASS_HAS_FP_REG (srcclass)
+ && REGCLASS_HAS_FP_REG (dstclass))
+ return 4;
+
+ if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
+ return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
+
+ if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
+ || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
+ return 9;
+
+ if ((REGCLASS_HAS_FP_REG (dstclass)
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (REGCLASS_HAS_GENERAL_REG (dstclass)
+ && REGCLASS_HAS_FP_REG (srcclass)))
+ {
+ /* Discourage trying to use fp regs for a pointer. This also
+ discourages fp regs with SImode because Pmode is an alias
+ of SImode on this target. See PR target/48596. */
+ int addend = (mode == Pmode) ? 40 : 0;
+
+ return ((TARGET_FMOVD ? 8 : 12) + addend)
+ * ((GET_MODE_SIZE (mode) + 7) / 8U);
+ }
+
+ if ((dstclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (srcclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (dstclass)))
+ return 5;
+
+ if ((dstclass == FPUL_REGS
+ && (srcclass == PR_REGS || srcclass == MAC_REGS || srcclass == T_REGS))
+ || (srcclass == FPUL_REGS
+ && (dstclass == PR_REGS || dstclass == MAC_REGS)))
+ return 7;
+
+ if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
+ || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
+ return 4;
+
+ if (TARGET_FMOVD
+ && ! REGCLASS_HAS_GENERAL_REG (srcclass)
+ && ! REGCLASS_HAS_GENERAL_REG (dstclass))
+ return 2 * ((GET_MODE_SIZE (mode) + 7) / 8U);
+
+ return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
+}
+
+static rtx
+emit_load_ptr (rtx reg, rtx addr)
+{
+ rtx mem = gen_const_mem (ptr_mode, addr);
+
+ if (Pmode != ptr_mode)
+ mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
+ return emit_move_insn (reg, mem);
+}
+
+static void
+sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+ tree function)
+{
+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
+ CUMULATIVE_ARGS cum;
+ int structure_value_byref = 0;
+ rtx this_rtx, this_value, sibcall, funexp;
+ rtx_insn *insns;
+ tree funtype = TREE_TYPE (function);
+ int simple_add = CONST_OK_FOR_ADD (delta);
+ int did_load = 0;
+ rtx scratch0, scratch1, scratch2;
+
+ reload_completed = 1;
+ epilogue_completed = 1;
+ crtl->uses_only_leaf_regs = 1;
+
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+ /* Find the "this" pointer. We have such a wide range of ABIs for the
+ SH that it's best to do this completely machine independently.
+ "this" is passed as first argument, unless a structure return pointer
+ comes first, in which case "this" comes second. */
+ INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1);
+#ifndef PCC_STATIC_STRUCT_RETURN
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ structure_value_byref = 1;
+#endif /* not PCC_STATIC_STRUCT_RETURN */
+ if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0)
+ {
+ tree ptype = build_pointer_type (TREE_TYPE (funtype));
+
+ function_arg_info ptr_arg (ptype, Pmode, /*named=*/true);
+ sh_function_arg_advance (pack_cumulative_args (&cum), ptr_arg);
+ }
+ function_arg_info ptr_arg (ptr_type_node, Pmode, /*named=*/true);
+ this_rtx = sh_function_arg (pack_cumulative_args (&cum), ptr_arg);
+
+ /* For SHcompact, we only have r0 for a scratch register: r1 is the
+ static chain pointer (even if you can't have nested virtual functions
+ right now, someone might implement them sometime), and the rest of the
+ registers are used for argument passing, are callee-saved, or reserved. */
+ /* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
+ -ffixed-reg has been used. */
+ if (! call_used_or_fixed_reg_p (0) || fixed_regs[0])
+ error ("r0 needs to be available as a call-clobbered register");
+ scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
+
+ {
+ if (call_used_or_fixed_reg_p (1) && ! fixed_regs[1])
+ scratch1 = gen_rtx_REG (ptr_mode, 1);
+ /* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
+ pointing where to return struct values. */
+ if (call_used_or_fixed_reg_p (3) && ! fixed_regs[3])
+ scratch2 = gen_rtx_REG (Pmode, 3);
+ }
+
+ this_value = plus_constant (Pmode, this_rtx, delta);
+ if (vcall_offset
+ && (simple_add || scratch0 != scratch1)
+ && strict_memory_address_p (ptr_mode, this_value))
+ {
+ emit_load_ptr (scratch0, this_value);
+ did_load = 1;
+ }
+
+ if (!delta)
+ ; /* Do nothing. */
+ else if (simple_add)
+ emit_move_insn (this_rtx, this_value);
+ else
+ {
+ emit_move_insn (scratch1, GEN_INT (delta));
+ emit_insn (gen_add2_insn (this_rtx, scratch1));
+ }
+
+ if (vcall_offset)
+ {
+ rtx offset_addr;
+
+ if (!did_load)
+ emit_load_ptr (scratch0, this_rtx);
+
+ offset_addr = plus_constant (Pmode, scratch0, vcall_offset);
+ if (strict_memory_address_p (ptr_mode, offset_addr))
+ ; /* Do nothing. */
+ else if (scratch0 != scratch1)
+ {
+ /* scratch0 != scratch1, and we have indexed loads. Get better
+ schedule by loading the offset into r1 and using an indexed
+ load - then the load of r1 can issue before the load from
+ (this_rtx + delta) finishes. */
+ emit_move_insn (scratch1, GEN_INT (vcall_offset));
+ offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
+ }
+ else if (CONST_OK_FOR_ADD (vcall_offset))
+ {
+ emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
+ offset_addr = scratch0;
+ }
+ else
+ gcc_unreachable (); /* FIXME */
+ emit_load_ptr (scratch0, offset_addr);
+
+ if (Pmode != ptr_mode)
+ scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0);
+ emit_insn (gen_add2_insn (this_rtx, scratch0));
+ }
+
+ /* Generate a tail call to the target function. */
+ if (! TREE_USED (function))
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ /* If the function is overridden, so is the thunk, hence we don't
+ need GOT addressing even if this is a public symbol. */
+#if 0
+ if (TARGET_SH1 && ! flag_weak)
+ sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
+ else
+#endif
+ if (TARGET_SH2 && flag_pic)
+ {
+ if (TARGET_FDPIC)
+ {
+ sibcall = gen_sibcall_pcrel_fdpic (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 3), 0) = scratch2;
+ }
+ else
+ {
+ sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+ }
+ }
+ else
+ {
+ emit_move_insn (scratch2, funexp);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
+ sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
+ }
+ sibcall = emit_call_insn (sibcall);
+ SIBLING_CALL_P (sibcall) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this_rtx);
+ emit_barrier ();
+
+ /* Run just enough of rest_of_compilation to do scheduling and get
+ the insns emitted. */
+
+ insns = get_insns ();
+
+ if (optimize > 0)
+ {
+ if (! cfun->cfg)
+ init_flow (cfun);
+ split_all_insns_noflow ();
+ }
+
+ sh_reorg ();
+ shorten_branches (insns);
+ assemble_start_function (thunk_fndecl, fnname);
+ final_start_function (insns, file, 1);
+ final (insns, file, 1);
+ final_end_function ();
+ assemble_end_function (thunk_fndecl, fnname);
+
+ reload_completed = 0;
+ epilogue_completed = 0;
+}
+
+/* Return an RTX pair for the address and call site label of a function
+ NAME of kind KIND, placing the result in TARGET if not NULL. For
+ SFUNC_STATIC, if FDPIC, the LAB member of result will be set to
+ (const_int 0) if jsr should be used, or a label_ref if bsrf should
+ be used. For FDPIC, both SFUNC_GOT and SFUNC_STATIC will return the
+ address of the function itself, not a function descriptor, so they
+ can only be used with functions not using the FDPIC register that
+ are known to be called directory without a PLT entry. */
+
+function_symbol_result
+function_symbol (rtx target, const char *name, sh_function_kind kind)
+{
+ /* If this is not an ordinary function, the name usually comes from a
+ string literal or an sprintf buffer. Make sure we use the same
+ string consistently, so that cse will be able to unify address loads. */
+ if (kind != FUNCTION_ORDINARY)
+ name = IDENTIFIER_POINTER (get_identifier (name));
+ rtx sym = gen_rtx_SYMBOL_REF (Pmode, name);
+ rtx lab = const0_rtx;
+ SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
+ if (flag_pic)
+ switch (kind)
+ {
+ case FUNCTION_ORDINARY:
+ break;
+ case SFUNC_GOT:
+ {
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, sym));
+ sym = reg;
+ break;
+ }
+ case SFUNC_STATIC:
+ {
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ if (TARGET_FDPIC)
+ {
+ /* We use PC-relative calls, since GOTOFF can only refer
+ to writable data. This works along with sh_sfunc_call. */
+ lab = PATTERN (gen_call_site ());
+ emit_insn (gen_sym_label2reg (reg, sym, lab));
+ }
+ else
+ {
+ /* ??? To allow cse to work, we use GOTOFF relocations.
+ we could add combiner patterns to transform this into
+ straight pc-relative calls with sym2PIC / bsrf when
+ label load and function call are still 1:1 and in the
+ same basic block during combine. */
+ emit_insn (gen_symGOTOFF2reg (reg, sym));
+ }
+
+ sym = reg;
+ break;
+ }
+ }
+ if (target && sym != target)
+ {
+ emit_move_insn (target, sym);
+ return function_symbol_result (target, lab);
+ }
+ return function_symbol_result (sym, lab);
+}
+
+/* Find the number of the first general purpose register in S that
+ is not set. */
+static int
+scavenge_reg (HARD_REG_SET *s)
+{
+ for (int r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
+ if (TEST_HARD_REG_BIT (*s, r))
+ return r;
+ return -1;
+}
+
+rtx
+sh_get_pr_initial_val (void)
+{
+ /* If we haven't finished rtl generation, there might be a nonlocal label
+ that we haven't seen yet.
+ ??? get_hard_reg_initial_val fails if it is called after register
+ allocation has started, unless it has been called before for the
+ same register. And even then, we end in trouble if we didn't use
+ the register in the same basic block before. So call
+ get_hard_reg_initial_val now and wrap it in an unspec if we might
+ need to replace it. */
+ /* ??? We also must do this for TARGET_SH1 in general, because otherwise
+ combine can put the pseudo returned by get_hard_reg_initial_val into
+ instructions that need a general purpose registers, which will fail to
+ be recognized when the pseudo becomes allocated to PR. */
+ rtx val = get_hard_reg_initial_val (Pmode, PR_REG);
+ return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
+}
+
+bool
+sh_expand_t_scc (rtx operands[])
+{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx target = operands[0];
+ rtx op0 = operands[2];
+ rtx op1 = operands[3];
+ rtx result = target;
+
+ if (!REG_P (op0) || REGNO (op0) != T_REG
+ || !CONST_INT_P (op1))
+ return false;
+ if (!REG_P (result))
+ result = gen_reg_rtx (SImode);
+ HOST_WIDE_INT val = INTVAL (op1);
+ if ((code == EQ && val == 1) || (code == NE && val == 0))
+ emit_insn (gen_movt (result, get_t_reg_rtx ()));
+ else if ((code == EQ && val == 0) || (code == NE && val == 1))
+ emit_insn (gen_movnegt (result, get_t_reg_rtx ()));
+ else if (code == EQ || code == NE)
+ emit_insn (gen_move_insn (result, GEN_INT (code == NE)));
+ else
+ return false;
+ if (result != target)
+ emit_move_insn (target, result);
+ return true;
+}
+
+/* INSN is an sfunc; return the rtx that describes the address used. */
+static rtx
+extract_sfunc_addr (rtx insn)
+{
+ rtx pattern = PATTERN (insn);
+ const int len = XVECLEN (pattern, 0);
+ for (int i = 0; i < len; i++)
+ {
+ rtx part = XVECEXP (pattern, 0, i);
+ if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode
+ && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
+ return XEXP (part, 0);
+ }
+ gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
+ return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
+}
+
+/* Verify that the register in use_sfunc_addr still agrees with the address
+ used in the sfunc. This prevents fill_slots_from_thread from changing
+ use_sfunc_addr.
+ INSN is the use_sfunc_addr instruction, and REG is the register it
+ guards. */
+bool
+check_use_sfunc_addr (rtx_insn *insn, rtx reg)
+{
+ /* Search for the sfunc. It should really come right after INSN. */
+ while ((insn = NEXT_INSN (insn)))
+ {
+ if (LABEL_P (insn) || JUMP_P (insn))
+ break;
+ if (! INSN_P (insn))
+ continue;
+
+ if (rtx_sequence *seq = dyn_cast<rtx_sequence *> (PATTERN (insn)))
+ insn = seq->insn (0);
+ if (GET_CODE (PATTERN (insn)) != PARALLEL
+ || get_attr_type (insn) != TYPE_SFUNC)
+ continue;
+ return rtx_equal_p (extract_sfunc_addr (insn), reg);
+ }
+ gcc_unreachable ();
+}
+
+/* This function returns a constant rtx that represents 2**15 / pi in
+ SFmode. It's used to scale a fixed-point signed 16.16-bit fraction
+ of a full circle back to an SFmode value, i.e. 0x10000 maps to 2*pi. */
+static GTY(()) rtx sh_fsca_sf2int_rtx;
+
+rtx
+sh_fsca_sf2int (void)
+{
+ if (! sh_fsca_sf2int_rtx)
+ {
+ REAL_VALUE_TYPE rv;
+
+ real_from_string (&rv, "10430.378350470453");
+ sh_fsca_sf2int_rtx = const_double_from_real_value (rv, SFmode);
+ }
+
+ return sh_fsca_sf2int_rtx;
+}
+
+/* This function returns a constant rtx that represents pi / 2**15 in
+ SFmode. It's used to scale SFmode angles, in radians, to a
+ fixed-point signed 16.16-bit fraction of a full circle, i.e. 2*pi
+ maps to 0x10000. */
+static GTY(()) rtx sh_fsca_int2sf_rtx;
+
+rtx
+sh_fsca_int2sf (void)
+{
+ if (! sh_fsca_int2sf_rtx)
+ {
+ REAL_VALUE_TYPE rv;
+
+ real_from_string (&rv, "9.587379924285257e-5");
+ sh_fsca_int2sf_rtx = const_double_from_real_value (rv, SFmode);
+ }
+
+ return sh_fsca_int2sf_rtx;
+}
+
+/* Initialize the CUMULATIVE_ARGS structure. */
+void
+sh_init_cumulative_args (CUMULATIVE_ARGS * pcum,
+ tree fntype,
+ rtx libname ATTRIBUTE_UNUSED,
+ tree fndecl,
+ signed int n_named_args,
+ machine_mode mode)
+{
+ pcum->arg_count [(int) SH_ARG_FLOAT] = 0;
+ pcum->free_single_fp_reg = 0;
+ pcum->outgoing = n_named_args != -1;
+
+ /* FIXME: Should we check TARGET_HITACHI here ??? */
+ pcum->renesas_abi = sh_attr_renesas_p (fntype);
+
+ if (fntype)
+ {
+ pcum->force_mem = ((TARGET_HITACHI || pcum->renesas_abi)
+ && aggregate_value_p (TREE_TYPE (fntype), fndecl));
+ pcum->prototype_p = prototype_p (fntype);
+ pcum->arg_count [(int) SH_ARG_INT] = false;
+ }
+ else
+ {
+ pcum->arg_count [(int) SH_ARG_INT] = 0;
+ pcum->prototype_p = false;
+ if (mode != VOIDmode)
+ {
+ /* If the default ABI is the Renesas ABI then all library
+ calls must assume that the library will be using the
+ Renesas ABI. So if the function would return its result
+ in memory then we must force the address of this memory
+ block onto the stack. Ideally we would like to call
+ targetm.calls.return_in_memory() here but we do not have
+ the TYPE or the FNDECL available so we synthesize the
+ contents of that function as best we can. */
+ pcum->force_mem =
+ (TARGET_DEFAULT & MASK_HITACHI)
+ && (mode == BLKmode
+ || (GET_MODE_SIZE (mode) > 4
+ && !(mode == DFmode
+ && TARGET_FPU_DOUBLE)));
+ }
+ else
+ pcum->force_mem = false;
+ }
+}
+
+rtx
+sh_gen_truncate (machine_mode mode, rtx x, int need_sign_ext)
+{
+ enum rtx_code code = TRUNCATE;
+
+ if (GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
+ {
+ rtx inner = XEXP (x, 0);
+ machine_mode inner_mode = GET_MODE (inner);
+
+ if (inner_mode == mode)
+ return inner;
+ else if (GET_MODE_SIZE (inner_mode) >= GET_MODE_SIZE (mode))
+ x = inner;
+ else if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)
+ && (! need_sign_ext || GET_CODE (x) == SIGN_EXTEND))
+ {
+ code = GET_CODE (x);
+ x = inner;
+ }
+ }
+ return gen_rtx_fmt_e (code, mode, x);
+}
+
+/* Load and store depend on the highpart of the address. However,
+ set_attr_alternative does not give well-defined results before reload,
+ so we must look at the rtl ourselves to see if any of the feeding
+ registers is used in a memref.
+
+ Return true iff INSN contains a MEM. */
+bool
+sh_contains_memref_p (rtx insn)
+{
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, PATTERN (insn), NONCONST)
+ if (MEM_P (*iter))
+ return true;
+ return false;
+}
+
+/* Return true iff INSN loads a banked register. */
+bool
+sh_loads_bankedreg_p (rtx insn)
+{
+ if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx op = SET_DEST (PATTERN(insn));
+ if (REG_P (op) && BANKED_REGISTER_P (REGNO (op)))
+ return true;
+ }
+
+ return false;
+}
+
+/* Implement TARGET_PREFERRED_RELOAD_CLASS. */
+static reg_class_t
+sh_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, reg_class_t rclass)
+{
+ return rclass;
+}
+
+/* Implement TARGET_SECONDARY_RELOAD. */
+static reg_class_t
+sh_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
+ machine_mode mode, secondary_reload_info *sri)
+{
+ enum reg_class rclass = (enum reg_class) rclass_i;
+
+ if (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS
+ && REG_P (XEXP (XEXP (x, 0), 0))
+ && REGNO (XEXP (XEXP (x, 0), 0)) == GBR_REG)
+ return rclass == R0_REGS ? NO_REGS : R0_REGS;
+
+ if (MEM_P (x) && REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) == GBR_REG)
+ return rclass == R0_REGS ? NO_REGS : R0_REGS;
+
+ if (REG_P (x) && REGNO (x) == GBR_REG)
+ return NO_REGS;
+
+ if (in_p)
+ {
+ if (REGCLASS_HAS_FP_REG (rclass)
+ && immediate_operand ((x), mode)
+ && ! ((fp_zero_operand (x) || fp_one_operand (x)) && mode == SFmode))
+ switch (mode)
+ {
+ case E_SFmode:
+ sri->icode = CODE_FOR_reload_insf__frn;
+ return NO_REGS;
+ case E_DFmode:
+ sri->icode = CODE_FOR_reload_indf__frn;
+ return NO_REGS;
+ case E_SImode:
+ /* ??? If we knew that we are in the appropriate mode -
+ single precision - we could use a reload pattern directly. */
+ return FPUL_REGS;
+ default:
+ abort ();
+ }
+ if (rclass == FPUL_REGS
+ && ((REG_P (x) && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG
+ || REGNO (x) == T_REG))
+ || GET_CODE (x) == PLUS))
+ return GENERAL_REGS;
+ if (rclass == FPUL_REGS && immediate_operand (x, mode))
+ {
+ if (satisfies_constraint_I08 (x) || fp_zero_operand (x))
+ return GENERAL_REGS;
+ else if (mode == SFmode)
+ return FP_REGS;
+ sri->icode = CODE_FOR_reload_insi__i_fpul;
+ return NO_REGS;
+ }
+ if (rclass == FPSCR_REGS
+ && ((REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ || (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS)))
+ return GENERAL_REGS;
+ } /* end of input-only processing. */
+
+ if (((REGCLASS_HAS_FP_REG (rclass)
+ && (REG_P (x)
+ && (GENERAL_OR_AP_REGISTER_P (REGNO (x))
+ || (FP_REGISTER_P (REGNO (x)) && mode == SImode
+ && TARGET_FMOVD))))
+ || (REGCLASS_HAS_GENERAL_REG (rclass)
+ && REG_P (x)
+ && FP_REGISTER_P (REGNO (x))))
+ && (mode == SFmode || mode == SImode))
+ return FPUL_REGS;
+ if ((rclass == FPUL_REGS
+ || (REGCLASS_HAS_FP_REG (rclass) && mode == SImode))
+ && (MEM_P (x)
+ || (REG_P (x)
+ && (REGNO (x) >= FIRST_PSEUDO_REGISTER
+ || REGNO (x) == T_REG
+ || system_reg_operand (x, VOIDmode)))))
+ {
+ if (rclass == FPUL_REGS)
+ return GENERAL_REGS;
+ return NO_REGS; // LRA wants NO_REGS here, it used to be FPUL_REGS;
+ }
+
+ if ((rclass == MAC_REGS || rclass == PR_REGS)
+ && REG_P (x) && ! GENERAL_REGISTER_P (REGNO (x))
+ && rclass != REGNO_REG_CLASS (REGNO (x)))
+ return GENERAL_REGS;
+
+ /* If here fall back to loading FPUL register through general registers.
+ This case can happen when movsi_ie insn is picked initially to
+ load/store the FPUL register from/to another register, and then the
+ other register is allocated on the stack. */
+ if (rclass == FPUL_REGS && true_regnum (x) == -1)
+ return GENERAL_REGS;
+
+ /* Force mov.b / mov.w displacement addressing insn to use R0 as
+ the other operand.
+ On SH2A could also just leave it alone here, which would result in a
+ 4 byte move insn being generated instead. However, for this to work
+ the insns must have the appropriate alternatives. */
+ if ((mode == QImode || mode == HImode) && rclass != R0_REGS
+ && satisfies_constraint_Sdd (x)
+ && sh_disp_addr_displacement (x)
+ <= sh_max_mov_insn_displacement (mode, false))
+ return R0_REGS;
+
+ /* When reload is trying to address a QImode or HImode subreg on the stack,
+ force any subreg byte into R0_REGS, as this is going to become a
+ displacement address.
+ We could restrict this to SUBREG_BYTE (x) > 0, but if the actual reg
+ is on the stack, the memref to it might already require a displacement
+ and that has to be added to the final address. At this point we don't
+ know the cumulative displacement so we assume the worst case. */
+ if ((mode == QImode || mode == HImode) && rclass != R0_REGS
+ && GET_CODE (x) == SUBREG && true_regnum (x) == -1)
+ return R0_REGS;
+
+ return NO_REGS;
+}
+
+/* Return true if SUBST can't safely replace its equivalent during RA. */
+static bool
+sh_cannot_substitute_mem_equiv_p (rtx)
+{
+ /* If SUBST is mem[base+index] or QI/HImode mem[base+disp], the insn
+ uses R0 and may cause spill failure when R0 is already used.
+ We have to return true for that case at least.
+ Moreover SH has strong R0 parity and also have not enough numbers of
+ the hard registers to make the equiv substitution win in the size
+ and the speed on average working sets. The pseudos produced to
+ hold the equiv values can't get good hard registers for bad cases
+ and end up memory save/restore insns which make the code worse. */
+ return true;
+}
+
+/* Implement TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT. */
+static bool
+sh_legitimize_address_displacement (rtx *offset1, rtx *offset2,
+ poly_int64 orig_offset,
+ machine_mode mode)
+{
+ if ((TARGET_FPU_DOUBLE && mode == DFmode)
+ || (TARGET_SH2E && mode == SFmode))
+ return false;
+
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode, orig_offset);
+ if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
+ {
+ *offset1 = adj.offset_adjust;
+ *offset2 = adj.mov_disp;
+ return true;
+ }
+
+ return false;
+}
+
+/* Return true if movsf insn should be splited with an additional
+ register. */
+bool
+sh_movsf_ie_ra_split_p (rtx op0, rtx op1, rtx op2)
+{
+ /* op0 == op1 */
+ if (rtx_equal_p (op0, op1))
+ return true;
+ /* fy, FQ, reg */
+ if (GET_CODE (op1) == CONST_DOUBLE
+ && ! satisfies_constraint_G (op1)
+ && ! satisfies_constraint_H (op1)
+ && REG_P (op0)
+ && REG_P (op2))
+ return true;
+ /* f, r, y */
+ if (REG_P (op0) && FP_REGISTER_P (REGNO (op0))
+ && REG_P (op1) && GENERAL_REGISTER_P (REGNO (op1))
+ && REG_P (op2) && (REGNO (op2) == FPUL_REG))
+ return true;
+ /* r, f, y */
+ if (REG_P (op1) && FP_REGISTER_P (REGNO (op1))
+ && REG_P (op0) && GENERAL_REGISTER_P (REGNO (op0))
+ && REG_P (op2) && (REGNO (op2) == FPUL_REG))
+ return true;
+
+ return false;
+}
+
+static void
+sh_conditional_register_usage (void)
+{
+ for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++)
+ if (! VALID_REGISTER_P (regno))
+ fixed_regs[regno] = 1;
+ /* R8 and R9 are call-clobbered on SH5, but not on earlier SH ABIs. */
+ if (flag_pic)
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+ if (TARGET_FDPIC)
+ {
+ fixed_regs[PIC_REG] = 1;
+ call_used_regs[PIC_REG] = 1;
+ }
+ /* Renesas saves and restores mac registers on call. */
+ if (TARGET_HITACHI && ! TARGET_NOMACSAVE)
+ {
+ call_used_regs[MACH_REG] = 0;
+ call_used_regs[MACL_REG] = 0;
+ }
+
+ for (int regno = FIRST_GENERAL_REG; regno <= LAST_GENERAL_REG; regno++)
+ if (! fixed_regs[regno] && call_used_regs[regno])
+ SET_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], regno);
+
+ call_used_regs[FPSCR_MODES_REG] = 0;
+ call_used_regs[FPSCR_STAT_REG] = 0;
+}
+
+/* Implement TARGET_LEGITIMATE_CONSTANT_P
+
+ can_store_by_pieces constructs VOIDmode CONST_DOUBLEs. */
+static bool
+sh_legitimate_constant_p (machine_mode mode, rtx x)
+{
+ if (SH_OFFSETS_MUST_BE_WITHIN_SECTIONS_P)
+ {
+ rtx base, offset;
+ split_const (x, &base, &offset);
+
+ if (GET_CODE (base) == SYMBOL_REF
+ && !offset_within_block_p (base, INTVAL (offset)))
+ return false;
+ }
+
+ if (TARGET_FDPIC
+ && (SYMBOLIC_CONST_P (x)
+ || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
+ && SYMBOLIC_CONST_P (XEXP (XEXP (x, 0), 0)))))
+ return false;
+
+ return GET_CODE (x) != CONST_DOUBLE
+ || mode == DFmode || mode == SFmode
+ || mode == DImode || GET_MODE (x) == VOIDmode;
+}
+
+enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
+
+static void
+sh_init_sync_libfuncs (void)
+{
+ init_sync_libfuncs (UNITS_PER_WORD);
+}
+
+/* Return true if it is appropriate to emit `ret' instructions in the
+ body of a function. */
+bool
+sh_can_use_simple_return_p (void)
+{
+ if (! reload_completed || frame_pointer_needed)
+ return false;
+
+ /* Moving prologue around does't reduce the size. */
+ if (optimize_function_for_size_p (cfun))
+ return false;
+
+ /* Finally, allow for pr save. */
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
+
+ if (rounded_frame_size (d) > 4)
+ return false;
+
+ return true;
+}
+
+/*------------------------------------------------------------------------------
+ Address mode optimization support code
+*/
+
+typedef HOST_WIDE_INT disp_t;
+static const disp_t MIN_DISP = HOST_WIDE_INT_MIN;
+static const disp_t MAX_DISP = HOST_WIDE_INT_MAX;
+static const disp_t INVALID_DISP = MAX_DISP;
+
+/* A memory reference which is described by a base register and a
+ displacement. */
+class base_reg_disp
+{
+public:
+ base_reg_disp (rtx br, disp_t d);
+
+ bool is_reg (void) const;
+ bool is_disp (void) const;
+ rtx reg (void) const;
+ disp_t disp (void) const;
+
+private:
+ rtx reg_;
+ disp_t disp_;
+};
+
+inline
+base_reg_disp::base_reg_disp (rtx br, disp_t d)
+: reg_ (br), disp_ (d)
+{
+}
+
+inline bool
+base_reg_disp::is_reg (void) const
+{
+ return reg_ != NULL_RTX && disp_ != INVALID_DISP;
+}
+
+inline bool
+base_reg_disp::is_disp (void) const
+{
+ return reg_ == NULL_RTX && disp_ != INVALID_DISP;
+}
+
+inline rtx
+base_reg_disp::reg (void) const
+{
+ return reg_;
+}
+
+inline disp_t
+base_reg_disp::disp (void) const
+{
+ return disp_;
+}
+
+/* Find the base register and calculate the displacement for a given
+ address rtx 'x'. */
+static base_reg_disp
+sh_find_base_reg_disp (rtx_insn* insn, rtx x, disp_t disp = 0,
+ rtx base_reg = NULL)
+{
+ if (REG_P (x))
+ {
+ if (REGNO (x) == GBR_REG)
+ return base_reg_disp (x, disp);
+
+ /* We've reached a hard-reg. This is probably the point where
+ function args are copied to pseudos. Do not go any further and
+ stick to the pseudo. If the original mem addr was in a hard reg
+ from the beginning, it will become the base reg. */
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+ return base_reg_disp (base_reg != NULL ? base_reg : x, disp);
+
+ /* Find the def of the reg and trace it. If there are more than one
+ defs and they are not the same, assume it's not safe to proceed. */
+ rtx_insn* last_i = NULL;
+ rtx last_set = NULL;
+ for (df_ref d = DF_REG_DEF_CHAIN (REGNO (x)); d != NULL;
+ d = DF_REF_NEXT_REG (d))
+ {
+ rtx set = const_cast<rtx> (set_of (x, DF_REF_INSN (d)));
+
+ /* Accept multiple defs, as long as they are equal. */
+ if (last_set == NULL || rtx_equal_p (last_set, set))
+ {
+ last_i = DF_REF_INSN (d);
+ last_set = set;
+ }
+ else
+ {
+ last_i = NULL;
+ last_set = NULL;
+ break;
+ }
+ }
+
+ if (last_set != NULL && last_i != NULL)
+ return sh_find_base_reg_disp (last_i, XEXP (last_set, 1), disp,
+ XEXP (last_set, 0));
+
+ /* When here, no previous insn was found that sets the reg.
+ The input reg is already the base reg. */
+ return base_reg_disp (x, disp);
+ }
+
+ else if (GET_CODE (x) == PLUS)
+ {
+ base_reg_disp left_val = sh_find_base_reg_disp (insn, XEXP (x, 0));
+ base_reg_disp right_val = sh_find_base_reg_disp (insn, XEXP (x, 1));
+
+ /* Either left or right val must be a reg.
+ We don't handle the case of 'reg + reg' here. */
+ if (left_val.is_reg () && right_val.is_disp ())
+ return base_reg_disp (left_val.reg (), left_val.disp ()
+ + right_val.disp () + disp);
+ else if (right_val.is_reg () && left_val.is_disp ())
+ return base_reg_disp (right_val.reg (), right_val.disp ()
+ + left_val.disp () + disp);
+ else
+ return base_reg_disp (base_reg, disp);
+ }
+
+ else if (CONST_INT_P (x))
+ return base_reg_disp (NULL, disp + INTVAL (x));
+
+ /* Didn't find anything useful. */
+ return base_reg_disp (base_reg, disp);
+}
+
+/* Given an insn and a memory operand, try to find an equivalent GBR
+ based memory address and return the corresponding new memory address.
+ Return NULL_RTX if not found. */
+rtx
+sh_find_equiv_gbr_addr (rtx_insn* insn, rtx mem)
+{
+ if (!MEM_P (mem) || gbr_address_mem (mem, GET_MODE (mem)))
+ return NULL_RTX;
+
+ /* Leave post/pre inc/dec or any other side effect addresses alone. */
+ if (side_effects_p (XEXP (mem, 0)))
+ return NULL_RTX;
+
+ /* When not optimizing there might be no dataflow available. */
+ if (df == NULL)
+ return NULL_RTX;
+
+ base_reg_disp gbr_disp = sh_find_base_reg_disp (insn, XEXP (mem, 0));
+
+ if (gbr_disp.is_reg () && REGNO (gbr_disp.reg ()) == GBR_REG)
+ {
+ /* If GBR is marked as call clobbered we bail out if we see a call.
+ FIXME: Actually should check if this mem refers to the gbr value
+ before or after the call. If there is a store_gbr preceeding this
+ mem, it's safe to use GBR for this mem.
+
+ If GBR is not marked as call clobbered, but there is some other
+ def than a call, it's probably a load_gbr upon which we also
+ bail out to be on the safe side.
+ FIXME: Should check if we have a use-after-def case, such as
+ the call case above. */
+ for (df_ref d = DF_REG_DEF_CHAIN (GBR_REG); d != NULL;
+ d = DF_REF_NEXT_REG (d))
+ {
+ if (CALL_P (DF_REF_INSN (d)))
+ {
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, GBR_REG))
+ return NULL_RTX;
+ else
+ continue;
+ }
+ else
+ return NULL_RTX;
+ }
+
+ rtx disp = GEN_INT (gbr_disp.disp ());
+ if (gbr_displacement (disp, GET_MODE (mem)))
+ return gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, GBR_REG), disp);
+ }
+
+ return NULL_RTX;
+}
+
+/*------------------------------------------------------------------------------
+ Manual insn combine support code.
+*/
+
+/* Return true if the specified insn contains any UNSPECs or
+ UNSPEC_VOLATILEs. */
+static bool
+sh_unspec_insn_p (rtx x)
+{
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (i, array, x, ALL)
+ if (*i != NULL
+ && (GET_CODE (*i) == UNSPEC || GET_CODE (*i) == UNSPEC_VOLATILE))
+ return true;
+
+ return false;
+}
+
+/* Return true if the register operands of the specified insn are modified
+ between the specified from and to insns (exclusive of those two). */
+bool
+sh_insn_operands_modified_between_p (rtx_insn* operands_insn,
+ const rtx_insn* from,
+ const rtx_insn* to)
+{
+ /* FIXME: Return true for multiple sets for now. */
+ rtx s = single_set (operands_insn);
+ if (s == NULL_RTX)
+ return true;
+
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (i, array, SET_SRC (s), ALL)
+ if (*i != NULL &&
+ ((REG_P (*i) || SUBREG_P (*i)) && reg_set_between_p (*i, from, to)))
+ return true;
+
+ return false;
+}
+
+/* Given an insn, determine whether it's a 'nott' insn, i.e. an insn that
+ negates the T bit and stores the result in the T bit. */
+bool
+sh_is_nott_insn (const rtx_insn* i)
+{
+ return i != NULL && GET_CODE (PATTERN (i)) == SET
+ && t_reg_operand (XEXP (PATTERN (i), 0), VOIDmode)
+ && negt_reg_operand (XEXP (PATTERN (i), 1), VOIDmode);
+}
+
+rtx
+sh_movt_set_dest (const rtx_insn* i)
+{
+ return i == NULL ? NULL : sh_movt_set_dest (PATTERN (i));
+}
+
+rtx
+sh_movt_set_dest (const_rtx pat)
+{
+ return GET_CODE (pat) == SET
+ && arith_reg_dest (XEXP (pat, 0), SImode)
+ && t_reg_operand (XEXP (pat, 1), VOIDmode) ? XEXP (pat, 0) : NULL;
+}
+
+/* Given an insn, check whether it's a 'movrt' kind of insn, i.e. an insn
+ that stores the negated T bit in a register, and return the destination
+ register rtx, or null. */
+rtx
+sh_movrt_set_dest (const rtx_insn* i)
+{
+ return i == NULL ? NULL : sh_movrt_set_dest (PATTERN (i));
+}
+
+rtx
+sh_movrt_set_dest (const_rtx pat)
+{
+ /* The negc movrt replacement is inside a parallel. */
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+
+ return GET_CODE (pat) == SET
+ && arith_reg_dest (XEXP (pat, 0), SImode)
+ && negt_reg_operand (XEXP (pat, 1), VOIDmode) ? XEXP (pat, 0) : NULL;
+
+}
+
+/* Given an insn and a reg number, tell whether the reg dies or is unused
+ after the insn. */
+bool
+sh_reg_dead_or_unused_after_insn (const rtx_insn* i, int regno)
+{
+ return find_regno_note (i, REG_DEAD, regno) != NULL
+ || find_regno_note (i, REG_UNUSED, regno) != NULL;
+}
+
+/* Given an insn and a reg number, remove reg dead or reg unused notes to
+ mark it as being used after the insn. */
+void
+sh_remove_reg_dead_or_unused_notes (rtx_insn* i, int regno)
+{
+ if (rtx n = find_regno_note (i, REG_DEAD, regno))
+ remove_note (i, n);
+ if (rtx n = find_regno_note (i, REG_UNUSED, regno))
+ remove_note (i, n);
+}
+
+/* Given an insn check if it contains any post/pre inc/dec mem operands and
+ add the REG_INC notes accordingly.
+ FIXME: This function is very similar to lra.c (add_auto_inc_notes).
+ FIXME: This function is currently used by peephole2 patterns because
+ the peephole2 pass does not preserve REG_INC notes. If the notes
+ are dropped the following passes will do wrong things. */
+rtx_insn*
+sh_check_add_incdec_notes (rtx_insn* i)
+{
+ struct for_each_inc_dec_clb
+ {
+ static int func (rtx mem ATTRIBUTE_UNUSED, rtx op ATTRIBUTE_UNUSED,
+ rtx dest, rtx src ATTRIBUTE_UNUSED,
+ rtx srcoff ATTRIBUTE_UNUSED, void* arg)
+ {
+ gcc_assert (REG_P (dest));
+
+ rtx_insn* i = (rtx_insn*)arg;
+ if (find_regno_note (i, REG_INC, REGNO (dest)) == NULL)
+ add_reg_note (i, REG_INC, dest);
+
+ return 0;
+ }
+ };
+
+ for_each_inc_dec (PATTERN (i), for_each_inc_dec_clb::func, i);
+ return i;
+}
+
+/* Given a move insn destiation and a source, make sure that the move source
+ operand is not a post-inc mem load with the same address reg as the
+ destination. Returns the modified source operand with the post-inc removed
+ if necessary. */
+rtx
+sh_remove_overlapping_post_inc (rtx dst, rtx src)
+{
+ if (!MEM_P (src))
+ return src;
+
+ rtx addr = XEXP (src, 0);
+
+ if (GET_CODE (addr) == POST_INC
+ && reg_overlap_mentioned_p (XEXP (addr, 0), dst))
+ return replace_equiv_address (src, XEXP (addr, 0));
+
+ gcc_assert (GET_CODE (addr) != POST_MODIFY);
+ return src;
+}
+
+/* Emit a move insn that is safe to be used in peephole patterns. */
+rtx_insn*
+sh_peephole_emit_move_insn (rtx dst, rtx src)
+{
+ return sh_check_add_incdec_notes (
+ emit_move_insn (dst, sh_remove_overlapping_post_inc (dst, src)));
+}
+
+/* Given an op rtx and an insn, try to find out whether the result of the
+ specified op consists only of logical operations on T bit stores. */
+bool
+sh_is_logical_t_store_expr (rtx op, rtx_insn* insn)
+{
+ if (!logical_operator (op, SImode))
+ return false;
+
+ rtx ops[2] = { XEXP (op, 0), XEXP (op, 1) };
+ int op_is_t_count = 0;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if (t_reg_operand (ops[i], VOIDmode)
+ || negt_reg_operand (ops[i], VOIDmode))
+ op_is_t_count++;
+
+ else
+ {
+ set_of_reg op_set = sh_find_set_of_reg
+ (ops[i], insn, prev_nonnote_nondebug_insn_bb);
+ if (op_set.set_src == NULL_RTX)
+ continue;
+
+ if (t_reg_operand (op_set.set_src, VOIDmode)
+ || negt_reg_operand (op_set.set_src, VOIDmode)
+ || sh_is_logical_t_store_expr (op_set.set_src, op_set.insn))
+ op_is_t_count++;
+ }
+ }
+
+ return op_is_t_count == 2;
+}
+
+/* Given the operand that is extended in a sign/zero extend insn, and the
+ insn, try to figure out whether the sign/zero extension can be replaced
+ by a simple reg-reg copy. If so, the replacement reg rtx is returned,
+ NULL_RTX otherwise. */
+rtx
+sh_try_omit_signzero_extend (rtx extended_op, rtx_insn* insn)
+{
+ if (REG_P (extended_op))
+ extended_op = extended_op;
+ else if (GET_CODE (extended_op) == SUBREG && REG_P (SUBREG_REG (extended_op)))
+ extended_op = SUBREG_REG (extended_op);
+ else
+ return NULL_RTX;
+
+ /* Reg moves must be of the same mode. */
+ if (GET_MODE (extended_op) != SImode)
+ return NULL_RTX;
+
+ set_of_reg s = sh_find_set_of_reg (extended_op, insn,
+ prev_nonnote_nondebug_insn_bb);
+ if (s.set_src == NULL_RTX)
+ return NULL_RTX;
+
+ if (t_reg_operand (s.set_src, VOIDmode)
+ || negt_reg_operand (s.set_src, VOIDmode))
+ return extended_op;
+
+ /* If the zero extended reg was formed by a logical operation, check the
+ operands of the logical operation. If both originated from T bit
+ stores the zero extension can be eliminated. */
+ else if (sh_is_logical_t_store_expr (s.set_src, s.insn))
+ return extended_op;
+
+ return NULL_RTX;
+}
+
+/* Given the current insn, which is assumed to be a movrt_negc insn, try to
+ figure out whether it should be converted into a movt-xor sequence in
+ the movrt_negc splitter.
+ Returns true if insns have been modified and the splitter has succeeded. */
+bool
+sh_split_movrt_negc_to_movt_xor (rtx_insn* curr_insn, rtx operands[])
+{
+ /* In cases such as
+ tst r4,r4
+ mov #-1,r1
+ negc r1,r1
+ tst r4,r4
+ we can replace the T bit clobbering negc with a movt-xor sequence and
+ eliminate the redundant comparison.
+ Because the xor insn depends on register allocation results, allow this
+ only before reload. */
+ if (!can_create_pseudo_p ())
+ return false;
+
+ set_of_reg t_before_negc = sh_find_set_of_reg
+ (get_t_reg_rtx (), curr_insn, prev_nonnote_nondebug_insn_bb);
+ set_of_reg t_after_negc = sh_find_set_of_reg
+ (get_t_reg_rtx (), curr_insn, next_nonnote_nondebug_insn_bb);
+
+ if (t_before_negc.set_rtx != NULL_RTX && t_after_negc.set_rtx != NULL_RTX
+ && rtx_equal_p (t_before_negc.set_rtx, t_after_negc.set_rtx)
+ && !reg_used_between_p (get_t_reg_rtx (), curr_insn, t_after_negc.insn)
+ && !sh_insn_operands_modified_between_p (t_before_negc.insn,
+ t_before_negc.insn,
+ t_after_negc.insn)
+ && !modified_between_p (get_t_reg_rtx (), curr_insn, t_after_negc.insn)
+ && !sh_unspec_insn_p (t_after_negc.insn)
+ && !volatile_insn_p (PATTERN (t_after_negc.insn))
+ && !side_effects_p (PATTERN (t_after_negc.insn))
+ && !may_trap_or_fault_p (PATTERN (t_after_negc.insn)))
+ {
+ emit_insn (gen_movrt_xor (operands[0], get_t_reg_rtx ()));
+ set_insn_deleted (t_after_negc.insn);
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Given a reg and the current insn, see if the value of the reg originated
+ from a sign or zero extension and return the discovered information. */
+sh_extending_set_of_reg
+sh_find_extending_set_of_reg (rtx reg, rtx_insn* curr_insn)
+{
+ if (reg == NULL)
+ return sh_extending_set_of_reg (curr_insn);
+
+ if (SUBREG_P (reg))
+ reg = SUBREG_REG (reg);
+
+ if (!REG_P (reg))
+ return sh_extending_set_of_reg (curr_insn);
+
+ /* FIXME: Also search the predecessor basic blocks. It seems that checking
+ only the adjacent predecessor blocks would cover most of the cases.
+ Also try to look through the first extension that we hit. There are some
+ cases, where a zero_extend is followed an (implicit) sign_extend, and it
+ fails to see the sign_extend. */
+ sh_extending_set_of_reg result = sh_find_set_of_reg
+ (reg, curr_insn, prev_nonnote_nondebug_insn_bb, true);
+
+ if (result.set_src != NULL)
+ {
+ if (GET_CODE (result.set_src) == SIGN_EXTEND
+ || GET_CODE (result.set_src) == ZERO_EXTEND)
+ {
+ if (dump_file)
+ fprintf (dump_file, "sh_find_extending_set_of_reg: reg %d is "
+ "explicitly sign/zero extended in insn %d\n",
+ REGNO (reg), INSN_UID (result.insn));
+ result.from_mode = GET_MODE (XEXP (result.set_src, 0));
+ result.ext_code = GET_CODE (result.set_src);
+ }
+ else if (MEM_P (result.set_src)
+ && (GET_MODE (result.set_src) == QImode
+ || GET_MODE (result.set_src) == HImode)
+ && !sh_unspec_insn_p (result.insn))
+ {
+ /* On SH QIHImode memory loads always sign extend. However, in
+ some cases where it seems that the higher bits are not
+ interesting, the loads will not be expanded as sign extending
+ insns, but as QIHImode loads into QIHImode regs. We report that
+ the reg has been sign extended by the mem load. When it is used
+ as such, we must convert the mem load into a sign extending insn,
+ see also sh_extending_set_of_reg::use_as_extended_reg. */
+ if (dump_file)
+ fprintf (dump_file, "sh_find_extending_set_of_reg: reg %d is "
+ "implicitly sign extended in insn %d\n",
+ REGNO (reg), INSN_UID (result.insn));
+ result.from_mode = GET_MODE (result.set_src);
+ result.ext_code = SIGN_EXTEND;
+ }
+ }
+
+ return result;
+}
+
+/* Given a reg that is known to be sign or zero extended at some insn,
+ take the appropriate measures so that the extended value can be used as
+ a reg at the specified insn and return the resulting reg rtx. */
+rtx
+sh_extending_set_of_reg::use_as_extended_reg (rtx_insn* use_at_insn) const
+{
+ gcc_assert (insn != NULL && set_src != NULL && set_rtx != NULL);
+ gcc_assert (ext_code == SIGN_EXTEND || ext_code == ZERO_EXTEND);
+ gcc_assert (from_mode == QImode || from_mode == HImode);
+
+ if (MEM_P (set_src) && ext_code == SIGN_EXTEND)
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "use_as_extended_reg: converting non-extending mem load in "
+ "insn %d into sign-extending load\n", INSN_UID (insn));
+
+ rtx r = gen_reg_rtx (SImode);
+ rtx_insn* i0;
+ if (from_mode == QImode)
+ i0 = sh_check_add_incdec_notes (
+ emit_insn_after (gen_extendqisi2 (r, set_src), insn));
+ else if (from_mode == HImode)
+ i0 = sh_check_add_incdec_notes (
+ emit_insn_after (gen_extendhisi2 (r, set_src), insn));
+ else
+ gcc_unreachable ();
+
+ emit_insn_after (
+ gen_move_insn (XEXP (set_rtx, 0),
+ gen_lowpart (GET_MODE (set_src), r)), i0);
+ set_insn_deleted (insn);
+ return r;
+ }
+ else
+ {
+ rtx extension_dst = XEXP (set_rtx, 0);
+ if (GET_MODE (extension_dst) != SImode)
+ extension_dst = simplify_gen_subreg (SImode, extension_dst,
+ GET_MODE (extension_dst), 0);
+ if (modified_between_p (extension_dst, insn, use_at_insn))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "use_as_extended_reg: dest reg %d of extending insn %d is "
+ "modified, inserting a reg-reg copy\n",
+ REGNO (extension_dst), INSN_UID (insn));
+
+ rtx r = gen_reg_rtx (SImode);
+ emit_insn_after (gen_move_insn (r, extension_dst), insn);
+ return r;
+ }
+ else
+ {
+ sh_remove_reg_dead_or_unused_notes (insn, REGNO (extension_dst));
+ return extension_dst;
+ }
+ }
+}
+
+bool
+sh_extending_set_of_reg::can_use_as_unextended_reg (void) const
+{
+ if ((ext_code == SIGN_EXTEND || ext_code == ZERO_EXTEND)
+ && (from_mode == QImode || from_mode == HImode)
+ && set_src != NULL)
+ return arith_reg_operand (XEXP (set_src, 0), from_mode);
+ else
+ return false;
+}
+
+rtx
+sh_extending_set_of_reg::use_as_unextended_reg (rtx_insn* use_at_insn) const
+{
+ gcc_assert (can_use_as_unextended_reg ());
+
+ rtx r = XEXP (set_src, 0);
+ rtx r0 = simplify_gen_subreg (SImode, r, from_mode, 0);
+
+ if (modified_between_p (r, insn, use_at_insn))
+ {
+ rtx r1 = gen_reg_rtx (SImode);
+ emit_insn_after (gen_move_insn (r1, r0), insn);
+ return r1;
+ }
+ else
+ {
+ sh_remove_reg_dead_or_unused_notes (insn, SUBREG_P (r)
+ ? REGNO (SUBREG_REG (r))
+ : REGNO (r));
+ return r0;
+ }
+}
+
+/* Given the current insn, which is assumed to be the *tst<mode>_t_subregs insn,
+ perform the necessary checks on the operands and split it accordingly. */
+void
+sh_split_tst_subregs (rtx_insn* curr_insn, machine_mode subreg_mode,
+ int subreg_offset, rtx operands[])
+{
+ gcc_assert (subreg_mode == QImode || subreg_mode == HImode);
+
+ sh_extending_set_of_reg eop0 = sh_find_extending_set_of_reg (operands[0],
+ curr_insn);
+ sh_extending_set_of_reg eop1 = sh_find_extending_set_of_reg (operands[1],
+ curr_insn);
+
+ /* If one of the operands is known to be zero extended, that's already
+ sufficient to mask out the unwanted high bits. */
+ if (eop0.ext_code == ZERO_EXTEND && eop0.from_mode == subreg_mode)
+ {
+ emit_insn (gen_tstsi_t (eop0.use_as_extended_reg (curr_insn),
+ operands[1]));
+ return;
+ }
+ if (eop1.ext_code == ZERO_EXTEND && eop1.from_mode == subreg_mode)
+ {
+ emit_insn (gen_tstsi_t (operands[0],
+ eop1.use_as_extended_reg (curr_insn)));
+ return;
+ }
+
+ /* None of the operands seem to be zero extended.
+ If both are sign extended it's OK, too. */
+ if (eop0.ext_code == SIGN_EXTEND && eop1.ext_code == SIGN_EXTEND
+ && eop0.from_mode == subreg_mode && eop1.from_mode == subreg_mode)
+ {
+ emit_insn (gen_tstsi_t (eop0.use_as_extended_reg (curr_insn),
+ eop1.use_as_extended_reg (curr_insn)));
+ return;
+ }
+
+ /* Otherwise we have to insert a zero extension on one of the operands to
+ mask out the unwanted high bits.
+ Prefer the operand that has no known extension. */
+ if (eop0.ext_code != UNKNOWN && eop1.ext_code == UNKNOWN)
+ std::swap (operands[0], operands[1]);
+
+ rtx tmp0 = gen_reg_rtx (SImode);
+ rtx tmp1 = simplify_gen_subreg (subreg_mode, operands[0],
+ GET_MODE (operands[0]), subreg_offset);
+ emit_insn (subreg_mode == QImode
+ ? gen_zero_extendqisi2 (tmp0, tmp1)
+ : gen_zero_extendhisi2 (tmp0, tmp1));
+ emit_insn (gen_tstsi_t (tmp0, operands[1]));
+}
+
+/* A helper class to increment/decrement a counter variable each time a
+ function is entered/left. */
+class scope_counter
+{
+public:
+ scope_counter (int& counter) : m_counter (counter) { ++m_counter; }
+
+ ~scope_counter (void)
+ {
+ --m_counter;
+ gcc_assert (m_counter >= 0);
+ }
+
+ int count (void) const { return m_counter; }
+
+private:
+ int& m_counter;
+};
+
+/* Given an rtx x, determine whether the expression can be used to create
+ an insn that calulates x and stores the result in the T bit.
+ This is used by the 'treg_set_expr' predicate to construct insns sequences
+ where T bit results are fed into other insns, such as addc, subc, negc
+ insns.
+
+ FIXME: The patterns that expand 'treg_set_expr' operands tend to
+ distinguish between 'positive' and 'negative' forms. For now this has to
+ be done in the preparation code. We could also introduce
+ 'pos_treg_set_expr' and 'neg_treg_set_expr' predicates for that and write
+ two different patterns for the 'postive' and 'negative' forms. However,
+ the total amount of lines of code seems to be about the same and the
+ '{pos|neg}_treg_set_expr' predicates would be more expensive, because the
+ recog function would need to look inside the expression by temporarily
+ splitting it. */
+static int sh_recog_treg_set_expr_reent_count = 0;
+
+bool
+sh_recog_treg_set_expr (rtx op, machine_mode mode)
+{
+ scope_counter recursion (sh_recog_treg_set_expr_reent_count);
+
+ /* Limit the recursion count to avoid nested expressions which we can't
+ resolve to a single treg set insn. */
+ if (recursion.count () > 1)
+ return false;
+
+ /* Early accept known possible operands before doing recog. */
+ if (op == const0_rtx || op == const1_rtx || t_reg_operand (op, mode)
+ || negt_reg_operand (op, mode))
+ return true;
+
+ /* Early reject impossible operands before doing recog.
+ There are some (set ((t) (subreg ...))) patterns, but we must be careful
+ not to allow any invalid reg-reg or mem-reg moves, or else other passes
+ such as lower-subreg will bail out. Some insns such as SH4A movua are
+ done with UNSPEC, so must reject those, too, or else it would result
+ in an invalid reg -> treg move. */
+ if (CONST_INT_P (op) || register_operand (op, mode)
+ || memory_operand (op, mode) || sh_unspec_insn_p (op))
+ return false;
+
+ if (!can_create_pseudo_p ())
+ return false;
+
+ /* expand_debug_locations may call this to compute rtx costs at
+ very early stage. In that case, don't make new insns here to
+ avoid codegen differences with -g. */
+ if (currently_expanding_to_rtl)
+ return false;
+
+ /* We are going to invoke recog in a re-entrant way and thus
+ have to capture its current state and restore it afterwards. */
+ recog_data_d prev_recog_data = recog_data;
+
+ rtx_insn* i = make_insn_raw (gen_rtx_SET (get_t_reg_rtx (), op));
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ /* If the comparison op doesn't have a result mode, set it to SImode. */
+ machine_mode prev_op_mode = GET_MODE (op);
+ if (COMPARISON_P (op) && prev_op_mode == VOIDmode)
+ PUT_MODE (op, SImode);
+
+ int result = recog (PATTERN (i), i, 0);
+
+ /* It seems there is no insn like that. Create a negated version and
+ try again. If we hit a negated form, we'll allow that and append a
+ nott sequence when splitting out the insns. Insns that do the split
+ can then remove the trailing nott if they know how to deal with it. */
+ if (result < 0 && COMPARISON_P (op))
+ {
+ machine_mode cmp_mode = GET_MODE (XEXP (op, 0));
+ if (cmp_mode == VOIDmode)
+ cmp_mode = GET_MODE (XEXP (op, 1));
+
+ rtx_code prev_code = GET_CODE (op);
+ PUT_CODE (op, reverse_condition (GET_CODE (op)));
+ result = recog (PATTERN (i), i, 0);
+ PUT_CODE (op, prev_code);
+ }
+
+ PUT_MODE (op, prev_op_mode);
+ recog_data = prev_recog_data;
+ return result >= 0;
+}
+
+/* Returns true when recog of a 'treg_set_expr' is currently in progress.
+ This can be used as a condition for insn/split patterns to allow certain
+ T bit setting patters only to be matched as sub expressions of other
+ patterns. */
+bool
+sh_in_recog_treg_set_expr (void)
+{
+ return sh_recog_treg_set_expr_reent_count > 0;
+}
+
+/* Given an rtx x, which is assumed to be some expression that has been
+ matched by the 'treg_set_expr' predicate before, split and emit the
+ insns that are necessary to calculate the expression and store the result
+ in the T bit.
+ The splitting is done recursively similar to 'try_split' in emit-rt.c.
+ Unfortunately we can't use 'try_split' here directly, as it tries to invoke
+ 'delete_insn' which then causes the DF parts to bail out, because we
+ currently are inside another gen_split* function and would invoke
+ 'try_split' in a reentrant way. */
+static std::pair<rtx_insn*, rtx_insn*>
+sh_try_split_insn_simple (rtx_insn* i, rtx_insn* curr_insn, int n = 0)
+{
+ if (dump_file)
+ {
+ fprintf (dump_file, "sh_try_split_insn_simple n = %d i = \n", n);
+ print_rtl_single (dump_file, i);
+ fprintf (dump_file, "\n");
+ }
+
+ rtx_insn* seq = split_insns (PATTERN (i), curr_insn);
+
+ if (seq == NULL)
+ return std::make_pair (i, i);
+
+ /* Avoid infinite splitter loops if any insn of the result matches
+ the original pattern. */
+ for (rtx_insn* s = seq; s != NULL; s = NEXT_INSN (s))
+ if (INSN_P (s) && rtx_equal_p (PATTERN (s), PATTERN (i)))
+ return std::make_pair (i, i);
+
+ unshare_all_rtl_in_chain (seq);
+
+ /* 'seq' is now a replacement for 'i'. Assuming that 'i' is an insn in
+ a linked list, replace the single insn with the new insns. */
+ rtx_insn* seqlast = seq;
+ while (NEXT_INSN (seqlast) != NULL)
+ seqlast = NEXT_INSN (seqlast);
+
+ if (rtx_insn* iprev = PREV_INSN (i))
+ SET_NEXT_INSN (iprev) = seq;
+ if (rtx_insn* inext = NEXT_INSN (i))
+ SET_PREV_INSN (inext) = seqlast;
+
+ SET_PREV_INSN (seq) = PREV_INSN (i);
+ SET_NEXT_INSN (seqlast) = NEXT_INSN (i);
+
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ /* Recursively split all insns. */
+ for (i = seq; ; i = NEXT_INSN (i))
+ {
+ std::pair<rtx_insn*, rtx_insn*> ii =
+ sh_try_split_insn_simple (i, curr_insn, n + 1);
+ if (i == seq)
+ seq = ii.first;
+ if (i == seqlast)
+ {
+ seqlast = ii.second;
+ break;
+ }
+ i = ii.first;
+ }
+
+ return std::make_pair (seq, seqlast);
+}
+
+sh_treg_insns
+sh_split_treg_set_expr (rtx x, rtx_insn* curr_insn)
+{
+ if (t_reg_operand (x, VOIDmode))
+ return sh_treg_insns ();
+
+ scope_counter in_treg_set_expr (sh_recog_treg_set_expr_reent_count);
+
+ rtx_insn* i = make_insn_raw (gen_rtx_SET (get_t_reg_rtx (), x));
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "split_treg_set_expr insn:\n");
+ print_rtl (dump_file, i);
+ fprintf (dump_file, "\n");
+ }
+
+ /* If the insn is not found, we will try a negated form and append
+ a nott. */
+ bool append_nott = false;
+
+ /* We are going to invoke recog/split_insns in a re-entrant way and thus
+ have to capture its current state and restore it afterwards. */
+ recog_data_d prev_recog_data = recog_data;
+
+ if (negt_reg_operand (x, GET_MODE (x)))
+ {
+ /* This is a normal movt followed by a nott. It will be converted
+ into a movrt after initial expansion. */
+ XEXP (PATTERN (i), 1) = get_t_reg_rtx ();
+ append_nott = true;
+ }
+ else
+ {
+ /* If the comparison op doesn't have a mode set, set it to SImode. */
+ if (COMPARISON_P (x) && GET_MODE (x) == VOIDmode)
+ PUT_MODE (x, SImode);
+
+ int insn_code = recog (PATTERN (i), i, 0);
+
+ if (insn_code < 0 && COMPARISON_P (x))
+ {
+ machine_mode cmp_mode = GET_MODE (XEXP (x, 0));
+ if (cmp_mode == VOIDmode)
+ cmp_mode = GET_MODE (XEXP (x, 1));
+
+ PUT_CODE (x, reverse_condition (GET_CODE (x)));
+ insn_code = recog (PATTERN (i), i, 0);
+ append_nott = true;
+ }
+
+ gcc_assert (insn_code >= 0);
+ }
+
+ /* Try to recursively split the insn. Some insns might refuse to split
+ any further while we are in the treg_set_expr splitting phase. They
+ will be emitted as part of the outer insn and then split again. */
+ std::pair<rtx_insn*, rtx_insn*> insnlist =
+ sh_try_split_insn_simple (i, curr_insn);
+
+ /* Restore recog state. */
+ recog_data = prev_recog_data;
+
+ rtx_insn* nott_insn = sh_is_nott_insn (insnlist.second)
+ ? insnlist.second
+ : NULL;
+ if (dump_file)
+ {
+ fprintf (dump_file, "split_treg_set_expr insnlist:\n");
+ print_rtl (dump_file, insnlist.first);
+ fprintf (dump_file, "\n");
+
+ if (nott_insn != NULL)
+ fprintf (dump_file, "trailing nott insn %d\n", INSN_UID (nott_insn));
+ }
+
+ emit_insn (insnlist.first);
+
+ if (nott_insn != NULL && append_nott)
+ {
+ if (dump_file)
+ fprintf (dump_file, "removing trailing nott\n");
+ remove_insn (nott_insn);
+ nott_insn = NULL;
+ append_nott = false;
+ }
+
+ if (append_nott)
+ nott_insn = emit_insn (gen_nott (get_t_reg_rtx ()));
+
+ rtx_insn* first_insn = get_insns ();
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "resulting insns:\n");
+ print_rtl (dump_file, first_insn);
+ fprintf (dump_file, "\n");
+ }
+
+ return sh_treg_insns (first_insn, nott_insn);
+}
+
+/*------------------------------------------------------------------------------
+ Mode switching support code.
+*/
+
+static void
+sh_emit_mode_set (int entity ATTRIBUTE_UNUSED, int mode,
+ int prev_mode, HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
+{
+ if ((TARGET_SH4A_FP || TARGET_FPU_SH4_300)
+ && prev_mode != FP_MODE_NONE && prev_mode != mode)
+ {
+ emit_insn (gen_toggle_pr ());
+ if (TARGET_FMOVD)
+ emit_insn (gen_toggle_sz ());
+ }
+ else if (mode != FP_MODE_NONE)
+ {
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_sts_fpscr (tmp));
+ rtx i = NULL;
+
+ const unsigned HOST_WIDE_INT fpbits =
+ TARGET_FMOVD ? (FPSCR_PR | FPSCR_SZ) : FPSCR_PR;
+
+ if (prev_mode != FP_MODE_NONE && prev_mode != mode)
+ i = gen_xorsi3 (tmp, tmp, force_reg (SImode, GEN_INT (fpbits)));
+ else if (mode == FP_MODE_SINGLE)
+ i = gen_andsi3 (tmp, tmp, force_reg (SImode, GEN_INT (~fpbits)));
+ else if (mode == FP_MODE_DOUBLE)
+ i = gen_iorsi3 (tmp, tmp, force_reg (SImode, GEN_INT (fpbits)));
+ else
+ gcc_unreachable ();
+
+ emit_insn (i);
+ emit_insn (gen_lds_fpscr (tmp));
+ }
+}
+
+static int
+sh_mode_needed (int entity ATTRIBUTE_UNUSED, rtx_insn *insn)
+{
+ return recog_memoized (insn) >= 0 ? get_attr_fp_mode (insn) : FP_MODE_NONE;
+}
+
+static int
+sh_mode_after (int entity ATTRIBUTE_UNUSED, int mode, rtx_insn *insn)
+{
+ if (TARGET_HITACHI && recog_memoized (insn) >= 0 &&
+ get_attr_fp_set (insn) != FP_SET_NONE)
+ return (int) get_attr_fp_set (insn);
+ else
+ return mode;
+}
+
+static int
+sh_mode_entry (int entity ATTRIBUTE_UNUSED)
+{
+ return NORMAL_MODE (entity);
+}
+
+static int
+sh_mode_exit (int entity ATTRIBUTE_UNUSED)
+{
+ return sh_cfun_attr_renesas_p () ? FP_MODE_NONE : NORMAL_MODE (entity);
+}
+
+static int
+sh_mode_priority (int entity ATTRIBUTE_UNUSED, int n)
+{
+ return ((TARGET_FPU_SINGLE != 0) ^ (n) ? FP_MODE_SINGLE : FP_MODE_DOUBLE);
+}
+
+/*------------------------------------------------------------------------------
+ Misc
+*/
+
+/* Return true if we use LRA instead of reload pass. */
+bool
+sh_lra_p (void)
+{
+ return sh_lra_flag;
+}
+
+/* Implement TARGET_USE_BY_PIECES_INFRASTRUCTURE_P. */
+
+static bool
+sh_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size,
+ unsigned int align,
+ enum by_pieces_operation op,
+ bool speed_p)
+{
+ switch (op)
+ {
+ case MOVE_BY_PIECES:
+ return by_pieces_ninsns (size, align, MOVE_MAX_PIECES + 1, op)
+ < (!speed_p ? 2 : (align >= 32) ? 16 : 2);
+ case STORE_BY_PIECES:
+ case SET_BY_PIECES:
+ return by_pieces_ninsns (size, align, STORE_MAX_PIECES + 1, op)
+ < (!speed_p ? 2 : (align >= 32) ? 16 : 2);
+ default:
+ return default_use_by_pieces_infrastructure_p (size, align,
+ op, speed_p);
+ }
+}
+
+bool
+sh_cannot_force_const_mem_p (machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED)
+{
+ return TARGET_FDPIC;
+}
+
+/* Emit insns to load the function address from FUNCDESC (an FDPIC
+ function descriptor) into r1 and the GOT address into r12,
+ returning an rtx for r1. */
+
+rtx
+sh_load_function_descriptor (rtx funcdesc)
+{
+ rtx r1 = gen_rtx_REG (Pmode, R1_REG);
+ rtx pic_reg = gen_rtx_REG (Pmode, PIC_REG);
+ rtx fnaddr = gen_rtx_MEM (Pmode, funcdesc);
+ rtx gotaddr = gen_rtx_MEM (Pmode, plus_constant (Pmode, funcdesc, 4));
+
+ emit_move_insn (r1, fnaddr);
+ /* The ABI requires the entry point address to be loaded first, so
+ prevent the load from being moved after that of the GOT
+ address. */
+ emit_insn (gen_blockage ());
+ emit_move_insn (pic_reg, gotaddr);
+ return r1;
+}
+
+/* Return an rtx holding the initial value of the FDPIC register (the
+ FDPIC pointer passed in from the caller). */
+
+rtx
+sh_get_fdpic_reg_initial_val (void)
+{
+ return get_hard_reg_initial_val (Pmode, PIC_REG);
+}
+
+#include "gt-sh.h"