diff options
author | Martin Liska <mliska@suse.cz> | 2022-01-14 16:56:44 +0100 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-01-17 22:12:04 +0100 |
commit | 5c69acb32329d49e58c26fa41ae74229a52b9106 (patch) | |
tree | ddb05f9d73afb6f998457d2ac4b720e3b3b60483 /gcc/config/sh/sh.cc | |
parent | 490e23032baaece71f2ec09fa1805064b150fbc2 (diff) | |
download | gcc-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.cc | 12630 |
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" |