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/combine.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/combine.cc')
-rw-r--r-- | gcc/combine.cc | 14960 |
1 files changed, 14960 insertions, 0 deletions
diff --git a/gcc/combine.cc b/gcc/combine.cc new file mode 100644 index 0000000..fb4a27a --- /dev/null +++ b/gcc/combine.cc @@ -0,0 +1,14960 @@ +/* Optimize by combining instructions for GNU compiler. + Copyright (C) 1987-2022 Free Software Foundation, Inc. + +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/>. */ + +/* This module is essentially the "combiner" phase of the U. of Arizona + Portable Optimizer, but redone to work on our list-structured + representation for RTL instead of their string representation. + + The LOG_LINKS of each insn identify the most recent assignment + to each REG used in the insn. It is a list of previous insns, + each of which contains a SET for a REG that is used in this insn + and not used or set in between. LOG_LINKs never cross basic blocks. + They were set up by the preceding pass (lifetime analysis). + + We try to combine each pair of insns joined by a logical link. + We also try to combine triplets of insns A, B and C when C has + a link back to B and B has a link back to A. Likewise for a + small number of quadruplets of insns A, B, C and D for which + there's high likelihood of success. + + We check (with modified_between_p) to avoid combining in such a way + as to move a computation to a place where its value would be different. + + Combination is done by mathematically substituting the previous + insn(s) values for the regs they set into the expressions in + the later insns that refer to these regs. If the result is a valid insn + for our target machine, according to the machine description, + we install it, delete the earlier insns, and update the data flow + information (LOG_LINKS and REG_NOTES) for what we did. + + There are a few exceptions where the dataflow information isn't + completely updated (however this is only a local issue since it is + regenerated before the next pass that uses it): + + - reg_live_length is not updated + - reg_n_refs is not adjusted in the rare case when a register is + no longer required in a computation + - there are extremely rare cases (see distribute_notes) when a + REG_DEAD note is lost + - a LOG_LINKS entry that refers to an insn with multiple SETs may be + removed because there is no way to know which register it was + linking + + To simplify substitution, we combine only when the earlier insn(s) + consist of only a single assignment. To simplify updating afterward, + we never combine when a subroutine call appears in the middle. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "cfghooks.h" +#include "predict.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "optabs.h" +#include "regs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "cgraph.h" +#include "stor-layout.h" +#include "cfgrtl.h" +#include "cfgcleanup.h" +/* Include expr.h after insn-config.h so we get HAVE_conditional_move. */ +#include "explow.h" +#include "insn-attr.h" +#include "rtlhooks-def.h" +#include "expr.h" +#include "tree-pass.h" +#include "valtrack.h" +#include "rtl-iter.h" +#include "print-rtl.h" +#include "function-abi.h" +#include "rtlanal.h" + +/* Number of attempts to combine instructions in this function. */ + +static int combine_attempts; + +/* Number of attempts that got as far as substitution in this function. */ + +static int combine_merges; + +/* Number of instructions combined with added SETs in this function. */ + +static int combine_extras; + +/* Number of instructions combined in this function. */ + +static int combine_successes; + +/* Totals over entire compilation. */ + +static int total_attempts, total_merges, total_extras, total_successes; + +/* combine_instructions may try to replace the right hand side of the + second instruction with the value of an associated REG_EQUAL note + before throwing it at try_combine. That is problematic when there + is a REG_DEAD note for a register used in the old right hand side + and can cause distribute_notes to do wrong things. This is the + second instruction if it has been so modified, null otherwise. */ + +static rtx_insn *i2mod; + +/* When I2MOD is nonnull, this is a copy of the old right hand side. */ + +static rtx i2mod_old_rhs; + +/* When I2MOD is nonnull, this is a copy of the new right hand side. */ + +static rtx i2mod_new_rhs; + +struct reg_stat_type { + /* Record last point of death of (hard or pseudo) register n. */ + rtx_insn *last_death; + + /* Record last point of modification of (hard or pseudo) register n. */ + rtx_insn *last_set; + + /* The next group of fields allows the recording of the last value assigned + to (hard or pseudo) register n. We use this information to see if an + operation being processed is redundant given a prior operation performed + on the register. For example, an `and' with a constant is redundant if + all the zero bits are already known to be turned off. + + We use an approach similar to that used by cse, but change it in the + following ways: + + (1) We do not want to reinitialize at each label. + (2) It is useful, but not critical, to know the actual value assigned + to a register. Often just its form is helpful. + + Therefore, we maintain the following fields: + + last_set_value the last value assigned + last_set_label records the value of label_tick when the + register was assigned + last_set_table_tick records the value of label_tick when a + value using the register is assigned + last_set_invalid set to nonzero when it is not valid + to use the value of this register in some + register's value + + To understand the usage of these tables, it is important to understand + the distinction between the value in last_set_value being valid and + the register being validly contained in some other expression in the + table. + + (The next two parameters are out of date). + + reg_stat[i].last_set_value is valid if it is nonzero, and either + reg_n_sets[i] is 1 or reg_stat[i].last_set_label == label_tick. + + Register I may validly appear in any expression returned for the value + of another register if reg_n_sets[i] is 1. It may also appear in the + value for register J if reg_stat[j].last_set_invalid is zero, or + reg_stat[i].last_set_label < reg_stat[j].last_set_label. + + If an expression is found in the table containing a register which may + not validly appear in an expression, the register is replaced by + something that won't match, (clobber (const_int 0)). */ + + /* Record last value assigned to (hard or pseudo) register n. */ + + rtx last_set_value; + + /* Record the value of label_tick when an expression involving register n + is placed in last_set_value. */ + + int last_set_table_tick; + + /* Record the value of label_tick when the value for register n is placed in + last_set_value. */ + + int last_set_label; + + /* These fields are maintained in parallel with last_set_value and are + used to store the mode in which the register was last set, the bits + that were known to be zero when it was last set, and the number of + sign bits copies it was known to have when it was last set. */ + + unsigned HOST_WIDE_INT last_set_nonzero_bits; + char last_set_sign_bit_copies; + ENUM_BITFIELD(machine_mode) last_set_mode : 8; + + /* Set nonzero if references to register n in expressions should not be + used. last_set_invalid is set nonzero when this register is being + assigned to and last_set_table_tick == label_tick. */ + + char last_set_invalid; + + /* Some registers that are set more than once and used in more than one + basic block are nevertheless always set in similar ways. For example, + a QImode register may be loaded from memory in two places on a machine + where byte loads zero extend. + + We record in the following fields if a register has some leading bits + that are always equal to the sign bit, and what we know about the + nonzero bits of a register, specifically which bits are known to be + zero. + + If an entry is zero, it means that we don't know anything special. */ + + unsigned char sign_bit_copies; + + unsigned HOST_WIDE_INT nonzero_bits; + + /* Record the value of the label_tick when the last truncation + happened. The field truncated_to_mode is only valid if + truncation_label == label_tick. */ + + int truncation_label; + + /* Record the last truncation seen for this register. If truncation + is not a nop to this mode we might be able to save an explicit + truncation if we know that value already contains a truncated + value. */ + + ENUM_BITFIELD(machine_mode) truncated_to_mode : 8; +}; + + +static vec<reg_stat_type> reg_stat; + +/* One plus the highest pseudo for which we track REG_N_SETS. + regstat_init_n_sets_and_refs allocates the array for REG_N_SETS just once, + but during combine_split_insns new pseudos can be created. As we don't have + updated DF information in that case, it is hard to initialize the array + after growing. The combiner only cares about REG_N_SETS (regno) == 1, + so instead of growing the arrays, just assume all newly created pseudos + during combine might be set multiple times. */ + +static unsigned int reg_n_sets_max; + +/* Record the luid of the last insn that invalidated memory + (anything that writes memory, and subroutine calls, but not pushes). */ + +static int mem_last_set; + +/* Record the luid of the last CALL_INSN + so we can tell whether a potential combination crosses any calls. */ + +static int last_call_luid; + +/* When `subst' is called, this is the insn that is being modified + (by combining in a previous insn). The PATTERN of this insn + is still the old pattern partially modified and it should not be + looked at, but this may be used to examine the successors of the insn + to judge whether a simplification is valid. */ + +static rtx_insn *subst_insn; + +/* This is the lowest LUID that `subst' is currently dealing with. + get_last_value will not return a value if the register was set at or + after this LUID. If not for this mechanism, we could get confused if + I2 or I1 in try_combine were an insn that used the old value of a register + to obtain a new value. In that case, we might erroneously get the + new value of the register when we wanted the old one. */ + +static int subst_low_luid; + +/* This contains any hard registers that are used in newpat; reg_dead_at_p + must consider all these registers to be always live. */ + +static HARD_REG_SET newpat_used_regs; + +/* This is an insn to which a LOG_LINKS entry has been added. If this + insn is the earlier than I2 or I3, combine should rescan starting at + that location. */ + +static rtx_insn *added_links_insn; + +/* And similarly, for notes. */ + +static rtx_insn *added_notes_insn; + +/* Basic block in which we are performing combines. */ +static basic_block this_basic_block; +static bool optimize_this_for_speed_p; + + +/* Length of the currently allocated uid_insn_cost array. */ + +static int max_uid_known; + +/* The following array records the insn_cost for every insn + in the instruction stream. */ + +static int *uid_insn_cost; + +/* The following array records the LOG_LINKS for every insn in the + instruction stream as struct insn_link pointers. */ + +struct insn_link { + rtx_insn *insn; + unsigned int regno; + struct insn_link *next; +}; + +static struct insn_link **uid_log_links; + +static inline int +insn_uid_check (const_rtx insn) +{ + int uid = INSN_UID (insn); + gcc_checking_assert (uid <= max_uid_known); + return uid; +} + +#define INSN_COST(INSN) (uid_insn_cost[insn_uid_check (INSN)]) +#define LOG_LINKS(INSN) (uid_log_links[insn_uid_check (INSN)]) + +#define FOR_EACH_LOG_LINK(L, INSN) \ + for ((L) = LOG_LINKS (INSN); (L); (L) = (L)->next) + +/* Links for LOG_LINKS are allocated from this obstack. */ + +static struct obstack insn_link_obstack; + +/* Allocate a link. */ + +static inline struct insn_link * +alloc_insn_link (rtx_insn *insn, unsigned int regno, struct insn_link *next) +{ + struct insn_link *l + = (struct insn_link *) obstack_alloc (&insn_link_obstack, + sizeof (struct insn_link)); + l->insn = insn; + l->regno = regno; + l->next = next; + return l; +} + +/* Incremented for each basic block. */ + +static int label_tick; + +/* Reset to label_tick for each extended basic block in scanning order. */ + +static int label_tick_ebb_start; + +/* Mode used to compute significance in reg_stat[].nonzero_bits. It is the + largest integer mode that can fit in HOST_BITS_PER_WIDE_INT. */ + +static scalar_int_mode nonzero_bits_mode; + +/* Nonzero when reg_stat[].nonzero_bits and reg_stat[].sign_bit_copies can + be safely used. It is zero while computing them and after combine has + completed. This former test prevents propagating values based on + previously set values, which can be incorrect if a variable is modified + in a loop. */ + +static int nonzero_sign_valid; + + +/* Record one modification to rtl structure + to be undone by storing old_contents into *where. */ + +enum undo_kind { UNDO_RTX, UNDO_INT, UNDO_MODE, UNDO_LINKS }; + +struct undo +{ + struct undo *next; + enum undo_kind kind; + union { rtx r; int i; machine_mode m; struct insn_link *l; } old_contents; + union { rtx *r; int *i; struct insn_link **l; } where; +}; + +/* Record a bunch of changes to be undone, up to MAX_UNDO of them. + num_undo says how many are currently recorded. + + other_insn is nonzero if we have modified some other insn in the process + of working on subst_insn. It must be verified too. */ + +struct undobuf +{ + struct undo *undos; + struct undo *frees; + rtx_insn *other_insn; +}; + +static struct undobuf undobuf; + +/* Number of times the pseudo being substituted for + was found and replaced. */ + +static int n_occurrences; + +static rtx reg_nonzero_bits_for_combine (const_rtx, scalar_int_mode, + scalar_int_mode, + unsigned HOST_WIDE_INT *); +static rtx reg_num_sign_bit_copies_for_combine (const_rtx, scalar_int_mode, + scalar_int_mode, + unsigned int *); +static void do_SUBST (rtx *, rtx); +static void do_SUBST_INT (int *, int); +static void init_reg_last (void); +static void setup_incoming_promotions (rtx_insn *); +static void set_nonzero_bits_and_sign_copies (rtx, const_rtx, void *); +static int cant_combine_insn_p (rtx_insn *); +static int can_combine_p (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *, + rtx_insn *, rtx_insn *, rtx *, rtx *); +static int combinable_i3pat (rtx_insn *, rtx *, rtx, rtx, rtx, int, int, rtx *); +static int contains_muldiv (rtx); +static rtx_insn *try_combine (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *, + int *, rtx_insn *); +static void undo_all (void); +static void undo_commit (void); +static rtx *find_split_point (rtx *, rtx_insn *, bool); +static rtx subst (rtx, rtx, rtx, int, int, int); +static rtx combine_simplify_rtx (rtx, machine_mode, int, int); +static rtx simplify_if_then_else (rtx); +static rtx simplify_set (rtx); +static rtx simplify_logical (rtx); +static rtx expand_compound_operation (rtx); +static const_rtx expand_field_assignment (const_rtx); +static rtx make_extraction (machine_mode, rtx, HOST_WIDE_INT, + rtx, unsigned HOST_WIDE_INT, int, int, int); +static int get_pos_from_mask (unsigned HOST_WIDE_INT, + unsigned HOST_WIDE_INT *); +static rtx canon_reg_for_combine (rtx, rtx); +static rtx force_int_to_mode (rtx, scalar_int_mode, scalar_int_mode, + scalar_int_mode, unsigned HOST_WIDE_INT, int); +static rtx force_to_mode (rtx, machine_mode, + unsigned HOST_WIDE_INT, int); +static rtx if_then_else_cond (rtx, rtx *, rtx *); +static rtx known_cond (rtx, enum rtx_code, rtx, rtx); +static int rtx_equal_for_field_assignment_p (rtx, rtx, bool = false); +static rtx make_field_assignment (rtx); +static rtx apply_distributive_law (rtx); +static rtx distribute_and_simplify_rtx (rtx, int); +static rtx simplify_and_const_int_1 (scalar_int_mode, rtx, + unsigned HOST_WIDE_INT); +static rtx simplify_and_const_int (rtx, scalar_int_mode, rtx, + unsigned HOST_WIDE_INT); +static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code, + HOST_WIDE_INT, machine_mode, int *); +static rtx simplify_shift_const_1 (enum rtx_code, machine_mode, rtx, int); +static rtx simplify_shift_const (rtx, enum rtx_code, machine_mode, rtx, + int); +static int recog_for_combine (rtx *, rtx_insn *, rtx *); +static rtx gen_lowpart_for_combine (machine_mode, rtx); +static enum rtx_code simplify_compare_const (enum rtx_code, machine_mode, + rtx, rtx *); +static enum rtx_code simplify_comparison (enum rtx_code, rtx *, rtx *); +static void update_table_tick (rtx); +static void record_value_for_reg (rtx, rtx_insn *, rtx); +static void check_promoted_subreg (rtx_insn *, rtx); +static void record_dead_and_set_regs_1 (rtx, const_rtx, void *); +static void record_dead_and_set_regs (rtx_insn *); +static int get_last_value_validate (rtx *, rtx_insn *, int, int); +static rtx get_last_value (const_rtx); +static void reg_dead_at_p_1 (rtx, const_rtx, void *); +static int reg_dead_at_p (rtx, rtx_insn *); +static void move_deaths (rtx, rtx, int, rtx_insn *, rtx *); +static int reg_bitfield_target_p (rtx, rtx); +static void distribute_notes (rtx, rtx_insn *, rtx_insn *, rtx_insn *, rtx, rtx, rtx); +static void distribute_links (struct insn_link *); +static void mark_used_regs_combine (rtx); +static void record_promoted_value (rtx_insn *, rtx); +static bool unmentioned_reg_p (rtx, rtx); +static void record_truncated_values (rtx *, void *); +static bool reg_truncated_to_mode (machine_mode, const_rtx); +static rtx gen_lowpart_or_truncate (machine_mode, rtx); + + +/* It is not safe to use ordinary gen_lowpart in combine. + See comments in gen_lowpart_for_combine. */ +#undef RTL_HOOKS_GEN_LOWPART +#define RTL_HOOKS_GEN_LOWPART gen_lowpart_for_combine + +/* Our implementation of gen_lowpart never emits a new pseudo. */ +#undef RTL_HOOKS_GEN_LOWPART_NO_EMIT +#define RTL_HOOKS_GEN_LOWPART_NO_EMIT gen_lowpart_for_combine + +#undef RTL_HOOKS_REG_NONZERO_REG_BITS +#define RTL_HOOKS_REG_NONZERO_REG_BITS reg_nonzero_bits_for_combine + +#undef RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES +#define RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES reg_num_sign_bit_copies_for_combine + +#undef RTL_HOOKS_REG_TRUNCATED_TO_MODE +#define RTL_HOOKS_REG_TRUNCATED_TO_MODE reg_truncated_to_mode + +static const struct rtl_hooks combine_rtl_hooks = RTL_HOOKS_INITIALIZER; + + +/* Convenience wrapper for the canonicalize_comparison target hook. + Target hooks cannot use enum rtx_code. */ +static inline void +target_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1, + bool op0_preserve_value) +{ + int code_int = (int)*code; + targetm.canonicalize_comparison (&code_int, op0, op1, op0_preserve_value); + *code = (enum rtx_code)code_int; +} + +/* Try to split PATTERN found in INSN. This returns NULL_RTX if + PATTERN cannot be split. Otherwise, it returns an insn sequence. + This is a wrapper around split_insns which ensures that the + reg_stat vector is made larger if the splitter creates a new + register. */ + +static rtx_insn * +combine_split_insns (rtx pattern, rtx_insn *insn) +{ + rtx_insn *ret; + unsigned int nregs; + + ret = split_insns (pattern, insn); + nregs = max_reg_num (); + if (nregs > reg_stat.length ()) + reg_stat.safe_grow_cleared (nregs, true); + return ret; +} + +/* This is used by find_single_use to locate an rtx in LOC that + contains exactly one use of DEST, which is typically a REG. + It returns a pointer to the innermost rtx expression + containing DEST. Appearances of DEST that are being used to + totally replace it are not counted. */ + +static rtx * +find_single_use_1 (rtx dest, rtx *loc) +{ + rtx x = *loc; + enum rtx_code code = GET_CODE (x); + rtx *result = NULL; + rtx *this_result; + int i; + const char *fmt; + + switch (code) + { + case CONST: + case LABEL_REF: + case SYMBOL_REF: + CASE_CONST_ANY: + case CLOBBER: + return 0; + + case SET: + /* If the destination is anything other than PC, a REG or a SUBREG + of a REG that occupies all of the REG, the insn uses DEST if + it is mentioned in the destination or the source. Otherwise, we + need just check the source. */ + if (GET_CODE (SET_DEST (x)) != PC + && !REG_P (SET_DEST (x)) + && ! (GET_CODE (SET_DEST (x)) == SUBREG + && REG_P (SUBREG_REG (SET_DEST (x))) + && !read_modify_subreg_p (SET_DEST (x)))) + break; + + return find_single_use_1 (dest, &SET_SRC (x)); + + case MEM: + case SUBREG: + return find_single_use_1 (dest, &XEXP (x, 0)); + + default: + break; + } + + /* If it wasn't one of the common cases above, check each expression and + vector of this code. Look for a unique usage of DEST. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (dest == XEXP (x, i) + || (REG_P (dest) && REG_P (XEXP (x, i)) + && REGNO (dest) == REGNO (XEXP (x, i)))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XEXP (x, i)); + + if (result == NULL) + result = this_result; + else if (this_result) + /* Duplicate usage. */ + return NULL; + } + else if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + if (XVECEXP (x, i, j) == dest + || (REG_P (dest) + && REG_P (XVECEXP (x, i, j)) + && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); + + if (result == NULL) + result = this_result; + else if (this_result) + return NULL; + } + } + } + + return result; +} + + +/* See if DEST, produced in INSN, is used only a single time in the + sequel. If so, return a pointer to the innermost rtx expression in which + it is used. + + If PLOC is nonzero, *PLOC is set to the insn containing the single use. + + Otherwise, we find the single use by finding an insn that has a + LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is + only referenced once in that insn, we know that it must be the first + and last insn referencing DEST. */ + +static rtx * +find_single_use (rtx dest, rtx_insn *insn, rtx_insn **ploc) +{ + basic_block bb; + rtx_insn *next; + rtx *result; + struct insn_link *link; + + if (!REG_P (dest)) + return 0; + + bb = BLOCK_FOR_INSN (insn); + for (next = NEXT_INSN (insn); + next && BLOCK_FOR_INSN (next) == bb; + next = NEXT_INSN (next)) + if (NONDEBUG_INSN_P (next) && dead_or_set_p (next, dest)) + { + FOR_EACH_LOG_LINK (link, next) + if (link->insn == insn && link->regno == REGNO (dest)) + break; + + if (link) + { + result = find_single_use_1 (dest, &PATTERN (next)); + if (ploc) + *ploc = next; + return result; + } + } + + return 0; +} + +/* Substitute NEWVAL, an rtx expression, into INTO, a place in some + insn. The substitution can be undone by undo_all. If INTO is already + set to NEWVAL, do not record this change. Because computing NEWVAL might + also call SUBST, we have to compute it before we put anything into + the undo table. */ + +static void +do_SUBST (rtx *into, rtx newval) +{ + struct undo *buf; + rtx oldval = *into; + + if (oldval == newval) + return; + + /* We'd like to catch as many invalid transformations here as + possible. Unfortunately, there are way too many mode changes + that are perfectly valid, so we'd waste too much effort for + little gain doing the checks here. Focus on catching invalid + transformations involving integer constants. */ + if (GET_MODE_CLASS (GET_MODE (oldval)) == MODE_INT + && CONST_INT_P (newval)) + { + /* Sanity check that we're replacing oldval with a CONST_INT + that is a valid sign-extension for the original mode. */ + gcc_assert (INTVAL (newval) + == trunc_int_for_mode (INTVAL (newval), GET_MODE (oldval))); + + /* Replacing the operand of a SUBREG or a ZERO_EXTEND with a + CONST_INT is not valid, because after the replacement, the + original mode would be gone. Unfortunately, we can't tell + when do_SUBST is called to replace the operand thereof, so we + perform this test on oldval instead, checking whether an + invalid replacement took place before we got here. */ + gcc_assert (!(GET_CODE (oldval) == SUBREG + && CONST_INT_P (SUBREG_REG (oldval)))); + gcc_assert (!(GET_CODE (oldval) == ZERO_EXTEND + && CONST_INT_P (XEXP (oldval, 0)))); + } + + if (undobuf.frees) + buf = undobuf.frees, undobuf.frees = buf->next; + else + buf = XNEW (struct undo); + + buf->kind = UNDO_RTX; + buf->where.r = into; + buf->old_contents.r = oldval; + *into = newval; + + buf->next = undobuf.undos, undobuf.undos = buf; +} + +#define SUBST(INTO, NEWVAL) do_SUBST (&(INTO), (NEWVAL)) + +/* Similar to SUBST, but NEWVAL is an int expression. Note that substitution + for the value of a HOST_WIDE_INT value (including CONST_INT) is + not safe. */ + +static void +do_SUBST_INT (int *into, int newval) +{ + struct undo *buf; + int oldval = *into; + + if (oldval == newval) + return; + + if (undobuf.frees) + buf = undobuf.frees, undobuf.frees = buf->next; + else + buf = XNEW (struct undo); + + buf->kind = UNDO_INT; + buf->where.i = into; + buf->old_contents.i = oldval; + *into = newval; + + buf->next = undobuf.undos, undobuf.undos = buf; +} + +#define SUBST_INT(INTO, NEWVAL) do_SUBST_INT (&(INTO), (NEWVAL)) + +/* Similar to SUBST, but just substitute the mode. This is used when + changing the mode of a pseudo-register, so that any other + references to the entry in the regno_reg_rtx array will change as + well. */ + +static void +do_SUBST_MODE (rtx *into, machine_mode newval) +{ + struct undo *buf; + machine_mode oldval = GET_MODE (*into); + + if (oldval == newval) + return; + + if (undobuf.frees) + buf = undobuf.frees, undobuf.frees = buf->next; + else + buf = XNEW (struct undo); + + buf->kind = UNDO_MODE; + buf->where.r = into; + buf->old_contents.m = oldval; + adjust_reg_mode (*into, newval); + + buf->next = undobuf.undos, undobuf.undos = buf; +} + +#define SUBST_MODE(INTO, NEWVAL) do_SUBST_MODE (&(INTO), (NEWVAL)) + +/* Similar to SUBST, but NEWVAL is a LOG_LINKS expression. */ + +static void +do_SUBST_LINK (struct insn_link **into, struct insn_link *newval) +{ + struct undo *buf; + struct insn_link * oldval = *into; + + if (oldval == newval) + return; + + if (undobuf.frees) + buf = undobuf.frees, undobuf.frees = buf->next; + else + buf = XNEW (struct undo); + + buf->kind = UNDO_LINKS; + buf->where.l = into; + buf->old_contents.l = oldval; + *into = newval; + + buf->next = undobuf.undos, undobuf.undos = buf; +} + +#define SUBST_LINK(oldval, newval) do_SUBST_LINK (&oldval, newval) + +/* Subroutine of try_combine. Determine whether the replacement patterns + NEWPAT, NEWI2PAT and NEWOTHERPAT are cheaper according to insn_cost + than the original sequence I0, I1, I2, I3 and undobuf.other_insn. Note + that I0, I1 and/or NEWI2PAT may be NULL_RTX. Similarly, NEWOTHERPAT and + undobuf.other_insn may also both be NULL_RTX. Return false if the cost + of all the instructions can be estimated and the replacements are more + expensive than the original sequence. */ + +static bool +combine_validate_cost (rtx_insn *i0, rtx_insn *i1, rtx_insn *i2, rtx_insn *i3, + rtx newpat, rtx newi2pat, rtx newotherpat) +{ + int i0_cost, i1_cost, i2_cost, i3_cost; + int new_i2_cost, new_i3_cost; + int old_cost, new_cost; + + /* Lookup the original insn_costs. */ + i2_cost = INSN_COST (i2); + i3_cost = INSN_COST (i3); + + if (i1) + { + i1_cost = INSN_COST (i1); + if (i0) + { + i0_cost = INSN_COST (i0); + old_cost = (i0_cost > 0 && i1_cost > 0 && i2_cost > 0 && i3_cost > 0 + ? i0_cost + i1_cost + i2_cost + i3_cost : 0); + } + else + { + old_cost = (i1_cost > 0 && i2_cost > 0 && i3_cost > 0 + ? i1_cost + i2_cost + i3_cost : 0); + i0_cost = 0; + } + } + else + { + old_cost = (i2_cost > 0 && i3_cost > 0) ? i2_cost + i3_cost : 0; + i1_cost = i0_cost = 0; + } + + /* If we have split a PARALLEL I2 to I1,I2, we have counted its cost twice; + correct that. */ + if (old_cost && i1 && INSN_UID (i1) == INSN_UID (i2)) + old_cost -= i1_cost; + + + /* Calculate the replacement insn_costs. */ + rtx tmp = PATTERN (i3); + PATTERN (i3) = newpat; + int tmpi = INSN_CODE (i3); + INSN_CODE (i3) = -1; + new_i3_cost = insn_cost (i3, optimize_this_for_speed_p); + PATTERN (i3) = tmp; + INSN_CODE (i3) = tmpi; + if (newi2pat) + { + tmp = PATTERN (i2); + PATTERN (i2) = newi2pat; + tmpi = INSN_CODE (i2); + INSN_CODE (i2) = -1; + new_i2_cost = insn_cost (i2, optimize_this_for_speed_p); + PATTERN (i2) = tmp; + INSN_CODE (i2) = tmpi; + new_cost = (new_i2_cost > 0 && new_i3_cost > 0) + ? new_i2_cost + new_i3_cost : 0; + } + else + { + new_cost = new_i3_cost; + new_i2_cost = 0; + } + + if (undobuf.other_insn) + { + int old_other_cost, new_other_cost; + + old_other_cost = INSN_COST (undobuf.other_insn); + tmp = PATTERN (undobuf.other_insn); + PATTERN (undobuf.other_insn) = newotherpat; + tmpi = INSN_CODE (undobuf.other_insn); + INSN_CODE (undobuf.other_insn) = -1; + new_other_cost = insn_cost (undobuf.other_insn, + optimize_this_for_speed_p); + PATTERN (undobuf.other_insn) = tmp; + INSN_CODE (undobuf.other_insn) = tmpi; + if (old_other_cost > 0 && new_other_cost > 0) + { + old_cost += old_other_cost; + new_cost += new_other_cost; + } + else + old_cost = 0; + } + + /* Disallow this combination if both new_cost and old_cost are greater than + zero, and new_cost is greater than old cost. */ + int reject = old_cost > 0 && new_cost > old_cost; + + if (dump_file) + { + fprintf (dump_file, "%s combination of insns ", + reject ? "rejecting" : "allowing"); + if (i0) + fprintf (dump_file, "%d, ", INSN_UID (i0)); + if (i1 && INSN_UID (i1) != INSN_UID (i2)) + fprintf (dump_file, "%d, ", INSN_UID (i1)); + fprintf (dump_file, "%d and %d\n", INSN_UID (i2), INSN_UID (i3)); + + fprintf (dump_file, "original costs "); + if (i0) + fprintf (dump_file, "%d + ", i0_cost); + if (i1 && INSN_UID (i1) != INSN_UID (i2)) + fprintf (dump_file, "%d + ", i1_cost); + fprintf (dump_file, "%d + %d = %d\n", i2_cost, i3_cost, old_cost); + + if (newi2pat) + fprintf (dump_file, "replacement costs %d + %d = %d\n", + new_i2_cost, new_i3_cost, new_cost); + else + fprintf (dump_file, "replacement cost %d\n", new_cost); + } + + if (reject) + return false; + + /* Update the uid_insn_cost array with the replacement costs. */ + INSN_COST (i2) = new_i2_cost; + INSN_COST (i3) = new_i3_cost; + if (i1) + { + INSN_COST (i1) = 0; + if (i0) + INSN_COST (i0) = 0; + } + + return true; +} + + +/* Delete any insns that copy a register to itself. + Return true if the CFG was changed. */ + +static bool +delete_noop_moves (void) +{ + rtx_insn *insn, *next; + basic_block bb; + + bool edges_deleted = false; + + FOR_EACH_BB_FN (bb, cfun) + { + for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next) + { + next = NEXT_INSN (insn); + if (INSN_P (insn) && noop_move_p (insn)) + { + if (dump_file) + fprintf (dump_file, "deleting noop move %d\n", INSN_UID (insn)); + + edges_deleted |= delete_insn_and_edges (insn); + } + } + } + + return edges_deleted; +} + + +/* Return false if we do not want to (or cannot) combine DEF. */ +static bool +can_combine_def_p (df_ref def) +{ + /* Do not consider if it is pre/post modification in MEM. */ + if (DF_REF_FLAGS (def) & DF_REF_PRE_POST_MODIFY) + return false; + + unsigned int regno = DF_REF_REGNO (def); + + /* Do not combine frame pointer adjustments. */ + if ((regno == FRAME_POINTER_REGNUM + && (!reload_completed || frame_pointer_needed)) + || (!HARD_FRAME_POINTER_IS_FRAME_POINTER + && regno == HARD_FRAME_POINTER_REGNUM + && (!reload_completed || frame_pointer_needed)) + || (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && regno == ARG_POINTER_REGNUM && fixed_regs[regno])) + return false; + + return true; +} + +/* Return false if we do not want to (or cannot) combine USE. */ +static bool +can_combine_use_p (df_ref use) +{ + /* Do not consider the usage of the stack pointer by function call. */ + if (DF_REF_FLAGS (use) & DF_REF_CALL_STACK_USAGE) + return false; + + return true; +} + +/* Fill in log links field for all insns. */ + +static void +create_log_links (void) +{ + basic_block bb; + rtx_insn **next_use; + rtx_insn *insn; + df_ref def, use; + + next_use = XCNEWVEC (rtx_insn *, max_reg_num ()); + + /* Pass through each block from the end, recording the uses of each + register and establishing log links when def is encountered. + Note that we do not clear next_use array in order to save time, + so we have to test whether the use is in the same basic block as def. + + There are a few cases below when we do not consider the definition or + usage -- these are taken from original flow.c did. Don't ask me why it is + done this way; I don't know and if it works, I don't want to know. */ + + FOR_EACH_BB_FN (bb, cfun) + { + FOR_BB_INSNS_REVERSE (bb, insn) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + /* Log links are created only once. */ + gcc_assert (!LOG_LINKS (insn)); + + FOR_EACH_INSN_DEF (def, insn) + { + unsigned int regno = DF_REF_REGNO (def); + rtx_insn *use_insn; + + if (!next_use[regno]) + continue; + + if (!can_combine_def_p (def)) + continue; + + use_insn = next_use[regno]; + next_use[regno] = NULL; + + if (BLOCK_FOR_INSN (use_insn) != bb) + continue; + + /* flow.c claimed: + + We don't build a LOG_LINK for hard registers contained + in ASM_OPERANDs. If these registers get replaced, + we might wind up changing the semantics of the insn, + even if reload can make what appear to be valid + assignments later. */ + if (regno < FIRST_PSEUDO_REGISTER + && asm_noperands (PATTERN (use_insn)) >= 0) + continue; + + /* Don't add duplicate links between instructions. */ + struct insn_link *links; + FOR_EACH_LOG_LINK (links, use_insn) + if (insn == links->insn && regno == links->regno) + break; + + if (!links) + LOG_LINKS (use_insn) + = alloc_insn_link (insn, regno, LOG_LINKS (use_insn)); + } + + FOR_EACH_INSN_USE (use, insn) + if (can_combine_use_p (use)) + next_use[DF_REF_REGNO (use)] = insn; + } + } + + free (next_use); +} + +/* Walk the LOG_LINKS of insn B to see if we find a reference to A. Return + true if we found a LOG_LINK that proves that A feeds B. This only works + if there are no instructions between A and B which could have a link + depending on A, since in that case we would not record a link for B. */ + +static bool +insn_a_feeds_b (rtx_insn *a, rtx_insn *b) +{ + struct insn_link *links; + FOR_EACH_LOG_LINK (links, b) + if (links->insn == a) + return true; + return false; +} + +/* Main entry point for combiner. F is the first insn of the function. + NREGS is the first unused pseudo-reg number. + + Return nonzero if the CFG was changed (e.g. if the combiner has + turned an indirect jump instruction into a direct jump). */ +static int +combine_instructions (rtx_insn *f, unsigned int nregs) +{ + rtx_insn *insn, *next; + struct insn_link *links, *nextlinks; + rtx_insn *first; + basic_block last_bb; + + int new_direct_jump_p = 0; + + for (first = f; first && !NONDEBUG_INSN_P (first); ) + first = NEXT_INSN (first); + if (!first) + return 0; + + combine_attempts = 0; + combine_merges = 0; + combine_extras = 0; + combine_successes = 0; + + rtl_hooks = combine_rtl_hooks; + + reg_stat.safe_grow_cleared (nregs, true); + + init_recog_no_volatile (); + + /* Allocate array for insn info. */ + max_uid_known = get_max_uid (); + uid_log_links = XCNEWVEC (struct insn_link *, max_uid_known + 1); + uid_insn_cost = XCNEWVEC (int, max_uid_known + 1); + gcc_obstack_init (&insn_link_obstack); + + nonzero_bits_mode = int_mode_for_size (HOST_BITS_PER_WIDE_INT, 0).require (); + + /* Don't use reg_stat[].nonzero_bits when computing it. This can cause + problems when, for example, we have j <<= 1 in a loop. */ + + nonzero_sign_valid = 0; + label_tick = label_tick_ebb_start = 1; + + /* Scan all SETs and see if we can deduce anything about what + bits are known to be zero for some registers and how many copies + of the sign bit are known to exist for those registers. + + Also set any known values so that we can use it while searching + for what bits are known to be set. */ + + setup_incoming_promotions (first); + /* Allow the entry block and the first block to fall into the same EBB. + Conceptually the incoming promotions are assigned to the entry block. */ + last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + + create_log_links (); + FOR_EACH_BB_FN (this_basic_block, cfun) + { + optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); + last_call_luid = 0; + mem_last_set = -1; + + label_tick++; + if (!single_pred_p (this_basic_block) + || single_pred (this_basic_block) != last_bb) + label_tick_ebb_start = label_tick; + last_bb = this_basic_block; + + FOR_BB_INSNS (this_basic_block, insn) + if (INSN_P (insn) && BLOCK_FOR_INSN (insn)) + { + rtx links; + + subst_low_luid = DF_INSN_LUID (insn); + subst_insn = insn; + + note_stores (insn, set_nonzero_bits_and_sign_copies, insn); + record_dead_and_set_regs (insn); + + if (AUTO_INC_DEC) + for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) + if (REG_NOTE_KIND (links) == REG_INC) + set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX, + insn); + + /* Record the current insn_cost of this instruction. */ + INSN_COST (insn) = insn_cost (insn, optimize_this_for_speed_p); + if (dump_file) + { + fprintf (dump_file, "insn_cost %d for ", INSN_COST (insn)); + dump_insn_slim (dump_file, insn); + } + } + } + + nonzero_sign_valid = 1; + + /* Now scan all the insns in forward order. */ + label_tick = label_tick_ebb_start = 1; + init_reg_last (); + setup_incoming_promotions (first); + last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + int max_combine = param_max_combine_insns; + + FOR_EACH_BB_FN (this_basic_block, cfun) + { + rtx_insn *last_combined_insn = NULL; + + /* Ignore instruction combination in basic blocks that are going to + be removed as unreachable anyway. See PR82386. */ + if (EDGE_COUNT (this_basic_block->preds) == 0) + continue; + + optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); + last_call_luid = 0; + mem_last_set = -1; + + label_tick++; + if (!single_pred_p (this_basic_block) + || single_pred (this_basic_block) != last_bb) + label_tick_ebb_start = label_tick; + last_bb = this_basic_block; + + rtl_profile_for_bb (this_basic_block); + for (insn = BB_HEAD (this_basic_block); + insn != NEXT_INSN (BB_END (this_basic_block)); + insn = next ? next : NEXT_INSN (insn)) + { + next = 0; + if (!NONDEBUG_INSN_P (insn)) + continue; + + while (last_combined_insn + && (!NONDEBUG_INSN_P (last_combined_insn) + || last_combined_insn->deleted ())) + last_combined_insn = PREV_INSN (last_combined_insn); + if (last_combined_insn == NULL_RTX + || BLOCK_FOR_INSN (last_combined_insn) != this_basic_block + || DF_INSN_LUID (last_combined_insn) <= DF_INSN_LUID (insn)) + last_combined_insn = insn; + + /* See if we know about function return values before this + insn based upon SUBREG flags. */ + check_promoted_subreg (insn, PATTERN (insn)); + + /* See if we can find hardregs and subreg of pseudos in + narrower modes. This could help turning TRUNCATEs + into SUBREGs. */ + note_uses (&PATTERN (insn), record_truncated_values, NULL); + + /* Try this insn with each insn it links back to. */ + + FOR_EACH_LOG_LINK (links, insn) + if ((next = try_combine (insn, links->insn, NULL, + NULL, &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "two-insn combine", 1); + goto retry; + } + + /* Try each sequence of three linked insns ending with this one. */ + + if (max_combine >= 3) + FOR_EACH_LOG_LINK (links, insn) + { + rtx_insn *link = links->insn; + + /* If the linked insn has been replaced by a note, then there + is no point in pursuing this chain any further. */ + if (NOTE_P (link)) + continue; + + FOR_EACH_LOG_LINK (nextlinks, link) + if ((next = try_combine (insn, link, nextlinks->insn, + NULL, &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "three-insn combine", 1); + goto retry; + } + } + + /* Try combining an insn with two different insns whose results it + uses. */ + if (max_combine >= 3) + FOR_EACH_LOG_LINK (links, insn) + for (nextlinks = links->next; nextlinks; + nextlinks = nextlinks->next) + if ((next = try_combine (insn, links->insn, + nextlinks->insn, NULL, + &new_direct_jump_p, + last_combined_insn)) != 0) + + { + statistics_counter_event (cfun, "three-insn combine", 1); + goto retry; + } + + /* Try four-instruction combinations. */ + if (max_combine >= 4) + FOR_EACH_LOG_LINK (links, insn) + { + struct insn_link *next1; + rtx_insn *link = links->insn; + + /* If the linked insn has been replaced by a note, then there + is no point in pursuing this chain any further. */ + if (NOTE_P (link)) + continue; + + FOR_EACH_LOG_LINK (next1, link) + { + rtx_insn *link1 = next1->insn; + if (NOTE_P (link1)) + continue; + /* I0 -> I1 -> I2 -> I3. */ + FOR_EACH_LOG_LINK (nextlinks, link1) + if ((next = try_combine (insn, link, link1, + nextlinks->insn, + &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "four-insn combine", 1); + goto retry; + } + /* I0, I1 -> I2, I2 -> I3. */ + for (nextlinks = next1->next; nextlinks; + nextlinks = nextlinks->next) + if ((next = try_combine (insn, link, link1, + nextlinks->insn, + &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "four-insn combine", 1); + goto retry; + } + } + + for (next1 = links->next; next1; next1 = next1->next) + { + rtx_insn *link1 = next1->insn; + if (NOTE_P (link1)) + continue; + /* I0 -> I2; I1, I2 -> I3. */ + FOR_EACH_LOG_LINK (nextlinks, link) + if ((next = try_combine (insn, link, link1, + nextlinks->insn, + &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "four-insn combine", 1); + goto retry; + } + /* I0 -> I1; I1, I2 -> I3. */ + FOR_EACH_LOG_LINK (nextlinks, link1) + if ((next = try_combine (insn, link, link1, + nextlinks->insn, + &new_direct_jump_p, + last_combined_insn)) != 0) + { + statistics_counter_event (cfun, "four-insn combine", 1); + goto retry; + } + } + } + + /* Try this insn with each REG_EQUAL note it links back to. */ + FOR_EACH_LOG_LINK (links, insn) + { + rtx set, note; + rtx_insn *temp = links->insn; + if ((set = single_set (temp)) != 0 + && (note = find_reg_equal_equiv_note (temp)) != 0 + && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST + && ! side_effects_p (SET_SRC (set)) + /* Avoid using a register that may already been marked + dead by an earlier instruction. */ + && ! unmentioned_reg_p (note, SET_SRC (set)) + && (GET_MODE (note) == VOIDmode + ? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set))) + : (GET_MODE (SET_DEST (set)) == GET_MODE (note) + && (GET_CODE (SET_DEST (set)) != ZERO_EXTRACT + || (GET_MODE (XEXP (SET_DEST (set), 0)) + == GET_MODE (note)))))) + { + /* Temporarily replace the set's source with the + contents of the REG_EQUAL note. The insn will + be deleted or recognized by try_combine. */ + rtx orig_src = SET_SRC (set); + rtx orig_dest = SET_DEST (set); + if (GET_CODE (SET_DEST (set)) == ZERO_EXTRACT) + SET_DEST (set) = XEXP (SET_DEST (set), 0); + SET_SRC (set) = note; + i2mod = temp; + i2mod_old_rhs = copy_rtx (orig_src); + i2mod_new_rhs = copy_rtx (note); + next = try_combine (insn, i2mod, NULL, NULL, + &new_direct_jump_p, + last_combined_insn); + i2mod = NULL; + if (next) + { + statistics_counter_event (cfun, "insn-with-note combine", 1); + goto retry; + } + SET_SRC (set) = orig_src; + SET_DEST (set) = orig_dest; + } + } + + if (!NOTE_P (insn)) + record_dead_and_set_regs (insn); + +retry: + ; + } + } + + default_rtl_profile (); + clear_bb_flags (); + new_direct_jump_p |= purge_all_dead_edges (); + new_direct_jump_p |= delete_noop_moves (); + + /* Clean up. */ + obstack_free (&insn_link_obstack, NULL); + free (uid_log_links); + free (uid_insn_cost); + reg_stat.release (); + + { + struct undo *undo, *next; + for (undo = undobuf.frees; undo; undo = next) + { + next = undo->next; + free (undo); + } + undobuf.frees = 0; + } + + total_attempts += combine_attempts; + total_merges += combine_merges; + total_extras += combine_extras; + total_successes += combine_successes; + + nonzero_sign_valid = 0; + rtl_hooks = general_rtl_hooks; + + /* Make recognizer allow volatile MEMs again. */ + init_recog (); + + return new_direct_jump_p; +} + +/* Wipe the last_xxx fields of reg_stat in preparation for another pass. */ + +static void +init_reg_last (void) +{ + unsigned int i; + reg_stat_type *p; + + FOR_EACH_VEC_ELT (reg_stat, i, p) + memset (p, 0, offsetof (reg_stat_type, sign_bit_copies)); +} + +/* Set up any promoted values for incoming argument registers. */ + +static void +setup_incoming_promotions (rtx_insn *first) +{ + tree arg; + bool strictly_local = false; + + for (arg = DECL_ARGUMENTS (current_function_decl); arg; + arg = DECL_CHAIN (arg)) + { + rtx x, reg = DECL_INCOMING_RTL (arg); + int uns1, uns3; + machine_mode mode1, mode2, mode3, mode4; + + /* Only continue if the incoming argument is in a register. */ + if (!REG_P (reg)) + continue; + + /* Determine, if possible, whether all call sites of the current + function lie within the current compilation unit. (This does + take into account the exporting of a function via taking its + address, and so forth.) */ + strictly_local + = cgraph_node::local_info_node (current_function_decl)->local; + + /* The mode and signedness of the argument before any promotions happen + (equal to the mode of the pseudo holding it at that stage). */ + mode1 = TYPE_MODE (TREE_TYPE (arg)); + uns1 = TYPE_UNSIGNED (TREE_TYPE (arg)); + + /* The mode and signedness of the argument after any source language and + TARGET_PROMOTE_PROTOTYPES-driven promotions. */ + mode2 = TYPE_MODE (DECL_ARG_TYPE (arg)); + uns3 = TYPE_UNSIGNED (DECL_ARG_TYPE (arg)); + + /* The mode and signedness of the argument as it is actually passed, + see assign_parm_setup_reg in function.c. */ + mode3 = promote_function_mode (TREE_TYPE (arg), mode1, &uns3, + TREE_TYPE (cfun->decl), 0); + + /* The mode of the register in which the argument is being passed. */ + mode4 = GET_MODE (reg); + + /* Eliminate sign extensions in the callee when: + (a) A mode promotion has occurred; */ + if (mode1 == mode3) + continue; + /* (b) The mode of the register is the same as the mode of + the argument as it is passed; */ + if (mode3 != mode4) + continue; + /* (c) There's no language level extension; */ + if (mode1 == mode2) + ; + /* (c.1) All callers are from the current compilation unit. If that's + the case we don't have to rely on an ABI, we only have to know + what we're generating right now, and we know that we will do the + mode1 to mode2 promotion with the given sign. */ + else if (!strictly_local) + continue; + /* (c.2) The combination of the two promotions is useful. This is + true when the signs match, or if the first promotion is unsigned. + In the later case, (sign_extend (zero_extend x)) is the same as + (zero_extend (zero_extend x)), so make sure to force UNS3 true. */ + else if (uns1) + uns3 = true; + else if (uns3) + continue; + + /* Record that the value was promoted from mode1 to mode3, + so that any sign extension at the head of the current + function may be eliminated. */ + x = gen_rtx_CLOBBER (mode1, const0_rtx); + x = gen_rtx_fmt_e ((uns3 ? ZERO_EXTEND : SIGN_EXTEND), mode3, x); + record_value_for_reg (reg, first, x); + } +} + +/* If MODE has a precision lower than PREC and SRC is a non-negative constant + that would appear negative in MODE, sign-extend SRC for use in nonzero_bits + because some machines (maybe most) will actually do the sign-extension and + this is the conservative approach. + + ??? For 2.5, try to tighten up the MD files in this regard instead of this + kludge. */ + +static rtx +sign_extend_short_imm (rtx src, machine_mode mode, unsigned int prec) +{ + scalar_int_mode int_mode; + if (CONST_INT_P (src) + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) < prec + && INTVAL (src) > 0 + && val_signbit_known_set_p (int_mode, INTVAL (src))) + src = GEN_INT (INTVAL (src) | ~GET_MODE_MASK (int_mode)); + + return src; +} + +/* Update RSP for pseudo-register X from INSN's REG_EQUAL note (if one exists) + and SET. */ + +static void +update_rsp_from_reg_equal (reg_stat_type *rsp, rtx_insn *insn, const_rtx set, + rtx x) +{ + rtx reg_equal_note = insn ? find_reg_equal_equiv_note (insn) : NULL_RTX; + unsigned HOST_WIDE_INT bits = 0; + rtx reg_equal = NULL, src = SET_SRC (set); + unsigned int num = 0; + + if (reg_equal_note) + reg_equal = XEXP (reg_equal_note, 0); + + if (SHORT_IMMEDIATES_SIGN_EXTEND) + { + src = sign_extend_short_imm (src, GET_MODE (x), BITS_PER_WORD); + if (reg_equal) + reg_equal = sign_extend_short_imm (reg_equal, GET_MODE (x), BITS_PER_WORD); + } + + /* Don't call nonzero_bits if it cannot change anything. */ + if (rsp->nonzero_bits != HOST_WIDE_INT_M1U) + { + machine_mode mode = GET_MODE (x); + if (GET_MODE_CLASS (mode) == MODE_INT + && HWI_COMPUTABLE_MODE_P (mode)) + mode = nonzero_bits_mode; + bits = nonzero_bits (src, mode); + if (reg_equal && bits) + bits &= nonzero_bits (reg_equal, mode); + rsp->nonzero_bits |= bits; + } + + /* Don't call num_sign_bit_copies if it cannot change anything. */ + if (rsp->sign_bit_copies != 1) + { + num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x)); + if (reg_equal && maybe_ne (num, GET_MODE_PRECISION (GET_MODE (x)))) + { + unsigned int numeq = num_sign_bit_copies (reg_equal, GET_MODE (x)); + if (num == 0 || numeq > num) + num = numeq; + } + if (rsp->sign_bit_copies == 0 || num < rsp->sign_bit_copies) + rsp->sign_bit_copies = num; + } +} + +/* Called via note_stores. If X is a pseudo that is narrower than + HOST_BITS_PER_WIDE_INT and is being set, record what bits are known zero. + + If we are setting only a portion of X and we can't figure out what + portion, assume all bits will be used since we don't know what will + be happening. + + Similarly, set how many bits of X are known to be copies of the sign bit + at all locations in the function. This is the smallest number implied + by any set of X. */ + +static void +set_nonzero_bits_and_sign_copies (rtx x, const_rtx set, void *data) +{ + rtx_insn *insn = (rtx_insn *) data; + scalar_int_mode mode; + + if (REG_P (x) + && REGNO (x) >= FIRST_PSEUDO_REGISTER + /* If this register is undefined at the start of the file, we can't + say what its contents were. */ + && ! REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), REGNO (x)) + && is_a <scalar_int_mode> (GET_MODE (x), &mode) + && HWI_COMPUTABLE_MODE_P (mode)) + { + reg_stat_type *rsp = ®_stat[REGNO (x)]; + + if (set == 0 || GET_CODE (set) == CLOBBER) + { + rsp->nonzero_bits = GET_MODE_MASK (mode); + rsp->sign_bit_copies = 1; + return; + } + + /* If this register is being initialized using itself, and the + register is uninitialized in this basic block, and there are + no LOG_LINKS which set the register, then part of the + register is uninitialized. In that case we can't assume + anything about the number of nonzero bits. + + ??? We could do better if we checked this in + reg_{nonzero_bits,num_sign_bit_copies}_for_combine. Then we + could avoid making assumptions about the insn which initially + sets the register, while still using the information in other + insns. We would have to be careful to check every insn + involved in the combination. */ + + if (insn + && reg_referenced_p (x, PATTERN (insn)) + && !REGNO_REG_SET_P (DF_LR_IN (BLOCK_FOR_INSN (insn)), + REGNO (x))) + { + struct insn_link *link; + + FOR_EACH_LOG_LINK (link, insn) + if (dead_or_set_p (link->insn, x)) + break; + if (!link) + { + rsp->nonzero_bits = GET_MODE_MASK (mode); + rsp->sign_bit_copies = 1; + return; + } + } + + /* If this is a complex assignment, see if we can convert it into a + simple assignment. */ + set = expand_field_assignment (set); + + /* If this is a simple assignment, or we have a paradoxical SUBREG, + set what we know about X. */ + + if (SET_DEST (set) == x + || (paradoxical_subreg_p (SET_DEST (set)) + && SUBREG_REG (SET_DEST (set)) == x)) + update_rsp_from_reg_equal (rsp, insn, set, x); + else + { + rsp->nonzero_bits = GET_MODE_MASK (mode); + rsp->sign_bit_copies = 1; + } + } +} + +/* See if INSN can be combined into I3. PRED, PRED2, SUCC and SUCC2 are + optionally insns that were previously combined into I3 or that will be + combined into the merger of INSN and I3. The order is PRED, PRED2, + INSN, SUCC, SUCC2, I3. + + Return 0 if the combination is not allowed for any reason. + + If the combination is allowed, *PDEST will be set to the single + destination of INSN and *PSRC to the single source, and this function + will return 1. */ + +static int +can_combine_p (rtx_insn *insn, rtx_insn *i3, rtx_insn *pred ATTRIBUTE_UNUSED, + rtx_insn *pred2 ATTRIBUTE_UNUSED, rtx_insn *succ, rtx_insn *succ2, + rtx *pdest, rtx *psrc) +{ + int i; + const_rtx set = 0; + rtx src, dest; + rtx_insn *p; + rtx link; + bool all_adjacent = true; + int (*is_volatile_p) (const_rtx); + + if (succ) + { + if (succ2) + { + if (next_active_insn (succ2) != i3) + all_adjacent = false; + if (next_active_insn (succ) != succ2) + all_adjacent = false; + } + else if (next_active_insn (succ) != i3) + all_adjacent = false; + if (next_active_insn (insn) != succ) + all_adjacent = false; + } + else if (next_active_insn (insn) != i3) + all_adjacent = false; + + /* Can combine only if previous insn is a SET of a REG or a SUBREG, + or a PARALLEL consisting of such a SET and CLOBBERs. + + If INSN has CLOBBER parallel parts, ignore them for our processing. + By definition, these happen during the execution of the insn. When it + is merged with another insn, all bets are off. If they are, in fact, + needed and aren't also supplied in I3, they may be added by + recog_for_combine. Otherwise, it won't match. + + We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED + note. + + Get the source and destination of INSN. If more than one, can't + combine. */ + + if (GET_CODE (PATTERN (insn)) == SET) + set = PATTERN (insn); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + { + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx elt = XVECEXP (PATTERN (insn), 0, i); + + switch (GET_CODE (elt)) + { + /* This is important to combine floating point insns + for the SH4 port. */ + case USE: + /* Combining an isolated USE doesn't make sense. + We depend here on combinable_i3pat to reject them. */ + /* The code below this loop only verifies that the inputs of + the SET in INSN do not change. We call reg_set_between_p + to verify that the REG in the USE does not change between + I3 and INSN. + If the USE in INSN was for a pseudo register, the matching + insn pattern will likely match any register; combining this + with any other USE would only be safe if we knew that the + used registers have identical values, or if there was + something to tell them apart, e.g. different modes. For + now, we forgo such complicated tests and simply disallow + combining of USES of pseudo registers with any other USE. */ + if (REG_P (XEXP (elt, 0)) + && GET_CODE (PATTERN (i3)) == PARALLEL) + { + rtx i3pat = PATTERN (i3); + int i = XVECLEN (i3pat, 0) - 1; + unsigned int regno = REGNO (XEXP (elt, 0)); + + do + { + rtx i3elt = XVECEXP (i3pat, 0, i); + + if (GET_CODE (i3elt) == USE + && REG_P (XEXP (i3elt, 0)) + && (REGNO (XEXP (i3elt, 0)) == regno + ? reg_set_between_p (XEXP (elt, 0), + PREV_INSN (insn), i3) + : regno >= FIRST_PSEUDO_REGISTER)) + return 0; + } + while (--i >= 0); + } + break; + + /* We can ignore CLOBBERs. */ + case CLOBBER: + break; + + case SET: + /* Ignore SETs whose result isn't used but not those that + have side-effects. */ + if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt)) + && insn_nothrow_p (insn) + && !side_effects_p (elt)) + break; + + /* If we have already found a SET, this is a second one and + so we cannot combine with this insn. */ + if (set) + return 0; + + set = elt; + break; + + default: + /* Anything else means we can't combine. */ + return 0; + } + } + + if (set == 0 + /* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs, + so don't do anything with it. */ + || GET_CODE (SET_SRC (set)) == ASM_OPERANDS) + return 0; + } + else + return 0; + + if (set == 0) + return 0; + + /* The simplification in expand_field_assignment may call back to + get_last_value, so set safe guard here. */ + subst_low_luid = DF_INSN_LUID (insn); + + set = expand_field_assignment (set); + src = SET_SRC (set), dest = SET_DEST (set); + + /* Do not eliminate user-specified register if it is in an + asm input because we may break the register asm usage defined + in GCC manual if allow to do so. + Be aware that this may cover more cases than we expect but this + should be harmless. */ + if (REG_P (dest) && REG_USERVAR_P (dest) && HARD_REGISTER_P (dest) + && extract_asm_operands (PATTERN (i3))) + return 0; + + /* Don't eliminate a store in the stack pointer. */ + if (dest == stack_pointer_rtx + /* Don't combine with an insn that sets a register to itself if it has + a REG_EQUAL note. This may be part of a LIBCALL sequence. */ + || (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX)) + /* Can't merge an ASM_OPERANDS. */ + || GET_CODE (src) == ASM_OPERANDS + /* Can't merge a function call. */ + || GET_CODE (src) == CALL + /* Don't eliminate a function call argument. */ + || (CALL_P (i3) + && (find_reg_fusage (i3, USE, dest) + || (REG_P (dest) + && REGNO (dest) < FIRST_PSEUDO_REGISTER + && global_regs[REGNO (dest)]))) + /* Don't substitute into an incremented register. */ + || FIND_REG_INC_NOTE (i3, dest) + || (succ && FIND_REG_INC_NOTE (succ, dest)) + || (succ2 && FIND_REG_INC_NOTE (succ2, dest)) + /* Don't substitute into a non-local goto, this confuses CFG. */ + || (JUMP_P (i3) && find_reg_note (i3, REG_NON_LOCAL_GOTO, NULL_RTX)) + /* Make sure that DEST is not used after INSN but before SUCC, or + after SUCC and before SUCC2, or after SUCC2 but before I3. */ + || (!all_adjacent + && ((succ2 + && (reg_used_between_p (dest, succ2, i3) + || reg_used_between_p (dest, succ, succ2))) + || (!succ2 && succ && reg_used_between_p (dest, succ, i3)) + || (!succ2 && !succ && reg_used_between_p (dest, insn, i3)) + || (succ + /* SUCC and SUCC2 can be split halves from a PARALLEL; in + that case SUCC is not in the insn stream, so use SUCC2 + instead for this test. */ + && reg_used_between_p (dest, insn, + succ2 + && INSN_UID (succ) == INSN_UID (succ2) + ? succ2 : succ)))) + /* Make sure that the value that is to be substituted for the register + does not use any registers whose values alter in between. However, + If the insns are adjacent, a use can't cross a set even though we + think it might (this can happen for a sequence of insns each setting + the same destination; last_set of that register might point to + a NOTE). If INSN has a REG_EQUIV note, the register is always + equivalent to the memory so the substitution is valid even if there + are intervening stores. Also, don't move a volatile asm or + UNSPEC_VOLATILE across any other insns. */ + || (! all_adjacent + && (((!MEM_P (src) + || ! find_reg_note (insn, REG_EQUIV, src)) + && modified_between_p (src, insn, i3)) + || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src)) + || GET_CODE (src) == UNSPEC_VOLATILE)) + /* Don't combine across a CALL_INSN, because that would possibly + change whether the life span of some REGs crosses calls or not, + and it is a pain to update that information. + Exception: if source is a constant, moving it later can't hurt. + Accept that as a special case. */ + || (DF_INSN_LUID (insn) < last_call_luid && ! CONSTANT_P (src))) + return 0; + + /* DEST must be a REG. */ + if (REG_P (dest)) + { + /* If register alignment is being enforced for multi-word items in all + cases except for parameters, it is possible to have a register copy + insn referencing a hard register that is not allowed to contain the + mode being copied and which would not be valid as an operand of most + insns. Eliminate this problem by not combining with such an insn. + + Also, on some machines we don't want to extend the life of a hard + register. */ + + if (REG_P (src) + && ((REGNO (dest) < FIRST_PSEUDO_REGISTER + && !targetm.hard_regno_mode_ok (REGNO (dest), GET_MODE (dest))) + /* Don't extend the life of a hard register unless it is + user variable (if we have few registers) or it can't + fit into the desired register (meaning something special + is going on). + Also avoid substituting a return register into I3, because + reload can't handle a conflict with constraints of other + inputs. */ + || (REGNO (src) < FIRST_PSEUDO_REGISTER + && !targetm.hard_regno_mode_ok (REGNO (src), + GET_MODE (src))))) + return 0; + } + else + return 0; + + + if (GET_CODE (PATTERN (i3)) == PARALLEL) + for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER) + { + rtx reg = XEXP (XVECEXP (PATTERN (i3), 0, i), 0); + + /* If the clobber represents an earlyclobber operand, we must not + substitute an expression containing the clobbered register. + As we do not analyze the constraint strings here, we have to + make the conservative assumption. However, if the register is + a fixed hard reg, the clobber cannot represent any operand; + we leave it up to the machine description to either accept or + reject use-and-clobber patterns. */ + if (!REG_P (reg) + || REGNO (reg) >= FIRST_PSEUDO_REGISTER + || !fixed_regs[REGNO (reg)]) + if (reg_overlap_mentioned_p (reg, src)) + return 0; + } + + /* If INSN contains anything volatile, or is an `asm' (whether volatile + or not), reject, unless nothing volatile comes between it and I3 */ + + if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src)) + { + /* Make sure neither succ nor succ2 contains a volatile reference. */ + if (succ2 != 0 && volatile_refs_p (PATTERN (succ2))) + return 0; + if (succ != 0 && volatile_refs_p (PATTERN (succ))) + return 0; + /* We'll check insns between INSN and I3 below. */ + } + + /* If INSN is an asm, and DEST is a hard register, reject, since it has + to be an explicit register variable, and was chosen for a reason. */ + + if (GET_CODE (src) == ASM_OPERANDS + && REG_P (dest) && REGNO (dest) < FIRST_PSEUDO_REGISTER) + return 0; + + /* If INSN contains volatile references (specifically volatile MEMs), + we cannot combine across any other volatile references. + Even if INSN doesn't contain volatile references, any intervening + volatile insn might affect machine state. */ + + is_volatile_p = volatile_refs_p (PATTERN (insn)) + ? volatile_refs_p + : volatile_insn_p; + + for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) + if (INSN_P (p) && p != succ && p != succ2 && is_volatile_p (PATTERN (p))) + return 0; + + /* If INSN contains an autoincrement or autodecrement, make sure that + register is not used between there and I3, and not already used in + I3 either. Neither must it be used in PRED or SUCC, if they exist. + Also insist that I3 not be a jump if using LRA; if it were one + and the incremented register were spilled, we would lose. + Reload handles this correctly. */ + + if (AUTO_INC_DEC) + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && ((JUMP_P (i3) && targetm.lra_p ()) + || reg_used_between_p (XEXP (link, 0), insn, i3) + || (pred != NULL_RTX + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred))) + || (pred2 != NULL_RTX + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred2))) + || (succ != NULL_RTX + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ))) + || (succ2 != NULL_RTX + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ2))) + || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3)))) + return 0; + + /* If we get here, we have passed all the tests and the combination is + to be allowed. */ + + *pdest = dest; + *psrc = src; + + return 1; +} + +/* LOC is the location within I3 that contains its pattern or the component + of a PARALLEL of the pattern. We validate that it is valid for combining. + + One problem is if I3 modifies its output, as opposed to replacing it + entirely, we can't allow the output to contain I2DEST, I1DEST or I0DEST as + doing so would produce an insn that is not equivalent to the original insns. + + Consider: + + (set (reg:DI 101) (reg:DI 100)) + (set (subreg:SI (reg:DI 101) 0) <foo>) + + This is NOT equivalent to: + + (parallel [(set (subreg:SI (reg:DI 100) 0) <foo>) + (set (reg:DI 101) (reg:DI 100))]) + + Not only does this modify 100 (in which case it might still be valid + if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100. + + We can also run into a problem if I2 sets a register that I1 + uses and I1 gets directly substituted into I3 (not via I2). In that + case, we would be getting the wrong value of I2DEST into I3, so we + must reject the combination. This case occurs when I2 and I1 both + feed into I3, rather than when I1 feeds into I2, which feeds into I3. + If I1_NOT_IN_SRC is nonzero, it means that finding I1 in the source + of a SET must prevent combination from occurring. The same situation + can occur for I0, in which case I0_NOT_IN_SRC is set. + + Before doing the above check, we first try to expand a field assignment + into a set of logical operations. + + If PI3_DEST_KILLED is nonzero, it is a pointer to a location in which + we place a register that is both set and used within I3. If more than one + such register is detected, we fail. + + Return 1 if the combination is valid, zero otherwise. */ + +static int +combinable_i3pat (rtx_insn *i3, rtx *loc, rtx i2dest, rtx i1dest, rtx i0dest, + int i1_not_in_src, int i0_not_in_src, rtx *pi3dest_killed) +{ + rtx x = *loc; + + if (GET_CODE (x) == SET) + { + rtx set = x ; + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + rtx inner_dest = dest; + rtx subdest; + + while (GET_CODE (inner_dest) == STRICT_LOW_PART + || GET_CODE (inner_dest) == SUBREG + || GET_CODE (inner_dest) == ZERO_EXTRACT) + inner_dest = XEXP (inner_dest, 0); + + /* Check for the case where I3 modifies its output, as discussed + above. We don't want to prevent pseudos from being combined + into the address of a MEM, so only prevent the combination if + i1 or i2 set the same MEM. */ + if ((inner_dest != dest && + (!MEM_P (inner_dest) + || rtx_equal_p (i2dest, inner_dest) + || (i1dest && rtx_equal_p (i1dest, inner_dest)) + || (i0dest && rtx_equal_p (i0dest, inner_dest))) + && (reg_overlap_mentioned_p (i2dest, inner_dest) + || (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest)) + || (i0dest && reg_overlap_mentioned_p (i0dest, inner_dest)))) + + /* This is the same test done in can_combine_p except we can't test + all_adjacent; we don't have to, since this instruction will stay + in place, thus we are not considering increasing the lifetime of + INNER_DEST. + + Also, if this insn sets a function argument, combining it with + something that might need a spill could clobber a previous + function argument; the all_adjacent test in can_combine_p also + checks this; here, we do a more specific test for this case. */ + + || (REG_P (inner_dest) + && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER + && !targetm.hard_regno_mode_ok (REGNO (inner_dest), + GET_MODE (inner_dest))) + || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)) + || (i0_not_in_src && reg_overlap_mentioned_p (i0dest, src))) + return 0; + + /* If DEST is used in I3, it is being killed in this insn, so + record that for later. We have to consider paradoxical + subregs here, since they kill the whole register, but we + ignore partial subregs, STRICT_LOW_PART, etc. + Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the + STACK_POINTER_REGNUM, since these are always considered to be + live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ + subdest = dest; + if (GET_CODE (subdest) == SUBREG && !partial_subreg_p (subdest)) + subdest = SUBREG_REG (subdest); + if (pi3dest_killed + && REG_P (subdest) + && reg_referenced_p (subdest, PATTERN (i3)) + && REGNO (subdest) != FRAME_POINTER_REGNUM + && (HARD_FRAME_POINTER_IS_FRAME_POINTER + || REGNO (subdest) != HARD_FRAME_POINTER_REGNUM) + && (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM + || (REGNO (subdest) != ARG_POINTER_REGNUM + || ! fixed_regs [REGNO (subdest)])) + && REGNO (subdest) != STACK_POINTER_REGNUM) + { + if (*pi3dest_killed) + return 0; + + *pi3dest_killed = subdest; + } + } + + else if (GET_CODE (x) == PARALLEL) + { + int i; + + for (i = 0; i < XVECLEN (x, 0); i++) + if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest, i0dest, + i1_not_in_src, i0_not_in_src, pi3dest_killed)) + return 0; + } + + return 1; +} + +/* Return 1 if X is an arithmetic expression that contains a multiplication + and division. We don't count multiplications by powers of two here. */ + +static int +contains_muldiv (rtx x) +{ + switch (GET_CODE (x)) + { + case MOD: case DIV: case UMOD: case UDIV: + return 1; + + case MULT: + return ! (CONST_INT_P (XEXP (x, 1)) + && pow2p_hwi (UINTVAL (XEXP (x, 1)))); + default: + if (BINARY_P (x)) + return contains_muldiv (XEXP (x, 0)) + || contains_muldiv (XEXP (x, 1)); + + if (UNARY_P (x)) + return contains_muldiv (XEXP (x, 0)); + + return 0; + } +} + +/* Determine whether INSN can be used in a combination. Return nonzero if + not. This is used in try_combine to detect early some cases where we + can't perform combinations. */ + +static int +cant_combine_insn_p (rtx_insn *insn) +{ + rtx set; + rtx src, dest; + + /* If this isn't really an insn, we can't do anything. + This can occur when flow deletes an insn that it has merged into an + auto-increment address. */ + if (!NONDEBUG_INSN_P (insn)) + return 1; + + /* Never combine loads and stores involving hard regs that are likely + to be spilled. The register allocator can usually handle such + reg-reg moves by tying. If we allow the combiner to make + substitutions of likely-spilled regs, reload might die. + As an exception, we allow combinations involving fixed regs; these are + not available to the register allocator so there's no risk involved. */ + + set = single_set (insn); + if (! set) + return 0; + src = SET_SRC (set); + dest = SET_DEST (set); + if (GET_CODE (src) == SUBREG) + src = SUBREG_REG (src); + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + if (REG_P (src) && REG_P (dest) + && ((HARD_REGISTER_P (src) + && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (src)) +#ifdef LEAF_REGISTERS + && ! LEAF_REGISTERS [REGNO (src)]) +#else + ) +#endif + || (HARD_REGISTER_P (dest) + && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (dest)) + && targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (dest)))))) + return 1; + + return 0; +} + +struct likely_spilled_retval_info +{ + unsigned regno, nregs; + unsigned mask; +}; + +/* Called via note_stores by likely_spilled_retval_p. Remove from info->mask + hard registers that are known to be written to / clobbered in full. */ +static void +likely_spilled_retval_1 (rtx x, const_rtx set, void *data) +{ + struct likely_spilled_retval_info *const info = + (struct likely_spilled_retval_info *) data; + unsigned regno, nregs; + unsigned new_mask; + + if (!REG_P (XEXP (set, 0))) + return; + regno = REGNO (x); + if (regno >= info->regno + info->nregs) + return; + nregs = REG_NREGS (x); + if (regno + nregs <= info->regno) + return; + new_mask = (2U << (nregs - 1)) - 1; + if (regno < info->regno) + new_mask >>= info->regno - regno; + else + new_mask <<= regno - info->regno; + info->mask &= ~new_mask; +} + +/* Return nonzero iff part of the return value is live during INSN, and + it is likely spilled. This can happen when more than one insn is needed + to copy the return value, e.g. when we consider to combine into the + second copy insn for a complex value. */ + +static int +likely_spilled_retval_p (rtx_insn *insn) +{ + rtx_insn *use = BB_END (this_basic_block); + rtx reg; + rtx_insn *p; + unsigned regno, nregs; + /* We assume here that no machine mode needs more than + 32 hard registers when the value overlaps with a register + for which TARGET_FUNCTION_VALUE_REGNO_P is true. */ + unsigned mask; + struct likely_spilled_retval_info info; + + if (!NONJUMP_INSN_P (use) || GET_CODE (PATTERN (use)) != USE || insn == use) + return 0; + reg = XEXP (PATTERN (use), 0); + if (!REG_P (reg) || !targetm.calls.function_value_regno_p (REGNO (reg))) + return 0; + regno = REGNO (reg); + nregs = REG_NREGS (reg); + if (nregs == 1) + return 0; + mask = (2U << (nregs - 1)) - 1; + + /* Disregard parts of the return value that are set later. */ + info.regno = regno; + info.nregs = nregs; + info.mask = mask; + for (p = PREV_INSN (use); info.mask && p != insn; p = PREV_INSN (p)) + if (INSN_P (p)) + note_stores (p, likely_spilled_retval_1, &info); + mask = info.mask; + + /* Check if any of the (probably) live return value registers is + likely spilled. */ + nregs --; + do + { + if ((mask & 1 << nregs) + && targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno + nregs))) + return 1; + } while (nregs--); + return 0; +} + +/* Adjust INSN after we made a change to its destination. + + Changing the destination can invalidate notes that say something about + the results of the insn and a LOG_LINK pointing to the insn. */ + +static void +adjust_for_new_dest (rtx_insn *insn) +{ + /* For notes, be conservative and simply remove them. */ + remove_reg_equal_equiv_notes (insn, true); + + /* The new insn will have a destination that was previously the destination + of an insn just above it. Call distribute_links to make a LOG_LINK from + the next use of that destination. */ + + rtx set = single_set (insn); + gcc_assert (set); + + rtx reg = SET_DEST (set); + + while (GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART + || GET_CODE (reg) == SUBREG) + reg = XEXP (reg, 0); + gcc_assert (REG_P (reg)); + + distribute_links (alloc_insn_link (insn, REGNO (reg), NULL)); + + df_insn_rescan (insn); +} + +/* Return TRUE if combine can reuse reg X in mode MODE. + ADDED_SETS is nonzero if the original set is still required. */ +static bool +can_change_dest_mode (rtx x, int added_sets, machine_mode mode) +{ + unsigned int regno; + + if (!REG_P (x)) + return false; + + /* Don't change between modes with different underlying register sizes, + since this could lead to invalid subregs. */ + if (maybe_ne (REGMODE_NATURAL_SIZE (mode), + REGMODE_NATURAL_SIZE (GET_MODE (x)))) + return false; + + regno = REGNO (x); + /* Allow hard registers if the new mode is legal, and occupies no more + registers than the old mode. */ + if (regno < FIRST_PSEUDO_REGISTER) + return (targetm.hard_regno_mode_ok (regno, mode) + && REG_NREGS (x) >= hard_regno_nregs (regno, mode)); + + /* Or a pseudo that is only used once. */ + return (regno < reg_n_sets_max + && REG_N_SETS (regno) == 1 + && !added_sets + && !REG_USERVAR_P (x)); +} + + +/* Check whether X, the destination of a set, refers to part of + the register specified by REG. */ + +static bool +reg_subword_p (rtx x, rtx reg) +{ + /* Check that reg is an integer mode register. */ + if (!REG_P (reg) || GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT) + return false; + + if (GET_CODE (x) == STRICT_LOW_PART + || GET_CODE (x) == ZERO_EXTRACT) + x = XEXP (x, 0); + + return GET_CODE (x) == SUBREG + && SUBREG_REG (x) == reg + && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT; +} + +/* Return whether PAT is a PARALLEL of exactly N register SETs followed + by an arbitrary number of CLOBBERs. */ +static bool +is_parallel_of_n_reg_sets (rtx pat, int n) +{ + if (GET_CODE (pat) != PARALLEL) + return false; + + int len = XVECLEN (pat, 0); + if (len < n) + return false; + + int i; + for (i = 0; i < n; i++) + if (GET_CODE (XVECEXP (pat, 0, i)) != SET + || !REG_P (SET_DEST (XVECEXP (pat, 0, i)))) + return false; + for ( ; i < len; i++) + switch (GET_CODE (XVECEXP (pat, 0, i))) + { + case CLOBBER: + if (XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx) + return false; + break; + default: + return false; + } + return true; +} + +/* Return whether INSN, a PARALLEL of N register SETs (and maybe some + CLOBBERs), can be split into individual SETs in that order, without + changing semantics. */ +static bool +can_split_parallel_of_n_reg_sets (rtx_insn *insn, int n) +{ + if (!insn_nothrow_p (insn)) + return false; + + rtx pat = PATTERN (insn); + + int i, j; + for (i = 0; i < n; i++) + { + if (side_effects_p (SET_SRC (XVECEXP (pat, 0, i)))) + return false; + + rtx reg = SET_DEST (XVECEXP (pat, 0, i)); + + for (j = i + 1; j < n; j++) + if (reg_referenced_p (reg, XVECEXP (pat, 0, j))) + return false; + } + + return true; +} + +/* Return whether X is just a single_set, with the source + a general_operand. */ +static bool +is_just_move (rtx_insn *x) +{ + rtx set = single_set (x); + if (!set) + return false; + + return general_operand (SET_SRC (set), VOIDmode); +} + +/* Callback function to count autoincs. */ + +static int +count_auto_inc (rtx, rtx, rtx, rtx, rtx, void *arg) +{ + (*((int *) arg))++; + + return 0; +} + +/* Try to combine the insns I0, I1 and I2 into I3. + Here I0, I1 and I2 appear earlier than I3. + I0 and I1 can be zero; then we combine just I2 into I3, or I1 and I2 into + I3. + + If we are combining more than two insns and the resulting insn is not + recognized, try splitting it into two insns. If that happens, I2 and I3 + are retained and I1/I0 are pseudo-deleted by turning them into a NOTE. + Otherwise, I0, I1 and I2 are pseudo-deleted. + + Return 0 if the combination does not work. Then nothing is changed. + If we did the combination, return the insn at which combine should + resume scanning. + + Set NEW_DIRECT_JUMP_P to a nonzero value if try_combine creates a + new direct jump instruction. + + LAST_COMBINED_INSN is either I3, or some insn after I3 that has + been I3 passed to an earlier try_combine within the same basic + block. */ + +static rtx_insn * +try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, + int *new_direct_jump_p, rtx_insn *last_combined_insn) +{ + /* New patterns for I3 and I2, respectively. */ + rtx newpat, newi2pat = 0; + rtvec newpat_vec_with_clobbers = 0; + int substed_i2 = 0, substed_i1 = 0, substed_i0 = 0; + /* Indicates need to preserve SET in I0, I1 or I2 in I3 if it is not + dead. */ + int added_sets_0, added_sets_1, added_sets_2; + /* Total number of SETs to put into I3. */ + int total_sets; + /* Nonzero if I2's or I1's body now appears in I3. */ + int i2_is_used = 0, i1_is_used = 0; + /* INSN_CODEs for new I3, new I2, and user of condition code. */ + int insn_code_number, i2_code_number = 0, other_code_number = 0; + /* Contains I3 if the destination of I3 is used in its source, which means + that the old life of I3 is being killed. If that usage is placed into + I2 and not in I3, a REG_DEAD note must be made. */ + rtx i3dest_killed = 0; + /* SET_DEST and SET_SRC of I2, I1 and I0. */ + rtx i2dest = 0, i2src = 0, i1dest = 0, i1src = 0, i0dest = 0, i0src = 0; + /* Copy of SET_SRC of I1 and I0, if needed. */ + rtx i1src_copy = 0, i0src_copy = 0, i0src_copy2 = 0; + /* Set if I2DEST was reused as a scratch register. */ + bool i2scratch = false; + /* The PATTERNs of I0, I1, and I2, or a copy of them in certain cases. */ + rtx i0pat = 0, i1pat = 0, i2pat = 0; + /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */ + int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0; + int i0dest_in_i0src = 0, i1dest_in_i0src = 0, i2dest_in_i0src = 0; + int i2dest_killed = 0, i1dest_killed = 0, i0dest_killed = 0; + int i1_feeds_i2_n = 0, i0_feeds_i2_n = 0, i0_feeds_i1_n = 0; + /* Notes that must be added to REG_NOTES in I3 and I2. */ + rtx new_i3_notes, new_i2_notes; + /* Notes that we substituted I3 into I2 instead of the normal case. */ + int i3_subst_into_i2 = 0; + /* Notes that I1, I2 or I3 is a MULT operation. */ + int have_mult = 0; + int swap_i2i3 = 0; + int split_i2i3 = 0; + int changed_i3_dest = 0; + bool i2_was_move = false, i3_was_move = false; + int n_auto_inc = 0; + + int maxreg; + rtx_insn *temp_insn; + rtx temp_expr; + struct insn_link *link; + rtx other_pat = 0; + rtx new_other_notes; + int i; + scalar_int_mode dest_mode, temp_mode; + + /* Immediately return if any of I0,I1,I2 are the same insn (I3 can + never be). */ + if (i1 == i2 || i0 == i2 || (i0 && i0 == i1)) + return 0; + + /* Only try four-insn combinations when there's high likelihood of + success. Look for simple insns, such as loads of constants or + binary operations involving a constant. */ + if (i0) + { + int i; + int ngood = 0; + int nshift = 0; + rtx set0, set3; + + if (!flag_expensive_optimizations) + return 0; + + for (i = 0; i < 4; i++) + { + rtx_insn *insn = i == 0 ? i0 : i == 1 ? i1 : i == 2 ? i2 : i3; + rtx set = single_set (insn); + rtx src; + if (!set) + continue; + src = SET_SRC (set); + if (CONSTANT_P (src)) + { + ngood += 2; + break; + } + else if (BINARY_P (src) && CONSTANT_P (XEXP (src, 1))) + ngood++; + else if (GET_CODE (src) == ASHIFT || GET_CODE (src) == ASHIFTRT + || GET_CODE (src) == LSHIFTRT) + nshift++; + } + + /* If I0 loads a memory and I3 sets the same memory, then I1 and I2 + are likely manipulating its value. Ideally we'll be able to combine + all four insns into a bitfield insertion of some kind. + + Note the source in I0 might be inside a sign/zero extension and the + memory modes in I0 and I3 might be different. So extract the address + from the destination of I3 and search for it in the source of I0. + + In the event that there's a match but the source/dest do not actually + refer to the same memory, the worst that happens is we try some + combinations that we wouldn't have otherwise. */ + if ((set0 = single_set (i0)) + /* Ensure the source of SET0 is a MEM, possibly buried inside + an extension. */ + && (GET_CODE (SET_SRC (set0)) == MEM + || ((GET_CODE (SET_SRC (set0)) == ZERO_EXTEND + || GET_CODE (SET_SRC (set0)) == SIGN_EXTEND) + && GET_CODE (XEXP (SET_SRC (set0), 0)) == MEM)) + && (set3 = single_set (i3)) + /* Ensure the destination of SET3 is a MEM. */ + && GET_CODE (SET_DEST (set3)) == MEM + /* Would it be better to extract the base address for the MEM + in SET3 and look for that? I don't have cases where it matters + but I could envision such cases. */ + && rtx_referenced_p (XEXP (SET_DEST (set3), 0), SET_SRC (set0))) + ngood += 2; + + if (ngood < 2 && nshift < 2) + return 0; + } + + /* Exit early if one of the insns involved can't be used for + combinations. */ + if (CALL_P (i2) + || (i1 && CALL_P (i1)) + || (i0 && CALL_P (i0)) + || cant_combine_insn_p (i3) + || cant_combine_insn_p (i2) + || (i1 && cant_combine_insn_p (i1)) + || (i0 && cant_combine_insn_p (i0)) + || likely_spilled_retval_p (i3)) + return 0; + + combine_attempts++; + undobuf.other_insn = 0; + + /* Reset the hard register usage information. */ + CLEAR_HARD_REG_SET (newpat_used_regs); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + if (i0) + fprintf (dump_file, "\nTrying %d, %d, %d -> %d:\n", + INSN_UID (i0), INSN_UID (i1), INSN_UID (i2), INSN_UID (i3)); + else if (i1) + fprintf (dump_file, "\nTrying %d, %d -> %d:\n", + INSN_UID (i1), INSN_UID (i2), INSN_UID (i3)); + else + fprintf (dump_file, "\nTrying %d -> %d:\n", + INSN_UID (i2), INSN_UID (i3)); + + if (i0) + dump_insn_slim (dump_file, i0); + if (i1) + dump_insn_slim (dump_file, i1); + dump_insn_slim (dump_file, i2); + dump_insn_slim (dump_file, i3); + } + + /* If multiple insns feed into one of I2 or I3, they can be in any + order. To simplify the code below, reorder them in sequence. */ + if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i2)) + std::swap (i0, i2); + if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i1)) + std::swap (i0, i1); + if (i1 && DF_INSN_LUID (i1) > DF_INSN_LUID (i2)) + std::swap (i1, i2); + + added_links_insn = 0; + added_notes_insn = 0; + + /* First check for one important special case that the code below will + not handle. Namely, the case where I1 is zero, I2 is a PARALLEL + and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case, + we may be able to replace that destination with the destination of I3. + This occurs in the common code where we compute both a quotient and + remainder into a structure, in which case we want to do the computation + directly into the structure to avoid register-register copies. + + Note that this case handles both multiple sets in I2 and also cases + where I2 has a number of CLOBBERs inside the PARALLEL. + + We make very conservative checks below and only try to handle the + most common cases of this. For example, we only handle the case + where I2 and I3 are adjacent to avoid making difficult register + usage tests. */ + + if (i1 == 0 && NONJUMP_INSN_P (i3) && GET_CODE (PATTERN (i3)) == SET + && REG_P (SET_SRC (PATTERN (i3))) + && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER + && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3))) + && GET_CODE (PATTERN (i2)) == PARALLEL + && ! side_effects_p (SET_DEST (PATTERN (i3))) + /* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code + below would need to check what is inside (and reg_overlap_mentioned_p + doesn't support those codes anyway). Don't allow those destinations; + the resulting insn isn't likely to be recognized anyway. */ + && GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART + && ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)), + SET_DEST (PATTERN (i3))) + && next_active_insn (i2) == i3) + { + rtx p2 = PATTERN (i2); + + /* Make sure that the destination of I3, + which we are going to substitute into one output of I2, + is not used within another output of I2. We must avoid making this: + (parallel [(set (mem (reg 69)) ...) + (set (reg 69) ...)]) + which is not well-defined as to order of actions. + (Besides, reload can't handle output reloads for this.) + + The problem can also happen if the dest of I3 is a memory ref, + if another dest in I2 is an indirect memory ref. + + Neither can this PARALLEL be an asm. We do not allow combining + that usually (see can_combine_p), so do not here either. */ + bool ok = true; + for (i = 0; ok && i < XVECLEN (p2, 0); i++) + { + if ((GET_CODE (XVECEXP (p2, 0, i)) == SET + || GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER) + && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)), + SET_DEST (XVECEXP (p2, 0, i)))) + ok = false; + else if (GET_CODE (XVECEXP (p2, 0, i)) == SET + && GET_CODE (SET_SRC (XVECEXP (p2, 0, i))) == ASM_OPERANDS) + ok = false; + } + + if (ok) + for (i = 0; i < XVECLEN (p2, 0); i++) + if (GET_CODE (XVECEXP (p2, 0, i)) == SET + && SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3))) + { + combine_merges++; + + subst_insn = i3; + subst_low_luid = DF_INSN_LUID (i2); + + added_sets_2 = added_sets_1 = added_sets_0 = 0; + i2src = SET_SRC (XVECEXP (p2, 0, i)); + i2dest = SET_DEST (XVECEXP (p2, 0, i)); + i2dest_killed = dead_or_set_p (i2, i2dest); + + /* Replace the dest in I2 with our dest and make the resulting + insn the new pattern for I3. Then skip to where we validate + the pattern. Everything was set up above. */ + SUBST (SET_DEST (XVECEXP (p2, 0, i)), SET_DEST (PATTERN (i3))); + newpat = p2; + i3_subst_into_i2 = 1; + goto validate_replacement; + } + } + + /* If I2 is setting a pseudo to a constant and I3 is setting some + sub-part of it to another constant, merge them by making a new + constant. */ + if (i1 == 0 + && (temp_expr = single_set (i2)) != 0 + && is_a <scalar_int_mode> (GET_MODE (SET_DEST (temp_expr)), &temp_mode) + && CONST_SCALAR_INT_P (SET_SRC (temp_expr)) + && GET_CODE (PATTERN (i3)) == SET + && CONST_SCALAR_INT_P (SET_SRC (PATTERN (i3))) + && reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp_expr))) + { + rtx dest = SET_DEST (PATTERN (i3)); + rtx temp_dest = SET_DEST (temp_expr); + int offset = -1; + int width = 0; + + if (GET_CODE (dest) == ZERO_EXTRACT) + { + if (CONST_INT_P (XEXP (dest, 1)) + && CONST_INT_P (XEXP (dest, 2)) + && is_a <scalar_int_mode> (GET_MODE (XEXP (dest, 0)), + &dest_mode)) + { + width = INTVAL (XEXP (dest, 1)); + offset = INTVAL (XEXP (dest, 2)); + dest = XEXP (dest, 0); + if (BITS_BIG_ENDIAN) + offset = GET_MODE_PRECISION (dest_mode) - width - offset; + } + } + else + { + if (GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + if (is_a <scalar_int_mode> (GET_MODE (dest), &dest_mode)) + { + width = GET_MODE_PRECISION (dest_mode); + offset = 0; + } + } + + if (offset >= 0) + { + /* If this is the low part, we're done. */ + if (subreg_lowpart_p (dest)) + ; + /* Handle the case where inner is twice the size of outer. */ + else if (GET_MODE_PRECISION (temp_mode) + == 2 * GET_MODE_PRECISION (dest_mode)) + offset += GET_MODE_PRECISION (dest_mode); + /* Otherwise give up for now. */ + else + offset = -1; + } + + if (offset >= 0) + { + rtx inner = SET_SRC (PATTERN (i3)); + rtx outer = SET_SRC (temp_expr); + + wide_int o = wi::insert (rtx_mode_t (outer, temp_mode), + rtx_mode_t (inner, dest_mode), + offset, width); + + combine_merges++; + subst_insn = i3; + subst_low_luid = DF_INSN_LUID (i2); + added_sets_2 = added_sets_1 = added_sets_0 = 0; + i2dest = temp_dest; + i2dest_killed = dead_or_set_p (i2, i2dest); + + /* Replace the source in I2 with the new constant and make the + resulting insn the new pattern for I3. Then skip to where we + validate the pattern. Everything was set up above. */ + SUBST (SET_SRC (temp_expr), + immed_wide_int_const (o, temp_mode)); + + newpat = PATTERN (i2); + + /* The dest of I3 has been replaced with the dest of I2. */ + changed_i3_dest = 1; + goto validate_replacement; + } + } + + /* If we have no I1 and I2 looks like: + (parallel [(set (reg:CC X) (compare:CC OP (const_int 0))) + (set Y OP)]) + make up a dummy I1 that is + (set Y OP) + and change I2 to be + (set (reg:CC X) (compare:CC Y (const_int 0))) + + (We can ignore any trailing CLOBBERs.) + + This undoes a previous combination and allows us to match a branch-and- + decrement insn. */ + + if (i1 == 0 + && is_parallel_of_n_reg_sets (PATTERN (i2), 2) + && (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)))) + == MODE_CC) + && GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE + && XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx + && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0), + SET_SRC (XVECEXP (PATTERN (i2), 0, 1))) + && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) + && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3)) + { + /* We make I1 with the same INSN_UID as I2. This gives it + the same DF_INSN_LUID for value tracking. Our fake I1 will + never appear in the insn stream so giving it the same INSN_UID + as I2 will not cause a problem. */ + + i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2), + XVECEXP (PATTERN (i2), 0, 1), INSN_LOCATION (i2), + -1, NULL_RTX); + INSN_UID (i1) = INSN_UID (i2); + + SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); + SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), + SET_DEST (PATTERN (i1))); + unsigned int regno = REGNO (SET_DEST (PATTERN (i1))); + SUBST_LINK (LOG_LINKS (i2), + alloc_insn_link (i1, regno, LOG_LINKS (i2))); + } + + /* If I2 is a PARALLEL of two SETs of REGs (and perhaps some CLOBBERs), + make those two SETs separate I1 and I2 insns, and make an I0 that is + the original I1. */ + if (i0 == 0 + && is_parallel_of_n_reg_sets (PATTERN (i2), 2) + && can_split_parallel_of_n_reg_sets (i2, 2) + && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) + && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3) + && !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) + && !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3)) + { + /* If there is no I1, there is no I0 either. */ + i0 = i1; + + /* We make I1 with the same INSN_UID as I2. This gives it + the same DF_INSN_LUID for value tracking. Our fake I1 will + never appear in the insn stream so giving it the same INSN_UID + as I2 will not cause a problem. */ + + i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2), + XVECEXP (PATTERN (i2), 0, 0), INSN_LOCATION (i2), + -1, NULL_RTX); + INSN_UID (i1) = INSN_UID (i2); + + SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 1)); + } + + /* Verify that I2 and maybe I1 and I0 can be combined into I3. */ + if (!can_combine_p (i2, i3, i0, i1, NULL, NULL, &i2dest, &i2src)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Can't combine i2 into i3\n"); + undo_all (); + return 0; + } + if (i1 && !can_combine_p (i1, i3, i0, NULL, i2, NULL, &i1dest, &i1src)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Can't combine i1 into i3\n"); + undo_all (); + return 0; + } + if (i0 && !can_combine_p (i0, i3, NULL, NULL, i1, i2, &i0dest, &i0src)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Can't combine i0 into i3\n"); + undo_all (); + return 0; + } + + /* Record whether i2 and i3 are trivial moves. */ + i2_was_move = is_just_move (i2); + i3_was_move = is_just_move (i3); + + /* Record whether I2DEST is used in I2SRC and similarly for the other + cases. Knowing this will help in register status updating below. */ + i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src); + i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src); + i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src); + i0dest_in_i0src = i0 && reg_overlap_mentioned_p (i0dest, i0src); + i1dest_in_i0src = i0 && reg_overlap_mentioned_p (i1dest, i0src); + i2dest_in_i0src = i0 && reg_overlap_mentioned_p (i2dest, i0src); + i2dest_killed = dead_or_set_p (i2, i2dest); + i1dest_killed = i1 && dead_or_set_p (i1, i1dest); + i0dest_killed = i0 && dead_or_set_p (i0, i0dest); + + /* For the earlier insns, determine which of the subsequent ones they + feed. */ + i1_feeds_i2_n = i1 && insn_a_feeds_b (i1, i2); + i0_feeds_i1_n = i0 && insn_a_feeds_b (i0, i1); + i0_feeds_i2_n = (i0 && (!i0_feeds_i1_n ? insn_a_feeds_b (i0, i2) + : (!reg_overlap_mentioned_p (i1dest, i0dest) + && reg_overlap_mentioned_p (i0dest, i2src)))); + + /* Ensure that I3's pattern can be the destination of combines. */ + if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, i0dest, + i1 && i2dest_in_i1src && !i1_feeds_i2_n, + i0 && ((i2dest_in_i0src && !i0_feeds_i2_n) + || (i1dest_in_i0src && !i0_feeds_i1_n)), + &i3dest_killed)) + { + undo_all (); + return 0; + } + + /* See if any of the insns is a MULT operation. Unless one is, we will + reject a combination that is, since it must be slower. Be conservative + here. */ + if (GET_CODE (i2src) == MULT + || (i1 != 0 && GET_CODE (i1src) == MULT) + || (i0 != 0 && GET_CODE (i0src) == MULT) + || (GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == MULT)) + have_mult = 1; + + /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd. + We used to do this EXCEPT in one case: I3 has a post-inc in an + output operand. However, that exception can give rise to insns like + mov r3,(r3)+ + which is a famous insn on the PDP-11 where the value of r3 used as the + source was model-dependent. Avoid this sort of thing. */ + +#if 0 + if (!(GET_CODE (PATTERN (i3)) == SET + && REG_P (SET_SRC (PATTERN (i3))) + && MEM_P (SET_DEST (PATTERN (i3))) + && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC + || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC))) + /* It's not the exception. */ +#endif + if (AUTO_INC_DEC) + { + rtx link; + for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2)) + || (i1 != 0 + && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1))))) + { + undo_all (); + return 0; + } + } + + /* See if the SETs in I1 or I2 need to be kept around in the merged + instruction: whenever the value set there is still needed past I3. + For the SET in I2, this is easy: we see if I2DEST dies or is set in I3. + + For the SET in I1, we have two cases: if I1 and I2 independently feed + into I3, the set in I1 needs to be kept around unless I1DEST dies + or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set + in I1 needs to be kept around unless I1DEST dies or is set in either + I2 or I3. The same considerations apply to I0. */ + + added_sets_2 = !dead_or_set_p (i3, i2dest); + + if (i1) + added_sets_1 = !(dead_or_set_p (i3, i1dest) + || (i1_feeds_i2_n && dead_or_set_p (i2, i1dest))); + else + added_sets_1 = 0; + + if (i0) + added_sets_0 = !(dead_or_set_p (i3, i0dest) + || (i0_feeds_i1_n && dead_or_set_p (i1, i0dest)) + || ((i0_feeds_i2_n || (i0_feeds_i1_n && i1_feeds_i2_n)) + && dead_or_set_p (i2, i0dest))); + else + added_sets_0 = 0; + + /* We are about to copy insns for the case where they need to be kept + around. Check that they can be copied in the merged instruction. */ + + if (targetm.cannot_copy_insn_p + && ((added_sets_2 && targetm.cannot_copy_insn_p (i2)) + || (i1 && added_sets_1 && targetm.cannot_copy_insn_p (i1)) + || (i0 && added_sets_0 && targetm.cannot_copy_insn_p (i0)))) + { + undo_all (); + return 0; + } + + /* We cannot safely duplicate volatile references in any case. */ + + if ((added_sets_2 && volatile_refs_p (PATTERN (i2))) + || (added_sets_1 && volatile_refs_p (PATTERN (i1))) + || (added_sets_0 && volatile_refs_p (PATTERN (i0)))) + { + undo_all (); + return 0; + } + + /* Count how many auto_inc expressions there were in the original insns; + we need to have the same number in the resulting patterns. */ + + if (i0) + for_each_inc_dec (PATTERN (i0), count_auto_inc, &n_auto_inc); + if (i1) + for_each_inc_dec (PATTERN (i1), count_auto_inc, &n_auto_inc); + for_each_inc_dec (PATTERN (i2), count_auto_inc, &n_auto_inc); + for_each_inc_dec (PATTERN (i3), count_auto_inc, &n_auto_inc); + + /* If the set in I2 needs to be kept around, we must make a copy of + PATTERN (I2), so that when we substitute I1SRC for I1DEST in + PATTERN (I2), we are only substituting for the original I1DEST, not into + an already-substituted copy. This also prevents making self-referential + rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to + I2DEST. */ + + if (added_sets_2) + { + if (GET_CODE (PATTERN (i2)) == PARALLEL) + i2pat = gen_rtx_SET (i2dest, copy_rtx (i2src)); + else + i2pat = copy_rtx (PATTERN (i2)); + } + + if (added_sets_1) + { + if (GET_CODE (PATTERN (i1)) == PARALLEL) + i1pat = gen_rtx_SET (i1dest, copy_rtx (i1src)); + else + i1pat = copy_rtx (PATTERN (i1)); + } + + if (added_sets_0) + { + if (GET_CODE (PATTERN (i0)) == PARALLEL) + i0pat = gen_rtx_SET (i0dest, copy_rtx (i0src)); + else + i0pat = copy_rtx (PATTERN (i0)); + } + + combine_merges++; + + /* Substitute in the latest insn for the regs set by the earlier ones. */ + + maxreg = max_reg_num (); + + subst_insn = i3; + + /* Many machines have insns that can both perform an + arithmetic operation and set the condition code. These operations will + be represented as a PARALLEL with the first element of the vector + being a COMPARE of an arithmetic operation with the constant zero. + The second element of the vector will set some pseudo to the result + of the same arithmetic operation. If we simplify the COMPARE, we won't + match such a pattern and so will generate an extra insn. Here we test + for this case, where both the comparison and the operation result are + needed, and make the PARALLEL by just replacing I2DEST in I3SRC with + I2SRC. Later we will make the PARALLEL that contains I2. */ + + if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE + && CONST_INT_P (XEXP (SET_SRC (PATTERN (i3)), 1)) + && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest)) + { + rtx newpat_dest; + rtx *cc_use_loc = NULL; + rtx_insn *cc_use_insn = NULL; + rtx op0 = i2src, op1 = XEXP (SET_SRC (PATTERN (i3)), 1); + machine_mode compare_mode, orig_compare_mode; + enum rtx_code compare_code = UNKNOWN, orig_compare_code = UNKNOWN; + scalar_int_mode mode; + + newpat = PATTERN (i3); + newpat_dest = SET_DEST (newpat); + compare_mode = orig_compare_mode = GET_MODE (newpat_dest); + + if (undobuf.other_insn == 0 + && (cc_use_loc = find_single_use (SET_DEST (newpat), i3, + &cc_use_insn))) + { + compare_code = orig_compare_code = GET_CODE (*cc_use_loc); + if (is_a <scalar_int_mode> (GET_MODE (i2dest), &mode)) + compare_code = simplify_compare_const (compare_code, mode, + op0, &op1); + target_canonicalize_comparison (&compare_code, &op0, &op1, 1); + } + + /* Do the rest only if op1 is const0_rtx, which may be the + result of simplification. */ + if (op1 == const0_rtx) + { + /* If a single use of the CC is found, prepare to modify it + when SELECT_CC_MODE returns a new CC-class mode, or when + the above simplify_compare_const() returned a new comparison + operator. undobuf.other_insn is assigned the CC use insn + when modifying it. */ + if (cc_use_loc) + { +#ifdef SELECT_CC_MODE + machine_mode new_mode + = SELECT_CC_MODE (compare_code, op0, op1); + if (new_mode != orig_compare_mode + && can_change_dest_mode (SET_DEST (newpat), + added_sets_2, new_mode)) + { + unsigned int regno = REGNO (newpat_dest); + compare_mode = new_mode; + if (regno < FIRST_PSEUDO_REGISTER) + newpat_dest = gen_rtx_REG (compare_mode, regno); + else + { + SUBST_MODE (regno_reg_rtx[regno], compare_mode); + newpat_dest = regno_reg_rtx[regno]; + } + } +#endif + /* Cases for modifying the CC-using comparison. */ + if (compare_code != orig_compare_code + /* ??? Do we need to verify the zero rtx? */ + && XEXP (*cc_use_loc, 1) == const0_rtx) + { + /* Replace cc_use_loc with entire new RTX. */ + SUBST (*cc_use_loc, + gen_rtx_fmt_ee (compare_code, GET_MODE (*cc_use_loc), + newpat_dest, const0_rtx)); + undobuf.other_insn = cc_use_insn; + } + else if (compare_mode != orig_compare_mode) + { + /* Just replace the CC reg with a new mode. */ + SUBST (XEXP (*cc_use_loc, 0), newpat_dest); + undobuf.other_insn = cc_use_insn; + } + } + + /* Now we modify the current newpat: + First, SET_DEST(newpat) is updated if the CC mode has been + altered. For targets without SELECT_CC_MODE, this should be + optimized away. */ + if (compare_mode != orig_compare_mode) + SUBST (SET_DEST (newpat), newpat_dest); + /* This is always done to propagate i2src into newpat. */ + SUBST (SET_SRC (newpat), + gen_rtx_COMPARE (compare_mode, op0, op1)); + /* Create new version of i2pat if needed; the below PARALLEL + creation needs this to work correctly. */ + if (! rtx_equal_p (i2src, op0)) + i2pat = gen_rtx_SET (i2dest, op0); + i2_is_used = 1; + } + } + + if (i2_is_used == 0) + { + /* It is possible that the source of I2 or I1 may be performing + an unneeded operation, such as a ZERO_EXTEND of something + that is known to have the high part zero. Handle that case + by letting subst look at the inner insns. + + Another way to do this would be to have a function that tries + to simplify a single insn instead of merging two or more + insns. We don't do this because of the potential of infinite + loops and because of the potential extra memory required. + However, doing it the way we are is a bit of a kludge and + doesn't catch all cases. + + But only do this if -fexpensive-optimizations since it slows + things down and doesn't usually win. + + This is not done in the COMPARE case above because the + unmodified I2PAT is used in the PARALLEL and so a pattern + with a modified I2SRC would not match. */ + + if (flag_expensive_optimizations) + { + /* Pass pc_rtx so no substitutions are done, just + simplifications. */ + if (i1) + { + subst_low_luid = DF_INSN_LUID (i1); + i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0, 0); + } + + subst_low_luid = DF_INSN_LUID (i2); + i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0, 0); + } + + n_occurrences = 0; /* `subst' counts here */ + subst_low_luid = DF_INSN_LUID (i2); + + /* If I1 feeds into I2 and I1DEST is in I1SRC, we need to make a unique + copy of I2SRC each time we substitute it, in order to avoid creating + self-referential RTL when we will be substituting I1SRC for I1DEST + later. Likewise if I0 feeds into I2, either directly or indirectly + through I1, and I0DEST is in I0SRC. */ + newpat = subst (PATTERN (i3), i2dest, i2src, 0, 0, + (i1_feeds_i2_n && i1dest_in_i1src) + || ((i0_feeds_i2_n || (i0_feeds_i1_n && i1_feeds_i2_n)) + && i0dest_in_i0src)); + substed_i2 = 1; + + /* Record whether I2's body now appears within I3's body. */ + i2_is_used = n_occurrences; + } + + /* If we already got a failure, don't try to do more. Otherwise, try to + substitute I1 if we have it. */ + + if (i1 && GET_CODE (newpat) != CLOBBER) + { + /* Before we can do this substitution, we must redo the test done + above (see detailed comments there) that ensures I1DEST isn't + mentioned in any SETs in NEWPAT that are field assignments. */ + if (!combinable_i3pat (NULL, &newpat, i1dest, NULL_RTX, NULL_RTX, + 0, 0, 0)) + { + undo_all (); + return 0; + } + + n_occurrences = 0; + subst_low_luid = DF_INSN_LUID (i1); + + /* If the following substitution will modify I1SRC, make a copy of it + for the case where it is substituted for I1DEST in I2PAT later. */ + if (added_sets_2 && i1_feeds_i2_n) + i1src_copy = copy_rtx (i1src); + + /* If I0 feeds into I1 and I0DEST is in I0SRC, we need to make a unique + copy of I1SRC each time we substitute it, in order to avoid creating + self-referential RTL when we will be substituting I0SRC for I0DEST + later. */ + newpat = subst (newpat, i1dest, i1src, 0, 0, + i0_feeds_i1_n && i0dest_in_i0src); + substed_i1 = 1; + + /* Record whether I1's body now appears within I3's body. */ + i1_is_used = n_occurrences; + } + + /* Likewise for I0 if we have it. */ + + if (i0 && GET_CODE (newpat) != CLOBBER) + { + if (!combinable_i3pat (NULL, &newpat, i0dest, NULL_RTX, NULL_RTX, + 0, 0, 0)) + { + undo_all (); + return 0; + } + + /* If the following substitution will modify I0SRC, make a copy of it + for the case where it is substituted for I0DEST in I1PAT later. */ + if (added_sets_1 && i0_feeds_i1_n) + i0src_copy = copy_rtx (i0src); + /* And a copy for I0DEST in I2PAT substitution. */ + if (added_sets_2 && ((i0_feeds_i1_n && i1_feeds_i2_n) + || (i0_feeds_i2_n))) + i0src_copy2 = copy_rtx (i0src); + + n_occurrences = 0; + subst_low_luid = DF_INSN_LUID (i0); + newpat = subst (newpat, i0dest, i0src, 0, 0, 0); + substed_i0 = 1; + } + + if (n_auto_inc) + { + int new_n_auto_inc = 0; + for_each_inc_dec (newpat, count_auto_inc, &new_n_auto_inc); + + if (n_auto_inc != new_n_auto_inc) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Number of auto_inc expressions changed\n"); + undo_all (); + return 0; + } + } + + /* Fail if an autoincrement side-effect has been duplicated. Be careful + to count all the ways that I2SRC and I1SRC can be used. */ + if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0 + && i2_is_used + added_sets_2 > 1) + || (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0 + && (i1_is_used + added_sets_1 + (added_sets_2 && i1_feeds_i2_n) + > 1)) + || (i0 != 0 && FIND_REG_INC_NOTE (i0, NULL_RTX) != 0 + && (n_occurrences + added_sets_0 + + (added_sets_1 && i0_feeds_i1_n) + + (added_sets_2 && i0_feeds_i2_n) + > 1)) + /* Fail if we tried to make a new register. */ + || max_reg_num () != maxreg + /* Fail if we couldn't do something and have a CLOBBER. */ + || GET_CODE (newpat) == CLOBBER + /* Fail if this new pattern is a MULT and we didn't have one before + at the outer level. */ + || (GET_CODE (newpat) == SET && GET_CODE (SET_SRC (newpat)) == MULT + && ! have_mult)) + { + undo_all (); + return 0; + } + + /* If the actions of the earlier insns must be kept + in addition to substituting them into the latest one, + we must make a new PARALLEL for the latest insn + to hold additional the SETs. */ + + if (added_sets_0 || added_sets_1 || added_sets_2) + { + int extra_sets = added_sets_0 + added_sets_1 + added_sets_2; + combine_extras++; + + if (GET_CODE (newpat) == PARALLEL) + { + rtvec old = XVEC (newpat, 0); + total_sets = XVECLEN (newpat, 0) + extra_sets; + newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets)); + memcpy (XVEC (newpat, 0)->elem, &old->elem[0], + sizeof (old->elem[0]) * old->num_elem); + } + else + { + rtx old = newpat; + total_sets = 1 + extra_sets; + newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets)); + XVECEXP (newpat, 0, 0) = old; + } + + if (added_sets_0) + XVECEXP (newpat, 0, --total_sets) = i0pat; + + if (added_sets_1) + { + rtx t = i1pat; + if (i0_feeds_i1_n) + t = subst (t, i0dest, i0src_copy ? i0src_copy : i0src, 0, 0, 0); + + XVECEXP (newpat, 0, --total_sets) = t; + } + if (added_sets_2) + { + rtx t = i2pat; + if (i1_feeds_i2_n) + t = subst (t, i1dest, i1src_copy ? i1src_copy : i1src, 0, 0, + i0_feeds_i1_n && i0dest_in_i0src); + if ((i0_feeds_i1_n && i1_feeds_i2_n) || i0_feeds_i2_n) + t = subst (t, i0dest, i0src_copy2 ? i0src_copy2 : i0src, 0, 0, 0); + + XVECEXP (newpat, 0, --total_sets) = t; + } + } + + validate_replacement: + + /* Note which hard regs this insn has as inputs. */ + mark_used_regs_combine (newpat); + + /* If recog_for_combine fails, it strips existing clobbers. If we'll + consider splitting this pattern, we might need these clobbers. */ + if (i1 && GET_CODE (newpat) == PARALLEL + && GET_CODE (XVECEXP (newpat, 0, XVECLEN (newpat, 0) - 1)) == CLOBBER) + { + int len = XVECLEN (newpat, 0); + + newpat_vec_with_clobbers = rtvec_alloc (len); + for (i = 0; i < len; i++) + RTVEC_ELT (newpat_vec_with_clobbers, i) = XVECEXP (newpat, 0, i); + } + + /* We have recognized nothing yet. */ + insn_code_number = -1; + + /* See if this is a PARALLEL of two SETs where one SET's destination is + a register that is unused and this isn't marked as an instruction that + might trap in an EH region. In that case, we just need the other SET. + We prefer this over the PARALLEL. + + This can occur when simplifying a divmod insn. We *must* test for this + case here because the code below that splits two independent SETs doesn't + handle this case correctly when it updates the register status. + + It's pointless doing this if we originally had two sets, one from + i3, and one from i2. Combining then splitting the parallel results + in the original i2 again plus an invalid insn (which we delete). + The net effect is only to move instructions around, which makes + debug info less accurate. + + If the remaining SET came from I2 its destination should not be used + between I2 and I3. See PR82024. */ + + if (!(added_sets_2 && i1 == 0) + && is_parallel_of_n_reg_sets (newpat, 2) + && asm_noperands (newpat) < 0) + { + rtx set0 = XVECEXP (newpat, 0, 0); + rtx set1 = XVECEXP (newpat, 0, 1); + rtx oldpat = newpat; + + if (((REG_P (SET_DEST (set1)) + && find_reg_note (i3, REG_UNUSED, SET_DEST (set1))) + || (GET_CODE (SET_DEST (set1)) == SUBREG + && find_reg_note (i3, REG_UNUSED, SUBREG_REG (SET_DEST (set1))))) + && insn_nothrow_p (i3) + && !side_effects_p (SET_SRC (set1))) + { + newpat = set0; + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + + else if (((REG_P (SET_DEST (set0)) + && find_reg_note (i3, REG_UNUSED, SET_DEST (set0))) + || (GET_CODE (SET_DEST (set0)) == SUBREG + && find_reg_note (i3, REG_UNUSED, + SUBREG_REG (SET_DEST (set0))))) + && insn_nothrow_p (i3) + && !side_effects_p (SET_SRC (set0))) + { + rtx dest = SET_DEST (set1); + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + if (!reg_used_between_p (dest, i2, i3)) + { + newpat = set1; + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + if (insn_code_number >= 0) + changed_i3_dest = 1; + } + } + + if (insn_code_number < 0) + newpat = oldpat; + } + + /* Is the result of combination a valid instruction? */ + if (insn_code_number < 0) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + /* If we were combining three insns and the result is a simple SET + with no ASM_OPERANDS that wasn't recognized, try to split it into two + insns. There are two ways to do this. It can be split using a + machine-specific method (like when you have an addition of a large + constant) or by combine in the function find_split_point. */ + + if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET + && asm_noperands (newpat) < 0) + { + rtx parallel, *split; + rtx_insn *m_split_insn; + + /* See if the MD file can split NEWPAT. If it can't, see if letting it + use I2DEST as a scratch register will help. In the latter case, + convert I2DEST to the mode of the source of NEWPAT if we can. */ + + m_split_insn = combine_split_insns (newpat, i3); + + /* We can only use I2DEST as a scratch reg if it doesn't overlap any + inputs of NEWPAT. */ + + /* ??? If I2DEST is not safe, and I1DEST exists, then it would be + possible to try that as a scratch reg. This would require adding + more code to make it work though. */ + + if (m_split_insn == 0 && ! reg_overlap_mentioned_p (i2dest, newpat)) + { + machine_mode new_mode = GET_MODE (SET_DEST (newpat)); + + /* ??? Reusing i2dest without resetting the reg_stat entry for it + (temporarily, until we are committed to this instruction + combination) does not work: for example, any call to nonzero_bits + on the register (from a splitter in the MD file, for example) + will get the old information, which is invalid. + + Since nowadays we can create registers during combine just fine, + we should just create a new one here, not reuse i2dest. */ + + /* First try to split using the original register as a + scratch register. */ + parallel = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, newpat, + gen_rtx_CLOBBER (VOIDmode, + i2dest))); + m_split_insn = combine_split_insns (parallel, i3); + + /* If that didn't work, try changing the mode of I2DEST if + we can. */ + if (m_split_insn == 0 + && new_mode != GET_MODE (i2dest) + && new_mode != VOIDmode + && can_change_dest_mode (i2dest, added_sets_2, new_mode)) + { + machine_mode old_mode = GET_MODE (i2dest); + rtx ni2dest; + + if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER) + ni2dest = gen_rtx_REG (new_mode, REGNO (i2dest)); + else + { + SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], new_mode); + ni2dest = regno_reg_rtx[REGNO (i2dest)]; + } + + parallel = (gen_rtx_PARALLEL + (VOIDmode, + gen_rtvec (2, newpat, + gen_rtx_CLOBBER (VOIDmode, + ni2dest)))); + m_split_insn = combine_split_insns (parallel, i3); + + if (m_split_insn == 0 + && REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) + { + struct undo *buf; + + adjust_reg_mode (regno_reg_rtx[REGNO (i2dest)], old_mode); + buf = undobuf.undos; + undobuf.undos = buf->next; + buf->next = undobuf.frees; + undobuf.frees = buf; + } + } + + i2scratch = m_split_insn != 0; + } + + /* If recog_for_combine has discarded clobbers, try to use them + again for the split. */ + if (m_split_insn == 0 && newpat_vec_with_clobbers) + { + parallel = gen_rtx_PARALLEL (VOIDmode, newpat_vec_with_clobbers); + m_split_insn = combine_split_insns (parallel, i3); + } + + if (m_split_insn && NEXT_INSN (m_split_insn) == NULL_RTX) + { + rtx m_split_pat = PATTERN (m_split_insn); + insn_code_number = recog_for_combine (&m_split_pat, i3, &new_i3_notes); + if (insn_code_number >= 0) + newpat = m_split_pat; + } + else if (m_split_insn && NEXT_INSN (NEXT_INSN (m_split_insn)) == NULL_RTX + && (next_nonnote_nondebug_insn (i2) == i3 + || !modified_between_p (PATTERN (m_split_insn), i2, i3))) + { + rtx i2set, i3set; + rtx newi3pat = PATTERN (NEXT_INSN (m_split_insn)); + newi2pat = PATTERN (m_split_insn); + + i3set = single_set (NEXT_INSN (m_split_insn)); + i2set = single_set (m_split_insn); + + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + + /* If I2 or I3 has multiple SETs, we won't know how to track + register status, so don't use these insns. If I2's destination + is used between I2 and I3, we also can't use these insns. */ + + if (i2_code_number >= 0 && i2set && i3set + && (next_nonnote_nondebug_insn (i2) == i3 + || ! reg_used_between_p (SET_DEST (i2set), i2, i3))) + insn_code_number = recog_for_combine (&newi3pat, i3, + &new_i3_notes); + if (insn_code_number >= 0) + newpat = newi3pat; + + /* It is possible that both insns now set the destination of I3. + If so, we must show an extra use of it. */ + + if (insn_code_number >= 0) + { + rtx new_i3_dest = SET_DEST (i3set); + rtx new_i2_dest = SET_DEST (i2set); + + while (GET_CODE (new_i3_dest) == ZERO_EXTRACT + || GET_CODE (new_i3_dest) == STRICT_LOW_PART + || GET_CODE (new_i3_dest) == SUBREG) + new_i3_dest = XEXP (new_i3_dest, 0); + + while (GET_CODE (new_i2_dest) == ZERO_EXTRACT + || GET_CODE (new_i2_dest) == STRICT_LOW_PART + || GET_CODE (new_i2_dest) == SUBREG) + new_i2_dest = XEXP (new_i2_dest, 0); + + if (REG_P (new_i3_dest) + && REG_P (new_i2_dest) + && REGNO (new_i3_dest) == REGNO (new_i2_dest) + && REGNO (new_i2_dest) < reg_n_sets_max) + INC_REG_N_SETS (REGNO (new_i2_dest), 1); + } + } + + /* If we can split it and use I2DEST, go ahead and see if that + helps things be recognized. Verify that none of the registers + are set between I2 and I3. */ + if (insn_code_number < 0 + && (split = find_split_point (&newpat, i3, false)) != 0 + /* We need I2DEST in the proper mode. If it is a hard register + or the only use of a pseudo, we can change its mode. + Make sure we don't change a hard register to have a mode that + isn't valid for it, or change the number of registers. */ + && (GET_MODE (*split) == GET_MODE (i2dest) + || GET_MODE (*split) == VOIDmode + || can_change_dest_mode (i2dest, added_sets_2, + GET_MODE (*split))) + && (next_nonnote_nondebug_insn (i2) == i3 + || !modified_between_p (*split, i2, i3)) + /* We can't overwrite I2DEST if its value is still used by + NEWPAT. */ + && ! reg_referenced_p (i2dest, newpat)) + { + rtx newdest = i2dest; + enum rtx_code split_code = GET_CODE (*split); + machine_mode split_mode = GET_MODE (*split); + bool subst_done = false; + newi2pat = NULL_RTX; + + i2scratch = true; + + /* *SPLIT may be part of I2SRC, so make sure we have the + original expression around for later debug processing. + We should not need I2SRC any more in other cases. */ + if (MAY_HAVE_DEBUG_BIND_INSNS) + i2src = copy_rtx (i2src); + else + i2src = NULL; + + /* Get NEWDEST as a register in the proper mode. We have already + validated that we can do this. */ + if (GET_MODE (i2dest) != split_mode && split_mode != VOIDmode) + { + if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER) + newdest = gen_rtx_REG (split_mode, REGNO (i2dest)); + else + { + SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], split_mode); + newdest = regno_reg_rtx[REGNO (i2dest)]; + } + } + + /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to + an ASHIFT. This can occur if it was inside a PLUS and hence + appeared to be a memory address. This is a kludge. */ + if (split_code == MULT + && CONST_INT_P (XEXP (*split, 1)) + && INTVAL (XEXP (*split, 1)) > 0 + && (i = exact_log2 (UINTVAL (XEXP (*split, 1)))) >= 0) + { + rtx i_rtx = gen_int_shift_amount (split_mode, i); + SUBST (*split, gen_rtx_ASHIFT (split_mode, + XEXP (*split, 0), i_rtx)); + /* Update split_code because we may not have a multiply + anymore. */ + split_code = GET_CODE (*split); + } + + /* Similarly for (plus (mult FOO (const_int pow2))). */ + if (split_code == PLUS + && GET_CODE (XEXP (*split, 0)) == MULT + && CONST_INT_P (XEXP (XEXP (*split, 0), 1)) + && INTVAL (XEXP (XEXP (*split, 0), 1)) > 0 + && (i = exact_log2 (UINTVAL (XEXP (XEXP (*split, 0), 1)))) >= 0) + { + rtx nsplit = XEXP (*split, 0); + rtx i_rtx = gen_int_shift_amount (GET_MODE (nsplit), i); + SUBST (XEXP (*split, 0), gen_rtx_ASHIFT (GET_MODE (nsplit), + XEXP (nsplit, 0), + i_rtx)); + /* Update split_code because we may not have a multiply + anymore. */ + split_code = GET_CODE (*split); + } + +#ifdef INSN_SCHEDULING + /* If *SPLIT is a paradoxical SUBREG, when we split it, it should + be written as a ZERO_EXTEND. */ + if (split_code == SUBREG && MEM_P (SUBREG_REG (*split))) + { + /* Or as a SIGN_EXTEND if LOAD_EXTEND_OP says that that's + what it really is. */ + if (load_extend_op (GET_MODE (SUBREG_REG (*split))) + == SIGN_EXTEND) + SUBST (*split, gen_rtx_SIGN_EXTEND (split_mode, + SUBREG_REG (*split))); + else + SUBST (*split, gen_rtx_ZERO_EXTEND (split_mode, + SUBREG_REG (*split))); + } +#endif + + /* Attempt to split binary operators using arithmetic identities. */ + if (BINARY_P (SET_SRC (newpat)) + && split_mode == GET_MODE (SET_SRC (newpat)) + && ! side_effects_p (SET_SRC (newpat))) + { + rtx setsrc = SET_SRC (newpat); + machine_mode mode = GET_MODE (setsrc); + enum rtx_code code = GET_CODE (setsrc); + rtx src_op0 = XEXP (setsrc, 0); + rtx src_op1 = XEXP (setsrc, 1); + + /* Split "X = Y op Y" as "Z = Y; X = Z op Z". */ + if (rtx_equal_p (src_op0, src_op1)) + { + newi2pat = gen_rtx_SET (newdest, src_op0); + SUBST (XEXP (setsrc, 0), newdest); + SUBST (XEXP (setsrc, 1), newdest); + subst_done = true; + } + /* Split "((P op Q) op R) op S" where op is PLUS or MULT. */ + else if ((code == PLUS || code == MULT) + && GET_CODE (src_op0) == code + && GET_CODE (XEXP (src_op0, 0)) == code + && (INTEGRAL_MODE_P (mode) + || (FLOAT_MODE_P (mode) + && flag_unsafe_math_optimizations))) + { + rtx p = XEXP (XEXP (src_op0, 0), 0); + rtx q = XEXP (XEXP (src_op0, 0), 1); + rtx r = XEXP (src_op0, 1); + rtx s = src_op1; + + /* Split both "((X op Y) op X) op Y" and + "((X op Y) op Y) op X" as "T op T" where T is + "X op Y". */ + if ((rtx_equal_p (p,r) && rtx_equal_p (q,s)) + || (rtx_equal_p (p,s) && rtx_equal_p (q,r))) + { + newi2pat = gen_rtx_SET (newdest, XEXP (src_op0, 0)); + SUBST (XEXP (setsrc, 0), newdest); + SUBST (XEXP (setsrc, 1), newdest); + subst_done = true; + } + /* Split "((X op X) op Y) op Y)" as "T op T" where + T is "X op Y". */ + else if (rtx_equal_p (p,q) && rtx_equal_p (r,s)) + { + rtx tmp = simplify_gen_binary (code, mode, p, r); + newi2pat = gen_rtx_SET (newdest, tmp); + SUBST (XEXP (setsrc, 0), newdest); + SUBST (XEXP (setsrc, 1), newdest); + subst_done = true; + } + } + } + + if (!subst_done) + { + newi2pat = gen_rtx_SET (newdest, *split); + SUBST (*split, newdest); + } + + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + + /* recog_for_combine might have added CLOBBERs to newi2pat. + Make sure NEWPAT does not depend on the clobbered regs. */ + if (GET_CODE (newi2pat) == PARALLEL) + for (i = XVECLEN (newi2pat, 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (newi2pat, 0, i)) == CLOBBER) + { + rtx reg = XEXP (XVECEXP (newi2pat, 0, i), 0); + if (reg_overlap_mentioned_p (reg, newpat)) + { + undo_all (); + return 0; + } + } + + /* If the split point was a MULT and we didn't have one before, + don't use one now. */ + if (i2_code_number >= 0 && ! (split_code == MULT && ! have_mult)) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + } + } + + /* Check for a case where we loaded from memory in a narrow mode and + then sign extended it, but we need both registers. In that case, + we have a PARALLEL with both loads from the same memory location. + We can split this into a load from memory followed by a register-register + copy. This saves at least one insn, more if register allocation can + eliminate the copy. + + We cannot do this if the destination of the first assignment is a + condition code register. We eliminate this case by making sure + the SET_DEST and SET_SRC have the same mode. + + We cannot do this if the destination of the second assignment is + a register that we have already assumed is zero-extended. Similarly + for a SUBREG of such a register. */ + + else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 + && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND + && (GET_MODE (SET_DEST (XVECEXP (newpat, 0, 0))) + == GET_MODE (SET_SRC (XVECEXP (newpat, 0, 0)))) + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)), + XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0)) + && !modified_between_p (SET_SRC (XVECEXP (newpat, 0, 1)), i2, i3) + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART + && ! (temp_expr = SET_DEST (XVECEXP (newpat, 0, 1)), + (REG_P (temp_expr) + && reg_stat[REGNO (temp_expr)].nonzero_bits != 0 + && known_lt (GET_MODE_PRECISION (GET_MODE (temp_expr)), + BITS_PER_WORD) + && known_lt (GET_MODE_PRECISION (GET_MODE (temp_expr)), + HOST_BITS_PER_INT) + && (reg_stat[REGNO (temp_expr)].nonzero_bits + != GET_MODE_MASK (word_mode)))) + && ! (GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == SUBREG + && (temp_expr = SUBREG_REG (SET_DEST (XVECEXP (newpat, 0, 1))), + (REG_P (temp_expr) + && reg_stat[REGNO (temp_expr)].nonzero_bits != 0 + && known_lt (GET_MODE_PRECISION (GET_MODE (temp_expr)), + BITS_PER_WORD) + && known_lt (GET_MODE_PRECISION (GET_MODE (temp_expr)), + HOST_BITS_PER_INT) + && (reg_stat[REGNO (temp_expr)].nonzero_bits + != GET_MODE_MASK (word_mode))))) + && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)), + SET_SRC (XVECEXP (newpat, 0, 1))) + && ! find_reg_note (i3, REG_UNUSED, + SET_DEST (XVECEXP (newpat, 0, 0)))) + { + rtx ni2dest; + + newi2pat = XVECEXP (newpat, 0, 0); + ni2dest = SET_DEST (XVECEXP (newpat, 0, 0)); + newpat = XVECEXP (newpat, 0, 1); + SUBST (SET_SRC (newpat), + gen_lowpart (GET_MODE (SET_SRC (newpat)), ni2dest)); + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + + if (i2_code_number >= 0) + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + if (insn_code_number >= 0) + swap_i2i3 = 1; + } + + /* Similarly, check for a case where we have a PARALLEL of two independent + SETs but we started with three insns. In this case, we can do the sets + as two separate insns. This case occurs when some SET allows two + other insns to combine, but the destination of that SET is still live. + + Also do this if we started with two insns and (at least) one of the + resulting sets is a noop; this noop will be deleted later. + + Also do this if we started with two insns neither of which was a simple + move. */ + + else if (insn_code_number < 0 && asm_noperands (newpat) < 0 + && GET_CODE (newpat) == PARALLEL + && XVECLEN (newpat, 0) == 2 + && GET_CODE (XVECEXP (newpat, 0, 0)) == SET + && GET_CODE (XVECEXP (newpat, 0, 1)) == SET + && (i1 + || set_noop_p (XVECEXP (newpat, 0, 0)) + || set_noop_p (XVECEXP (newpat, 0, 1)) + || (!i2_was_move && !i3_was_move)) + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT + && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART + && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)), + XVECEXP (newpat, 0, 0)) + && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)), + XVECEXP (newpat, 0, 1)) + && ! (contains_muldiv (SET_SRC (XVECEXP (newpat, 0, 0))) + && contains_muldiv (SET_SRC (XVECEXP (newpat, 0, 1))))) + { + rtx set0 = XVECEXP (newpat, 0, 0); + rtx set1 = XVECEXP (newpat, 0, 1); + + /* Normally, it doesn't matter which of the two is done first, but + one which uses any regs/memory set in between i2 and i3 can't + be first. The PARALLEL might also have been pre-existing in i3, + so we need to make sure that we won't wrongly hoist a SET to i2 + that would conflict with a death note present in there, or would + have its dest modified between i2 and i3. */ + if (!modified_between_p (SET_SRC (set1), i2, i3) + && !(REG_P (SET_DEST (set1)) + && find_reg_note (i2, REG_DEAD, SET_DEST (set1))) + && !(GET_CODE (SET_DEST (set1)) == SUBREG + && find_reg_note (i2, REG_DEAD, + SUBREG_REG (SET_DEST (set1)))) + && !modified_between_p (SET_DEST (set1), i2, i3) + /* If I3 is a jump, ensure that set0 is a jump so that + we do not create invalid RTL. */ + && (!JUMP_P (i3) || SET_DEST (set0) == pc_rtx) + ) + { + newi2pat = set1; + newpat = set0; + } + else if (!modified_between_p (SET_SRC (set0), i2, i3) + && !(REG_P (SET_DEST (set0)) + && find_reg_note (i2, REG_DEAD, SET_DEST (set0))) + && !(GET_CODE (SET_DEST (set0)) == SUBREG + && find_reg_note (i2, REG_DEAD, + SUBREG_REG (SET_DEST (set0)))) + && !modified_between_p (SET_DEST (set0), i2, i3) + /* If I3 is a jump, ensure that set1 is a jump so that + we do not create invalid RTL. */ + && (!JUMP_P (i3) || SET_DEST (set1) == pc_rtx) + ) + { + newi2pat = set0; + newpat = set1; + } + else + { + undo_all (); + return 0; + } + + i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); + + if (i2_code_number >= 0) + { + /* recog_for_combine might have added CLOBBERs to newi2pat. + Make sure NEWPAT does not depend on the clobbered regs. */ + if (GET_CODE (newi2pat) == PARALLEL) + { + for (i = XVECLEN (newi2pat, 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (newi2pat, 0, i)) == CLOBBER) + { + rtx reg = XEXP (XVECEXP (newi2pat, 0, i), 0); + if (reg_overlap_mentioned_p (reg, newpat)) + { + undo_all (); + return 0; + } + } + } + + insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); + + if (insn_code_number >= 0) + split_i2i3 = 1; + } + } + + /* If it still isn't recognized, fail and change things back the way they + were. */ + if ((insn_code_number < 0 + /* Is the result a reasonable ASM_OPERANDS? */ + && (! check_asm_operands (newpat) || added_sets_1 || added_sets_2))) + { + undo_all (); + return 0; + } + + /* If we had to change another insn, make sure it is valid also. */ + if (undobuf.other_insn) + { + CLEAR_HARD_REG_SET (newpat_used_regs); + + other_pat = PATTERN (undobuf.other_insn); + other_code_number = recog_for_combine (&other_pat, undobuf.other_insn, + &new_other_notes); + + if (other_code_number < 0 && ! check_asm_operands (other_pat)) + { + undo_all (); + return 0; + } + } + + /* Only allow this combination if insn_cost reports that the + replacement instructions are cheaper than the originals. */ + if (!combine_validate_cost (i0, i1, i2, i3, newpat, newi2pat, other_pat)) + { + undo_all (); + return 0; + } + + if (MAY_HAVE_DEBUG_BIND_INSNS) + { + struct undo *undo; + + for (undo = undobuf.undos; undo; undo = undo->next) + if (undo->kind == UNDO_MODE) + { + rtx reg = *undo->where.r; + machine_mode new_mode = GET_MODE (reg); + machine_mode old_mode = undo->old_contents.m; + + /* Temporarily revert mode back. */ + adjust_reg_mode (reg, old_mode); + + if (reg == i2dest && i2scratch) + { + /* If we used i2dest as a scratch register with a + different mode, substitute it for the original + i2src while its original mode is temporarily + restored, and then clear i2scratch so that we don't + do it again later. */ + propagate_for_debug (i2, last_combined_insn, reg, i2src, + this_basic_block); + i2scratch = false; + /* Put back the new mode. */ + adjust_reg_mode (reg, new_mode); + } + else + { + rtx tempreg = gen_raw_REG (old_mode, REGNO (reg)); + rtx_insn *first, *last; + + if (reg == i2dest) + { + first = i2; + last = last_combined_insn; + } + else + { + first = i3; + last = undobuf.other_insn; + gcc_assert (last); + if (DF_INSN_LUID (last) + < DF_INSN_LUID (last_combined_insn)) + last = last_combined_insn; + } + + /* We're dealing with a reg that changed mode but not + meaning, so we want to turn it into a subreg for + the new mode. However, because of REG sharing and + because its mode had already changed, we have to do + it in two steps. First, replace any debug uses of + reg, with its original mode temporarily restored, + with this copy we have created; then, replace the + copy with the SUBREG of the original shared reg, + once again changed to the new mode. */ + propagate_for_debug (first, last, reg, tempreg, + this_basic_block); + adjust_reg_mode (reg, new_mode); + propagate_for_debug (first, last, tempreg, + lowpart_subreg (old_mode, reg, new_mode), + this_basic_block); + } + } + } + + /* If we will be able to accept this, we have made a + change to the destination of I3. This requires us to + do a few adjustments. */ + + if (changed_i3_dest) + { + PATTERN (i3) = newpat; + adjust_for_new_dest (i3); + } + + /* We now know that we can do this combination. Merge the insns and + update the status of registers and LOG_LINKS. */ + + if (undobuf.other_insn) + { + rtx note, next; + + PATTERN (undobuf.other_insn) = other_pat; + + /* If any of the notes in OTHER_INSN were REG_DEAD or REG_UNUSED, + ensure that they are still valid. Then add any non-duplicate + notes added by recog_for_combine. */ + for (note = REG_NOTES (undobuf.other_insn); note; note = next) + { + next = XEXP (note, 1); + + if ((REG_NOTE_KIND (note) == REG_DEAD + && !reg_referenced_p (XEXP (note, 0), + PATTERN (undobuf.other_insn))) + ||(REG_NOTE_KIND (note) == REG_UNUSED + && !reg_set_p (XEXP (note, 0), + PATTERN (undobuf.other_insn))) + /* Simply drop equal note since it may be no longer valid + for other_insn. It may be possible to record that CC + register is changed and only discard those notes, but + in practice it's unnecessary complication and doesn't + give any meaningful improvement. + + See PR78559. */ + || REG_NOTE_KIND (note) == REG_EQUAL + || REG_NOTE_KIND (note) == REG_EQUIV) + remove_note (undobuf.other_insn, note); + } + + distribute_notes (new_other_notes, undobuf.other_insn, + undobuf.other_insn, NULL, NULL_RTX, NULL_RTX, + NULL_RTX); + } + + if (swap_i2i3) + { + /* I3 now uses what used to be its destination and which is now + I2's destination. This requires us to do a few adjustments. */ + PATTERN (i3) = newpat; + adjust_for_new_dest (i3); + } + + if (swap_i2i3 || split_i2i3) + { + /* We might need a LOG_LINK from I3 to I2. But then we used to + have one, so we still will. + + However, some later insn might be using I2's dest and have + a LOG_LINK pointing at I3. We should change it to point at + I2 instead. */ + + /* newi2pat is usually a SET here; however, recog_for_combine might + have added some clobbers. */ + rtx x = newi2pat; + if (GET_CODE (x) == PARALLEL) + x = XVECEXP (newi2pat, 0, 0); + + if (REG_P (SET_DEST (x)) + || (GET_CODE (SET_DEST (x)) == SUBREG + && REG_P (SUBREG_REG (SET_DEST (x))))) + { + unsigned int regno = reg_or_subregno (SET_DEST (x)); + + bool done = false; + for (rtx_insn *insn = NEXT_INSN (i3); + !done + && insn + && NONDEBUG_INSN_P (insn) + && BLOCK_FOR_INSN (insn) == this_basic_block; + insn = NEXT_INSN (insn)) + { + struct insn_link *link; + FOR_EACH_LOG_LINK (link, insn) + if (link->insn == i3 && link->regno == regno) + { + link->insn = i2; + done = true; + break; + } + } + } + } + + { + rtx i3notes, i2notes, i1notes = 0, i0notes = 0; + struct insn_link *i3links, *i2links, *i1links = 0, *i0links = 0; + rtx midnotes = 0; + int from_luid; + /* Compute which registers we expect to eliminate. newi2pat may be setting + either i3dest or i2dest, so we must check it. */ + rtx elim_i2 = ((newi2pat && reg_set_p (i2dest, newi2pat)) + || i2dest_in_i2src || i2dest_in_i1src || i2dest_in_i0src + || !i2dest_killed + ? 0 : i2dest); + /* For i1, we need to compute both local elimination and global + elimination information with respect to newi2pat because i1dest + may be the same as i3dest, in which case newi2pat may be setting + i1dest. Global information is used when distributing REG_DEAD + note for i2 and i3, in which case it does matter if newi2pat sets + i1dest or not. + + Local information is used when distributing REG_DEAD note for i1, + in which case it doesn't matter if newi2pat sets i1dest or not. + See PR62151, if we have four insns combination: + i0: r0 <- i0src + i1: r1 <- i1src (using r0) + REG_DEAD (r0) + i2: r0 <- i2src (using r1) + i3: r3 <- i3src (using r0) + ix: using r0 + From i1's point of view, r0 is eliminated, no matter if it is set + by newi2pat or not. In other words, REG_DEAD info for r0 in i1 + should be discarded. + + Note local information only affects cases in forms like "I1->I2->I3", + "I0->I1->I2->I3" or "I0&I1->I2, I2->I3". For other cases like + "I0->I1, I1&I2->I3" or "I1&I2->I3", newi2pat won't set i1dest or + i0dest anyway. */ + rtx local_elim_i1 = (i1 == 0 || i1dest_in_i1src || i1dest_in_i0src + || !i1dest_killed + ? 0 : i1dest); + rtx elim_i1 = (local_elim_i1 == 0 + || (newi2pat && reg_set_p (i1dest, newi2pat)) + ? 0 : i1dest); + /* Same case as i1. */ + rtx local_elim_i0 = (i0 == 0 || i0dest_in_i0src || !i0dest_killed + ? 0 : i0dest); + rtx elim_i0 = (local_elim_i0 == 0 + || (newi2pat && reg_set_p (i0dest, newi2pat)) + ? 0 : i0dest); + + /* Get the old REG_NOTES and LOG_LINKS from all our insns and + clear them. */ + i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3); + i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2); + if (i1) + i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1); + if (i0) + i0notes = REG_NOTES (i0), i0links = LOG_LINKS (i0); + + /* Ensure that we do not have something that should not be shared but + occurs multiple times in the new insns. Check this by first + resetting all the `used' flags and then copying anything is shared. */ + + reset_used_flags (i3notes); + reset_used_flags (i2notes); + reset_used_flags (i1notes); + reset_used_flags (i0notes); + reset_used_flags (newpat); + reset_used_flags (newi2pat); + if (undobuf.other_insn) + reset_used_flags (PATTERN (undobuf.other_insn)); + + i3notes = copy_rtx_if_shared (i3notes); + i2notes = copy_rtx_if_shared (i2notes); + i1notes = copy_rtx_if_shared (i1notes); + i0notes = copy_rtx_if_shared (i0notes); + newpat = copy_rtx_if_shared (newpat); + newi2pat = copy_rtx_if_shared (newi2pat); + if (undobuf.other_insn) + reset_used_flags (PATTERN (undobuf.other_insn)); + + INSN_CODE (i3) = insn_code_number; + PATTERN (i3) = newpat; + + if (CALL_P (i3) && CALL_INSN_FUNCTION_USAGE (i3)) + { + for (rtx link = CALL_INSN_FUNCTION_USAGE (i3); link; + link = XEXP (link, 1)) + { + if (substed_i2) + { + /* I2SRC must still be meaningful at this point. Some + splitting operations can invalidate I2SRC, but those + operations do not apply to calls. */ + gcc_assert (i2src); + XEXP (link, 0) = simplify_replace_rtx (XEXP (link, 0), + i2dest, i2src); + } + if (substed_i1) + XEXP (link, 0) = simplify_replace_rtx (XEXP (link, 0), + i1dest, i1src); + if (substed_i0) + XEXP (link, 0) = simplify_replace_rtx (XEXP (link, 0), + i0dest, i0src); + } + } + + if (undobuf.other_insn) + INSN_CODE (undobuf.other_insn) = other_code_number; + + /* We had one special case above where I2 had more than one set and + we replaced a destination of one of those sets with the destination + of I3. In that case, we have to update LOG_LINKS of insns later + in this basic block. Note that this (expensive) case is rare. + + Also, in this case, we must pretend that all REG_NOTEs for I2 + actually came from I3, so that REG_UNUSED notes from I2 will be + properly handled. */ + + if (i3_subst_into_i2) + { + for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++) + if ((GET_CODE (XVECEXP (PATTERN (i2), 0, i)) == SET + || GET_CODE (XVECEXP (PATTERN (i2), 0, i)) == CLOBBER) + && REG_P (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) + && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest + && ! find_reg_note (i2, REG_UNUSED, + SET_DEST (XVECEXP (PATTERN (i2), 0, i)))) + for (temp_insn = NEXT_INSN (i2); + temp_insn + && (this_basic_block->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) + || BB_HEAD (this_basic_block) != temp_insn); + temp_insn = NEXT_INSN (temp_insn)) + if (temp_insn != i3 && NONDEBUG_INSN_P (temp_insn)) + FOR_EACH_LOG_LINK (link, temp_insn) + if (link->insn == i2) + link->insn = i3; + + if (i3notes) + { + rtx link = i3notes; + while (XEXP (link, 1)) + link = XEXP (link, 1); + XEXP (link, 1) = i2notes; + } + else + i3notes = i2notes; + i2notes = 0; + } + + LOG_LINKS (i3) = NULL; + REG_NOTES (i3) = 0; + LOG_LINKS (i2) = NULL; + REG_NOTES (i2) = 0; + + if (newi2pat) + { + if (MAY_HAVE_DEBUG_BIND_INSNS && i2scratch) + propagate_for_debug (i2, last_combined_insn, i2dest, i2src, + this_basic_block); + INSN_CODE (i2) = i2_code_number; + PATTERN (i2) = newi2pat; + } + else + { + if (MAY_HAVE_DEBUG_BIND_INSNS && i2src) + propagate_for_debug (i2, last_combined_insn, i2dest, i2src, + this_basic_block); + SET_INSN_DELETED (i2); + } + + if (i1) + { + LOG_LINKS (i1) = NULL; + REG_NOTES (i1) = 0; + if (MAY_HAVE_DEBUG_BIND_INSNS) + propagate_for_debug (i1, last_combined_insn, i1dest, i1src, + this_basic_block); + SET_INSN_DELETED (i1); + } + + if (i0) + { + LOG_LINKS (i0) = NULL; + REG_NOTES (i0) = 0; + if (MAY_HAVE_DEBUG_BIND_INSNS) + propagate_for_debug (i0, last_combined_insn, i0dest, i0src, + this_basic_block); + SET_INSN_DELETED (i0); + } + + /* Get death notes for everything that is now used in either I3 or + I2 and used to die in a previous insn. If we built two new + patterns, move from I1 to I2 then I2 to I3 so that we get the + proper movement on registers that I2 modifies. */ + + if (i0) + from_luid = DF_INSN_LUID (i0); + else if (i1) + from_luid = DF_INSN_LUID (i1); + else + from_luid = DF_INSN_LUID (i2); + if (newi2pat) + move_deaths (newi2pat, NULL_RTX, from_luid, i2, &midnotes); + move_deaths (newpat, newi2pat, from_luid, i3, &midnotes); + + /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */ + if (i3notes) + distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL, + elim_i2, elim_i1, elim_i0); + if (i2notes) + distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL, + elim_i2, elim_i1, elim_i0); + if (i1notes) + distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL, + elim_i2, local_elim_i1, local_elim_i0); + if (i0notes) + distribute_notes (i0notes, i0, i3, newi2pat ? i2 : NULL, + elim_i2, elim_i1, local_elim_i0); + if (midnotes) + distribute_notes (midnotes, NULL, i3, newi2pat ? i2 : NULL, + elim_i2, elim_i1, elim_i0); + + /* Distribute any notes added to I2 or I3 by recog_for_combine. We + know these are REG_UNUSED and want them to go to the desired insn, + so we always pass it as i3. */ + + if (newi2pat && new_i2_notes) + distribute_notes (new_i2_notes, i2, i2, NULL, NULL_RTX, NULL_RTX, + NULL_RTX); + + if (new_i3_notes) + distribute_notes (new_i3_notes, i3, i3, NULL, NULL_RTX, NULL_RTX, + NULL_RTX); + + /* If I3DEST was used in I3SRC, it really died in I3. We may need to + put a REG_DEAD note for it somewhere. If NEWI2PAT exists and sets + I3DEST, the death must be somewhere before I2, not I3. If we passed I3 + in that case, it might delete I2. Similarly for I2 and I1. + Show an additional death due to the REG_DEAD note we make here. If + we discard it in distribute_notes, we will decrement it again. */ + + if (i3dest_killed) + { + rtx new_note = alloc_reg_note (REG_DEAD, i3dest_killed, NULL_RTX); + if (newi2pat && reg_set_p (i3dest_killed, newi2pat)) + distribute_notes (new_note, NULL, i2, NULL, elim_i2, + elim_i1, elim_i0); + else + distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + elim_i2, elim_i1, elim_i0); + } + + if (i2dest_in_i2src) + { + rtx new_note = alloc_reg_note (REG_DEAD, i2dest, NULL_RTX); + if (newi2pat && reg_set_p (i2dest, newi2pat)) + distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + NULL_RTX, NULL_RTX); + else + distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + NULL_RTX, NULL_RTX, NULL_RTX); + } + + if (i1dest_in_i1src) + { + rtx new_note = alloc_reg_note (REG_DEAD, i1dest, NULL_RTX); + if (newi2pat && reg_set_p (i1dest, newi2pat)) + distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + NULL_RTX, NULL_RTX); + else + distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + NULL_RTX, NULL_RTX, NULL_RTX); + } + + if (i0dest_in_i0src) + { + rtx new_note = alloc_reg_note (REG_DEAD, i0dest, NULL_RTX); + if (newi2pat && reg_set_p (i0dest, newi2pat)) + distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + NULL_RTX, NULL_RTX); + else + distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + NULL_RTX, NULL_RTX, NULL_RTX); + } + + distribute_links (i3links); + distribute_links (i2links); + distribute_links (i1links); + distribute_links (i0links); + + if (REG_P (i2dest)) + { + struct insn_link *link; + rtx_insn *i2_insn = 0; + rtx i2_val = 0, set; + + /* The insn that used to set this register doesn't exist, and + this life of the register may not exist either. See if one of + I3's links points to an insn that sets I2DEST. If it does, + that is now the last known value for I2DEST. If we don't update + this and I2 set the register to a value that depended on its old + contents, we will get confused. If this insn is used, thing + will be set correctly in combine_instructions. */ + FOR_EACH_LOG_LINK (link, i3) + if ((set = single_set (link->insn)) != 0 + && rtx_equal_p (i2dest, SET_DEST (set))) + i2_insn = link->insn, i2_val = SET_SRC (set); + + record_value_for_reg (i2dest, i2_insn, i2_val); + + /* If the reg formerly set in I2 died only once and that was in I3, + zero its use count so it won't make `reload' do any work. */ + if (! added_sets_2 + && (newi2pat == 0 || ! reg_mentioned_p (i2dest, newi2pat)) + && ! i2dest_in_i2src + && REGNO (i2dest) < reg_n_sets_max) + INC_REG_N_SETS (REGNO (i2dest), -1); + } + + if (i1 && REG_P (i1dest)) + { + struct insn_link *link; + rtx_insn *i1_insn = 0; + rtx i1_val = 0, set; + + FOR_EACH_LOG_LINK (link, i3) + if ((set = single_set (link->insn)) != 0 + && rtx_equal_p (i1dest, SET_DEST (set))) + i1_insn = link->insn, i1_val = SET_SRC (set); + + record_value_for_reg (i1dest, i1_insn, i1_val); + + if (! added_sets_1 + && ! i1dest_in_i1src + && REGNO (i1dest) < reg_n_sets_max) + INC_REG_N_SETS (REGNO (i1dest), -1); + } + + if (i0 && REG_P (i0dest)) + { + struct insn_link *link; + rtx_insn *i0_insn = 0; + rtx i0_val = 0, set; + + FOR_EACH_LOG_LINK (link, i3) + if ((set = single_set (link->insn)) != 0 + && rtx_equal_p (i0dest, SET_DEST (set))) + i0_insn = link->insn, i0_val = SET_SRC (set); + + record_value_for_reg (i0dest, i0_insn, i0_val); + + if (! added_sets_0 + && ! i0dest_in_i0src + && REGNO (i0dest) < reg_n_sets_max) + INC_REG_N_SETS (REGNO (i0dest), -1); + } + + /* Update reg_stat[].nonzero_bits et al for any changes that may have + been made to this insn. The order is important, because newi2pat + can affect nonzero_bits of newpat. */ + if (newi2pat) + note_pattern_stores (newi2pat, set_nonzero_bits_and_sign_copies, NULL); + note_pattern_stores (newpat, set_nonzero_bits_and_sign_copies, NULL); + } + + if (undobuf.other_insn != NULL_RTX) + { + if (dump_file) + { + fprintf (dump_file, "modifying other_insn "); + dump_insn_slim (dump_file, undobuf.other_insn); + } + df_insn_rescan (undobuf.other_insn); + } + + if (i0 && !(NOTE_P (i0) && (NOTE_KIND (i0) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i0 "); + dump_insn_slim (dump_file, i0); + } + df_insn_rescan (i0); + } + + if (i1 && !(NOTE_P (i1) && (NOTE_KIND (i1) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i1 "); + dump_insn_slim (dump_file, i1); + } + df_insn_rescan (i1); + } + + if (i2 && !(NOTE_P (i2) && (NOTE_KIND (i2) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i2 "); + dump_insn_slim (dump_file, i2); + } + df_insn_rescan (i2); + } + + if (i3 && !(NOTE_P (i3) && (NOTE_KIND (i3) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i3 "); + dump_insn_slim (dump_file, i3); + } + df_insn_rescan (i3); + } + + /* Set new_direct_jump_p if a new return or simple jump instruction + has been created. Adjust the CFG accordingly. */ + if (returnjump_p (i3) || any_uncondjump_p (i3)) + { + *new_direct_jump_p = 1; + mark_jump_label (PATTERN (i3), i3, 0); + update_cfg_for_uncondjump (i3); + } + + if (undobuf.other_insn != NULL_RTX + && (returnjump_p (undobuf.other_insn) + || any_uncondjump_p (undobuf.other_insn))) + { + *new_direct_jump_p = 1; + update_cfg_for_uncondjump (undobuf.other_insn); + } + + if (GET_CODE (PATTERN (i3)) == TRAP_IF + && XEXP (PATTERN (i3), 0) == const1_rtx) + { + basic_block bb = BLOCK_FOR_INSN (i3); + gcc_assert (bb); + remove_edge (split_block (bb, i3)); + emit_barrier_after_bb (bb); + *new_direct_jump_p = 1; + } + + if (undobuf.other_insn + && GET_CODE (PATTERN (undobuf.other_insn)) == TRAP_IF + && XEXP (PATTERN (undobuf.other_insn), 0) == const1_rtx) + { + basic_block bb = BLOCK_FOR_INSN (undobuf.other_insn); + gcc_assert (bb); + remove_edge (split_block (bb, undobuf.other_insn)); + emit_barrier_after_bb (bb); + *new_direct_jump_p = 1; + } + + /* A noop might also need cleaning up of CFG, if it comes from the + simplification of a jump. */ + if (JUMP_P (i3) + && GET_CODE (newpat) == SET + && SET_SRC (newpat) == pc_rtx + && SET_DEST (newpat) == pc_rtx) + { + *new_direct_jump_p = 1; + update_cfg_for_uncondjump (i3); + } + + if (undobuf.other_insn != NULL_RTX + && JUMP_P (undobuf.other_insn) + && GET_CODE (PATTERN (undobuf.other_insn)) == SET + && SET_SRC (PATTERN (undobuf.other_insn)) == pc_rtx + && SET_DEST (PATTERN (undobuf.other_insn)) == pc_rtx) + { + *new_direct_jump_p = 1; + update_cfg_for_uncondjump (undobuf.other_insn); + } + + combine_successes++; + undo_commit (); + + rtx_insn *ret = newi2pat ? i2 : i3; + if (added_links_insn && DF_INSN_LUID (added_links_insn) < DF_INSN_LUID (ret)) + ret = added_links_insn; + if (added_notes_insn && DF_INSN_LUID (added_notes_insn) < DF_INSN_LUID (ret)) + ret = added_notes_insn; + + return ret; +} + +/* Get a marker for undoing to the current state. */ + +static void * +get_undo_marker (void) +{ + return undobuf.undos; +} + +/* Undo the modifications up to the marker. */ + +static void +undo_to_marker (void *marker) +{ + struct undo *undo, *next; + + for (undo = undobuf.undos; undo != marker; undo = next) + { + gcc_assert (undo); + + next = undo->next; + switch (undo->kind) + { + case UNDO_RTX: + *undo->where.r = undo->old_contents.r; + break; + case UNDO_INT: + *undo->where.i = undo->old_contents.i; + break; + case UNDO_MODE: + adjust_reg_mode (*undo->where.r, undo->old_contents.m); + break; + case UNDO_LINKS: + *undo->where.l = undo->old_contents.l; + break; + default: + gcc_unreachable (); + } + + undo->next = undobuf.frees; + undobuf.frees = undo; + } + + undobuf.undos = (struct undo *) marker; +} + +/* Undo all the modifications recorded in undobuf. */ + +static void +undo_all (void) +{ + undo_to_marker (0); +} + +/* We've committed to accepting the changes we made. Move all + of the undos to the free list. */ + +static void +undo_commit (void) +{ + struct undo *undo, *next; + + for (undo = undobuf.undos; undo; undo = next) + { + next = undo->next; + undo->next = undobuf.frees; + undobuf.frees = undo; + } + undobuf.undos = 0; +} + +/* Find the innermost point within the rtx at LOC, possibly LOC itself, + where we have an arithmetic expression and return that point. LOC will + be inside INSN. + + try_combine will call this function to see if an insn can be split into + two insns. */ + +static rtx * +find_split_point (rtx *loc, rtx_insn *insn, bool set_src) +{ + rtx x = *loc; + enum rtx_code code = GET_CODE (x); + rtx *split; + unsigned HOST_WIDE_INT len = 0; + HOST_WIDE_INT pos = 0; + int unsignedp = 0; + rtx inner = NULL_RTX; + scalar_int_mode mode, inner_mode; + + /* First special-case some codes. */ + switch (code) + { + case SUBREG: +#ifdef INSN_SCHEDULING + /* If we are making a paradoxical SUBREG invalid, it becomes a split + point. */ + if (MEM_P (SUBREG_REG (x))) + return loc; +#endif + return find_split_point (&SUBREG_REG (x), insn, false); + + case MEM: + /* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it + using LO_SUM and HIGH. */ + if (HAVE_lo_sum && (GET_CODE (XEXP (x, 0)) == CONST + || GET_CODE (XEXP (x, 0)) == SYMBOL_REF)) + { + machine_mode address_mode = get_address_mode (x); + + SUBST (XEXP (x, 0), + gen_rtx_LO_SUM (address_mode, + gen_rtx_HIGH (address_mode, XEXP (x, 0)), + XEXP (x, 0))); + return &XEXP (XEXP (x, 0), 0); + } + + /* If we have a PLUS whose second operand is a constant and the + address is not valid, perhaps we can split it up using + the machine-specific way to split large constants. We use + the first pseudo-reg (one of the virtual regs) as a placeholder; + it will not remain in the result. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && ! memory_address_addr_space_p (GET_MODE (x), XEXP (x, 0), + MEM_ADDR_SPACE (x))) + { + rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER]; + rtx_insn *seq = combine_split_insns (gen_rtx_SET (reg, XEXP (x, 0)), + subst_insn); + + /* This should have produced two insns, each of which sets our + placeholder. If the source of the second is a valid address, + we can put both sources together and make a split point + in the middle. */ + + if (seq + && NEXT_INSN (seq) != NULL_RTX + && NEXT_INSN (NEXT_INSN (seq)) == NULL_RTX + && NONJUMP_INSN_P (seq) + && GET_CODE (PATTERN (seq)) == SET + && SET_DEST (PATTERN (seq)) == reg + && ! reg_mentioned_p (reg, + SET_SRC (PATTERN (seq))) + && NONJUMP_INSN_P (NEXT_INSN (seq)) + && GET_CODE (PATTERN (NEXT_INSN (seq))) == SET + && SET_DEST (PATTERN (NEXT_INSN (seq))) == reg + && memory_address_addr_space_p + (GET_MODE (x), SET_SRC (PATTERN (NEXT_INSN (seq))), + MEM_ADDR_SPACE (x))) + { + rtx src1 = SET_SRC (PATTERN (seq)); + rtx src2 = SET_SRC (PATTERN (NEXT_INSN (seq))); + + /* Replace the placeholder in SRC2 with SRC1. If we can + find where in SRC2 it was placed, that can become our + split point and we can replace this address with SRC2. + Just try two obvious places. */ + + src2 = replace_rtx (src2, reg, src1); + split = 0; + if (XEXP (src2, 0) == src1) + split = &XEXP (src2, 0); + else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e' + && XEXP (XEXP (src2, 0), 0) == src1) + split = &XEXP (XEXP (src2, 0), 0); + + if (split) + { + SUBST (XEXP (x, 0), src2); + return split; + } + } + + /* If that didn't work and we have a nested plus, like: + ((REG1 * CONST1) + REG2) + CONST2 and (REG1 + REG2) + CONST2 + is valid address, try to split (REG1 * CONST1). */ + if (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS + && !OBJECT_P (XEXP (XEXP (XEXP (x, 0), 0), 0)) + && OBJECT_P (XEXP (XEXP (XEXP (x, 0), 0), 1)) + && ! (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (XEXP (XEXP (x, 0), + 0), 0))))) + { + rtx tem = XEXP (XEXP (XEXP (x, 0), 0), 0); + XEXP (XEXP (XEXP (x, 0), 0), 0) = reg; + if (memory_address_addr_space_p (GET_MODE (x), XEXP (x, 0), + MEM_ADDR_SPACE (x))) + { + XEXP (XEXP (XEXP (x, 0), 0), 0) = tem; + return &XEXP (XEXP (XEXP (x, 0), 0), 0); + } + XEXP (XEXP (XEXP (x, 0), 0), 0) = tem; + } + else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS + && OBJECT_P (XEXP (XEXP (XEXP (x, 0), 0), 0)) + && !OBJECT_P (XEXP (XEXP (XEXP (x, 0), 0), 1)) + && ! (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (XEXP (XEXP (x, 0), + 0), 1))))) + { + rtx tem = XEXP (XEXP (XEXP (x, 0), 0), 1); + XEXP (XEXP (XEXP (x, 0), 0), 1) = reg; + if (memory_address_addr_space_p (GET_MODE (x), XEXP (x, 0), + MEM_ADDR_SPACE (x))) + { + XEXP (XEXP (XEXP (x, 0), 0), 1) = tem; + return &XEXP (XEXP (XEXP (x, 0), 0), 1); + } + XEXP (XEXP (XEXP (x, 0), 0), 1) = tem; + } + + /* If that didn't work, perhaps the first operand is complex and + needs to be computed separately, so make a split point there. + This will occur on machines that just support REG + CONST + and have a constant moved through some previous computation. */ + if (!OBJECT_P (XEXP (XEXP (x, 0), 0)) + && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (XEXP (x, 0), 0))))) + return &XEXP (XEXP (x, 0), 0); + } + + /* If we have a PLUS whose first operand is complex, try computing it + separately by making a split there. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && ! memory_address_addr_space_p (GET_MODE (x), XEXP (x, 0), + MEM_ADDR_SPACE (x)) + && ! OBJECT_P (XEXP (XEXP (x, 0), 0)) + && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (XEXP (x, 0), 0))))) + return &XEXP (XEXP (x, 0), 0); + break; + + case SET: + /* See if we can split SET_SRC as it stands. */ + split = find_split_point (&SET_SRC (x), insn, true); + if (split && split != &SET_SRC (x)) + return split; + + /* See if we can split SET_DEST as it stands. */ + split = find_split_point (&SET_DEST (x), insn, false); + if (split && split != &SET_DEST (x)) + return split; + + /* See if this is a bitfield assignment with everything constant. If + so, this is an IOR of an AND, so split it into that. */ + if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT + && is_a <scalar_int_mode> (GET_MODE (XEXP (SET_DEST (x), 0)), + &inner_mode) + && HWI_COMPUTABLE_MODE_P (inner_mode) + && CONST_INT_P (XEXP (SET_DEST (x), 1)) + && CONST_INT_P (XEXP (SET_DEST (x), 2)) + && CONST_INT_P (SET_SRC (x)) + && ((INTVAL (XEXP (SET_DEST (x), 1)) + + INTVAL (XEXP (SET_DEST (x), 2))) + <= GET_MODE_PRECISION (inner_mode)) + && ! side_effects_p (XEXP (SET_DEST (x), 0))) + { + HOST_WIDE_INT pos = INTVAL (XEXP (SET_DEST (x), 2)); + unsigned HOST_WIDE_INT len = INTVAL (XEXP (SET_DEST (x), 1)); + rtx dest = XEXP (SET_DEST (x), 0); + unsigned HOST_WIDE_INT mask = (HOST_WIDE_INT_1U << len) - 1; + unsigned HOST_WIDE_INT src = INTVAL (SET_SRC (x)) & mask; + rtx or_mask; + + if (BITS_BIG_ENDIAN) + pos = GET_MODE_PRECISION (inner_mode) - len - pos; + + or_mask = gen_int_mode (src << pos, inner_mode); + if (src == mask) + SUBST (SET_SRC (x), + simplify_gen_binary (IOR, inner_mode, dest, or_mask)); + else + { + rtx negmask = gen_int_mode (~(mask << pos), inner_mode); + SUBST (SET_SRC (x), + simplify_gen_binary (IOR, inner_mode, + simplify_gen_binary (AND, inner_mode, + dest, negmask), + or_mask)); + } + + SUBST (SET_DEST (x), dest); + + split = find_split_point (&SET_SRC (x), insn, true); + if (split && split != &SET_SRC (x)) + return split; + } + + /* Otherwise, see if this is an operation that we can split into two. + If so, try to split that. */ + code = GET_CODE (SET_SRC (x)); + + switch (code) + { + case AND: + /* If we are AND'ing with a large constant that is only a single + bit and the result is only being used in a context where we + need to know if it is zero or nonzero, replace it with a bit + extraction. This will avoid the large constant, which might + have taken more than one insn to make. If the constant were + not a valid argument to the AND but took only one insn to make, + this is no worse, but if it took more than one insn, it will + be better. */ + + if (CONST_INT_P (XEXP (SET_SRC (x), 1)) + && REG_P (XEXP (SET_SRC (x), 0)) + && (pos = exact_log2 (UINTVAL (XEXP (SET_SRC (x), 1)))) >= 7 + && REG_P (SET_DEST (x)) + && (split = find_single_use (SET_DEST (x), insn, NULL)) != 0 + && (GET_CODE (*split) == EQ || GET_CODE (*split) == NE) + && XEXP (*split, 0) == SET_DEST (x) + && XEXP (*split, 1) == const0_rtx) + { + rtx extraction = make_extraction (GET_MODE (SET_DEST (x)), + XEXP (SET_SRC (x), 0), + pos, NULL_RTX, 1, 1, 0, 0); + if (extraction != 0) + { + SUBST (SET_SRC (x), extraction); + return find_split_point (loc, insn, false); + } + } + break; + + case NE: + /* If STORE_FLAG_VALUE is -1, this is (NE X 0) and only one bit of X + is known to be on, this can be converted into a NEG of a shift. */ + if (STORE_FLAG_VALUE == -1 && XEXP (SET_SRC (x), 1) == const0_rtx + && GET_MODE (SET_SRC (x)) == GET_MODE (XEXP (SET_SRC (x), 0)) + && ((pos = exact_log2 (nonzero_bits (XEXP (SET_SRC (x), 0), + GET_MODE (XEXP (SET_SRC (x), + 0))))) >= 1)) + { + machine_mode mode = GET_MODE (XEXP (SET_SRC (x), 0)); + rtx pos_rtx = gen_int_shift_amount (mode, pos); + SUBST (SET_SRC (x), + gen_rtx_NEG (mode, + gen_rtx_LSHIFTRT (mode, + XEXP (SET_SRC (x), 0), + pos_rtx))); + + split = find_split_point (&SET_SRC (x), insn, true); + if (split && split != &SET_SRC (x)) + return split; + } + break; + + case SIGN_EXTEND: + inner = XEXP (SET_SRC (x), 0); + + /* We can't optimize if either mode is a partial integer + mode as we don't know how many bits are significant + in those modes. */ + if (!is_int_mode (GET_MODE (inner), &inner_mode) + || GET_MODE_CLASS (GET_MODE (SET_SRC (x))) == MODE_PARTIAL_INT) + break; + + pos = 0; + len = GET_MODE_PRECISION (inner_mode); + unsignedp = 0; + break; + + case SIGN_EXTRACT: + case ZERO_EXTRACT: + if (is_a <scalar_int_mode> (GET_MODE (XEXP (SET_SRC (x), 0)), + &inner_mode) + && CONST_INT_P (XEXP (SET_SRC (x), 1)) + && CONST_INT_P (XEXP (SET_SRC (x), 2))) + { + inner = XEXP (SET_SRC (x), 0); + len = INTVAL (XEXP (SET_SRC (x), 1)); + pos = INTVAL (XEXP (SET_SRC (x), 2)); + + if (BITS_BIG_ENDIAN) + pos = GET_MODE_PRECISION (inner_mode) - len - pos; + unsignedp = (code == ZERO_EXTRACT); + } + break; + + default: + break; + } + + if (len + && known_subrange_p (pos, len, + 0, GET_MODE_PRECISION (GET_MODE (inner))) + && is_a <scalar_int_mode> (GET_MODE (SET_SRC (x)), &mode)) + { + /* For unsigned, we have a choice of a shift followed by an + AND or two shifts. Use two shifts for field sizes where the + constant might be too large. We assume here that we can + always at least get 8-bit constants in an AND insn, which is + true for every current RISC. */ + + if (unsignedp && len <= 8) + { + unsigned HOST_WIDE_INT mask + = (HOST_WIDE_INT_1U << len) - 1; + rtx pos_rtx = gen_int_shift_amount (mode, pos); + SUBST (SET_SRC (x), + gen_rtx_AND (mode, + gen_rtx_LSHIFTRT + (mode, gen_lowpart (mode, inner), pos_rtx), + gen_int_mode (mask, mode))); + + split = find_split_point (&SET_SRC (x), insn, true); + if (split && split != &SET_SRC (x)) + return split; + } + else + { + int left_bits = GET_MODE_PRECISION (mode) - len - pos; + int right_bits = GET_MODE_PRECISION (mode) - len; + SUBST (SET_SRC (x), + gen_rtx_fmt_ee + (unsignedp ? LSHIFTRT : ASHIFTRT, mode, + gen_rtx_ASHIFT (mode, + gen_lowpart (mode, inner), + gen_int_shift_amount (mode, left_bits)), + gen_int_shift_amount (mode, right_bits))); + + split = find_split_point (&SET_SRC (x), insn, true); + if (split && split != &SET_SRC (x)) + return split; + } + } + + /* See if this is a simple operation with a constant as the second + operand. It might be that this constant is out of range and hence + could be used as a split point. */ + if (BINARY_P (SET_SRC (x)) + && CONSTANT_P (XEXP (SET_SRC (x), 1)) + && (OBJECT_P (XEXP (SET_SRC (x), 0)) + || (GET_CODE (XEXP (SET_SRC (x), 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (SET_SRC (x), 0)))))) + return &XEXP (SET_SRC (x), 1); + + /* Finally, see if this is a simple operation with its first operand + not in a register. The operation might require this operand in a + register, so return it as a split point. We can always do this + because if the first operand were another operation, we would have + already found it as a split point. */ + if ((BINARY_P (SET_SRC (x)) || UNARY_P (SET_SRC (x))) + && ! register_operand (XEXP (SET_SRC (x), 0), VOIDmode)) + return &XEXP (SET_SRC (x), 0); + + return 0; + + case AND: + case IOR: + /* We write NOR as (and (not A) (not B)), but if we don't have a NOR, + it is better to write this as (not (ior A B)) so we can split it. + Similarly for IOR. */ + if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT) + { + SUBST (*loc, + gen_rtx_NOT (GET_MODE (x), + gen_rtx_fmt_ee (code == IOR ? AND : IOR, + GET_MODE (x), + XEXP (XEXP (x, 0), 0), + XEXP (XEXP (x, 1), 0)))); + return find_split_point (loc, insn, set_src); + } + + /* Many RISC machines have a large set of logical insns. If the + second operand is a NOT, put it first so we will try to split the + other operand first. */ + if (GET_CODE (XEXP (x, 1)) == NOT) + { + rtx tem = XEXP (x, 0); + SUBST (XEXP (x, 0), XEXP (x, 1)); + SUBST (XEXP (x, 1), tem); + } + break; + + case PLUS: + case MINUS: + /* Canonicalization can produce (minus A (mult B C)), where C is a + constant. It may be better to try splitting (plus (mult B -C) A) + instead if this isn't a multiply by a power of two. */ + if (set_src && code == MINUS && GET_CODE (XEXP (x, 1)) == MULT + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && !pow2p_hwi (INTVAL (XEXP (XEXP (x, 1), 1)))) + { + machine_mode mode = GET_MODE (x); + unsigned HOST_WIDE_INT this_int = INTVAL (XEXP (XEXP (x, 1), 1)); + HOST_WIDE_INT other_int = trunc_int_for_mode (-this_int, mode); + SUBST (*loc, gen_rtx_PLUS (mode, + gen_rtx_MULT (mode, + XEXP (XEXP (x, 1), 0), + gen_int_mode (other_int, + mode)), + XEXP (x, 0))); + return find_split_point (loc, insn, set_src); + } + + /* Split at a multiply-accumulate instruction. However if this is + the SET_SRC, we likely do not have such an instruction and it's + worthless to try this split. */ + if (!set_src + && (GET_CODE (XEXP (x, 0)) == MULT + || (GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))) + return loc; + + default: + break; + } + + /* Otherwise, select our actions depending on our rtx class. */ + switch (GET_RTX_CLASS (code)) + { + case RTX_BITFIELD_OPS: /* This is ZERO_EXTRACT and SIGN_EXTRACT. */ + case RTX_TERNARY: + split = find_split_point (&XEXP (x, 2), insn, false); + if (split) + return split; + /* fall through */ + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: + case RTX_COMPARE: + case RTX_COMM_COMPARE: + split = find_split_point (&XEXP (x, 1), insn, false); + if (split) + return split; + /* fall through */ + case RTX_UNARY: + /* Some machines have (and (shift ...) ...) insns. If X is not + an AND, but XEXP (X, 0) is, use it as our split point. */ + if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND) + return &XEXP (x, 0); + + split = find_split_point (&XEXP (x, 0), insn, false); + if (split) + return split; + return loc; + + default: + /* Otherwise, we don't have a split point. */ + return 0; + } +} + +/* Throughout X, replace FROM with TO, and return the result. + The result is TO if X is FROM; + otherwise the result is X, but its contents may have been modified. + If they were modified, a record was made in undobuf so that + undo_all will (among other things) return X to its original state. + + If the number of changes necessary is too much to record to undo, + the excess changes are not made, so the result is invalid. + The changes already made can still be undone. + undobuf.num_undo is incremented for such changes, so by testing that + the caller can tell whether the result is valid. + + `n_occurrences' is incremented each time FROM is replaced. + + IN_DEST is nonzero if we are processing the SET_DEST of a SET. + + IN_COND is nonzero if we are at the top level of a condition. + + UNIQUE_COPY is nonzero if each substitution must be unique. We do this + by copying if `n_occurrences' is nonzero. */ + +static rtx +subst (rtx x, rtx from, rtx to, int in_dest, int in_cond, int unique_copy) +{ + enum rtx_code code = GET_CODE (x); + machine_mode op0_mode = VOIDmode; + const char *fmt; + int len, i; + rtx new_rtx; + +/* Two expressions are equal if they are identical copies of a shared + RTX or if they are both registers with the same register number + and mode. */ + +#define COMBINE_RTX_EQUAL_P(X,Y) \ + ((X) == (Y) \ + || (REG_P (X) && REG_P (Y) \ + && REGNO (X) == REGNO (Y) && GET_MODE (X) == GET_MODE (Y))) + + /* Do not substitute into clobbers of regs -- this will never result in + valid RTL. */ + if (GET_CODE (x) == CLOBBER && REG_P (XEXP (x, 0))) + return x; + + if (! in_dest && COMBINE_RTX_EQUAL_P (x, from)) + { + n_occurrences++; + return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to); + } + + /* If X and FROM are the same register but different modes, they + will not have been seen as equal above. However, the log links code + will make a LOG_LINKS entry for that case. If we do nothing, we + will try to rerecognize our original insn and, when it succeeds, + we will delete the feeding insn, which is incorrect. + + So force this insn not to match in this (rare) case. */ + if (! in_dest && code == REG && REG_P (from) + && reg_overlap_mentioned_p (x, from)) + return gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); + + /* If this is an object, we are done unless it is a MEM or LO_SUM, both + of which may contain things that can be combined. */ + if (code != MEM && code != LO_SUM && OBJECT_P (x)) + return x; + + /* It is possible to have a subexpression appear twice in the insn. + Suppose that FROM is a register that appears within TO. + Then, after that subexpression has been scanned once by `subst', + the second time it is scanned, TO may be found. If we were + to scan TO here, we would find FROM within it and create a + self-referent rtl structure which is completely wrong. */ + if (COMBINE_RTX_EQUAL_P (x, to)) + return to; + + /* Parallel asm_operands need special attention because all of the + inputs are shared across the arms. Furthermore, unsharing the + rtl results in recognition failures. Failure to handle this case + specially can result in circular rtl. + + Solve this by doing a normal pass across the first entry of the + parallel, and only processing the SET_DESTs of the subsequent + entries. Ug. */ + + if (code == PARALLEL + && GET_CODE (XVECEXP (x, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == ASM_OPERANDS) + { + new_rtx = subst (XVECEXP (x, 0, 0), from, to, 0, 0, unique_copy); + + /* If this substitution failed, this whole thing fails. */ + if (GET_CODE (new_rtx) == CLOBBER + && XEXP (new_rtx, 0) == const0_rtx) + return new_rtx; + + SUBST (XVECEXP (x, 0, 0), new_rtx); + + for (i = XVECLEN (x, 0) - 1; i >= 1; i--) + { + rtx dest = SET_DEST (XVECEXP (x, 0, i)); + + if (!REG_P (dest) && GET_CODE (dest) != PC) + { + new_rtx = subst (dest, from, to, 0, 0, unique_copy); + + /* If this substitution failed, this whole thing fails. */ + if (GET_CODE (new_rtx) == CLOBBER + && XEXP (new_rtx, 0) == const0_rtx) + return new_rtx; + + SUBST (SET_DEST (XVECEXP (x, 0, i)), new_rtx); + } + } + } + else + { + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + /* We don't need to process a SET_DEST that is a register or PC, so + set up to skip this common case. All other cases where we want + to suppress replacing something inside a SET_SRC are handled via + the IN_DEST operand. */ + if (code == SET + && (REG_P (SET_DEST (x)) + || GET_CODE (SET_DEST (x)) == PC)) + fmt = "ie"; + + /* Trying to simplify the operands of a widening MULT is not likely + to create RTL matching a machine insn. */ + if (code == MULT + && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND + || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND) + && (GET_CODE (XEXP (x, 1)) == ZERO_EXTEND + || GET_CODE (XEXP (x, 1)) == SIGN_EXTEND) + && REG_P (XEXP (XEXP (x, 0), 0)) + && REG_P (XEXP (XEXP (x, 1), 0)) + && from == to) + return x; + + + /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a + constant. */ + if (fmt[0] == 'e') + op0_mode = GET_MODE (XEXP (x, 0)); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from)) + { + new_rtx = (unique_copy && n_occurrences + ? copy_rtx (to) : to); + n_occurrences++; + } + else + { + new_rtx = subst (XVECEXP (x, i, j), from, to, 0, 0, + unique_copy); + + /* If this substitution failed, this whole thing + fails. */ + if (GET_CODE (new_rtx) == CLOBBER + && XEXP (new_rtx, 0) == const0_rtx) + return new_rtx; + } + + SUBST (XVECEXP (x, i, j), new_rtx); + } + } + else if (fmt[i] == 'e') + { + /* If this is a register being set, ignore it. */ + new_rtx = XEXP (x, i); + if (in_dest + && i == 0 + && (((code == SUBREG || code == ZERO_EXTRACT) + && REG_P (new_rtx)) + || code == STRICT_LOW_PART)) + ; + + else if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from)) + { + /* In general, don't install a subreg involving two + modes not tieable. It can worsen register + allocation, and can even make invalid reload + insns, since the reg inside may need to be copied + from in the outside mode, and that may be invalid + if it is an fp reg copied in integer mode. + + We allow an exception to this: It is valid if + it is inside another SUBREG and the mode of that + SUBREG and the mode of the inside of TO is + tieable. */ + + if (GET_CODE (to) == SUBREG + && !targetm.modes_tieable_p (GET_MODE (to), + GET_MODE (SUBREG_REG (to))) + && ! (code == SUBREG + && (targetm.modes_tieable_p + (GET_MODE (x), GET_MODE (SUBREG_REG (to)))))) + return gen_rtx_CLOBBER (VOIDmode, const0_rtx); + + if (code == SUBREG + && REG_P (to) + && REGNO (to) < FIRST_PSEUDO_REGISTER + && simplify_subreg_regno (REGNO (to), GET_MODE (to), + SUBREG_BYTE (x), + GET_MODE (x)) < 0) + return gen_rtx_CLOBBER (VOIDmode, const0_rtx); + + new_rtx = (unique_copy && n_occurrences ? copy_rtx (to) : to); + n_occurrences++; + } + else + /* If we are in a SET_DEST, suppress most cases unless we + have gone inside a MEM, in which case we want to + simplify the address. We assume here that things that + are actually part of the destination have their inner + parts in the first expression. This is true for SUBREG, + STRICT_LOW_PART, and ZERO_EXTRACT, which are the only + things aside from REG and MEM that should appear in a + SET_DEST. */ + new_rtx = subst (XEXP (x, i), from, to, + (((in_dest + && (code == SUBREG || code == STRICT_LOW_PART + || code == ZERO_EXTRACT)) + || code == SET) + && i == 0), + code == IF_THEN_ELSE && i == 0, + unique_copy); + + /* If we found that we will have to reject this combination, + indicate that by returning the CLOBBER ourselves, rather than + an expression containing it. This will speed things up as + well as prevent accidents where two CLOBBERs are considered + to be equal, thus producing an incorrect simplification. */ + + if (GET_CODE (new_rtx) == CLOBBER && XEXP (new_rtx, 0) == const0_rtx) + return new_rtx; + + if (GET_CODE (x) == SUBREG && CONST_SCALAR_INT_P (new_rtx)) + { + machine_mode mode = GET_MODE (x); + + x = simplify_subreg (GET_MODE (x), new_rtx, + GET_MODE (SUBREG_REG (x)), + SUBREG_BYTE (x)); + if (! x) + x = gen_rtx_CLOBBER (mode, const0_rtx); + } + else if (CONST_SCALAR_INT_P (new_rtx) + && (GET_CODE (x) == ZERO_EXTEND + || GET_CODE (x) == SIGN_EXTEND + || GET_CODE (x) == FLOAT + || GET_CODE (x) == UNSIGNED_FLOAT)) + { + x = simplify_unary_operation (GET_CODE (x), GET_MODE (x), + new_rtx, + GET_MODE (XEXP (x, 0))); + if (!x) + return gen_rtx_CLOBBER (VOIDmode, const0_rtx); + } + else + SUBST (XEXP (x, i), new_rtx); + } + } + } + + /* Check if we are loading something from the constant pool via float + extension; in this case we would undo compress_float_constant + optimization and degenerate constant load to an immediate value. */ + if (GET_CODE (x) == FLOAT_EXTEND + && MEM_P (XEXP (x, 0)) + && MEM_READONLY_P (XEXP (x, 0))) + { + rtx tmp = avoid_constant_pool_reference (x); + if (x != tmp) + return x; + } + + /* Try to simplify X. If the simplification changed the code, it is likely + that further simplification will help, so loop, but limit the number + of repetitions that will be performed. */ + + for (i = 0; i < 4; i++) + { + /* If X is sufficiently simple, don't bother trying to do anything + with it. */ + if (code != CONST_INT && code != REG && code != CLOBBER) + x = combine_simplify_rtx (x, op0_mode, in_dest, in_cond); + + if (GET_CODE (x) == code) + break; + + code = GET_CODE (x); + + /* We no longer know the original mode of operand 0 since we + have changed the form of X) */ + op0_mode = VOIDmode; + } + + return x; +} + +/* If X is a commutative operation whose operands are not in the canonical + order, use substitutions to swap them. */ + +static void +maybe_swap_commutative_operands (rtx x) +{ + if (COMMUTATIVE_ARITH_P (x) + && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1))) + { + rtx temp = XEXP (x, 0); + SUBST (XEXP (x, 0), XEXP (x, 1)); + SUBST (XEXP (x, 1), temp); + } +} + +/* Simplify X, a piece of RTL. We just operate on the expression at the + outer level; call `subst' to simplify recursively. Return the new + expression. + + OP0_MODE is the original mode of XEXP (x, 0). IN_DEST is nonzero + if we are inside a SET_DEST. IN_COND is nonzero if we are at the top level + of a condition. */ + +static rtx +combine_simplify_rtx (rtx x, machine_mode op0_mode, int in_dest, + int in_cond) +{ + enum rtx_code code = GET_CODE (x); + machine_mode mode = GET_MODE (x); + scalar_int_mode int_mode; + rtx temp; + int i; + + /* If this is a commutative operation, put a constant last and a complex + expression first. We don't need to do this for comparisons here. */ + maybe_swap_commutative_operands (x); + + /* Try to fold this expression in case we have constants that weren't + present before. */ + temp = 0; + switch (GET_RTX_CLASS (code)) + { + case RTX_UNARY: + if (op0_mode == VOIDmode) + op0_mode = GET_MODE (XEXP (x, 0)); + temp = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode); + break; + case RTX_COMPARE: + case RTX_COMM_COMPARE: + { + machine_mode cmp_mode = GET_MODE (XEXP (x, 0)); + if (cmp_mode == VOIDmode) + { + cmp_mode = GET_MODE (XEXP (x, 1)); + if (cmp_mode == VOIDmode) + cmp_mode = op0_mode; + } + temp = simplify_relational_operation (code, mode, cmp_mode, + XEXP (x, 0), XEXP (x, 1)); + } + break; + case RTX_COMM_ARITH: + case RTX_BIN_ARITH: + temp = simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1)); + break; + case RTX_BITFIELD_OPS: + case RTX_TERNARY: + temp = simplify_ternary_operation (code, mode, op0_mode, XEXP (x, 0), + XEXP (x, 1), XEXP (x, 2)); + break; + default: + break; + } + + if (temp) + { + x = temp; + code = GET_CODE (temp); + op0_mode = VOIDmode; + mode = GET_MODE (temp); + } + + /* If this is a simple operation applied to an IF_THEN_ELSE, try + applying it to the arms of the IF_THEN_ELSE. This often simplifies + things. Check for cases where both arms are testing the same + condition. + + Don't do anything if all operands are very simple. */ + + if ((BINARY_P (x) + && ((!OBJECT_P (XEXP (x, 0)) + && ! (GET_CODE (XEXP (x, 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (x, 0))))) + || (!OBJECT_P (XEXP (x, 1)) + && ! (GET_CODE (XEXP (x, 1)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (x, 1))))))) + || (UNARY_P (x) + && (!OBJECT_P (XEXP (x, 0)) + && ! (GET_CODE (XEXP (x, 0)) == SUBREG + && OBJECT_P (SUBREG_REG (XEXP (x, 0))))))) + { + rtx cond, true_rtx, false_rtx; + + cond = if_then_else_cond (x, &true_rtx, &false_rtx); + if (cond != 0 + /* If everything is a comparison, what we have is highly unlikely + to be simpler, so don't use it. */ + && ! (COMPARISON_P (x) + && (COMPARISON_P (true_rtx) || COMPARISON_P (false_rtx))) + /* Similarly, if we end up with one of the expressions the same + as the original, it is certainly not simpler. */ + && ! rtx_equal_p (x, true_rtx) + && ! rtx_equal_p (x, false_rtx)) + { + rtx cop1 = const0_rtx; + enum rtx_code cond_code = simplify_comparison (NE, &cond, &cop1); + + if (cond_code == NE && COMPARISON_P (cond)) + return x; + + /* Simplify the alternative arms; this may collapse the true and + false arms to store-flag values. Be careful to use copy_rtx + here since true_rtx or false_rtx might share RTL with x as a + result of the if_then_else_cond call above. */ + true_rtx = subst (copy_rtx (true_rtx), pc_rtx, pc_rtx, 0, 0, 0); + false_rtx = subst (copy_rtx (false_rtx), pc_rtx, pc_rtx, 0, 0, 0); + + /* If true_rtx and false_rtx are not general_operands, an if_then_else + is unlikely to be simpler. */ + if (general_operand (true_rtx, VOIDmode) + && general_operand (false_rtx, VOIDmode)) + { + enum rtx_code reversed; + + /* Restarting if we generate a store-flag expression will cause + us to loop. Just drop through in this case. */ + + /* If the result values are STORE_FLAG_VALUE and zero, we can + just make the comparison operation. */ + if (true_rtx == const_true_rtx && false_rtx == const0_rtx) + x = simplify_gen_relational (cond_code, mode, VOIDmode, + cond, cop1); + else if (true_rtx == const0_rtx && false_rtx == const_true_rtx + && ((reversed = reversed_comparison_code_parts + (cond_code, cond, cop1, NULL)) + != UNKNOWN)) + x = simplify_gen_relational (reversed, mode, VOIDmode, + cond, cop1); + + /* Likewise, we can make the negate of a comparison operation + if the result values are - STORE_FLAG_VALUE and zero. */ + else if (CONST_INT_P (true_rtx) + && INTVAL (true_rtx) == - STORE_FLAG_VALUE + && false_rtx == const0_rtx) + x = simplify_gen_unary (NEG, mode, + simplify_gen_relational (cond_code, + mode, VOIDmode, + cond, cop1), + mode); + else if (CONST_INT_P (false_rtx) + && INTVAL (false_rtx) == - STORE_FLAG_VALUE + && true_rtx == const0_rtx + && ((reversed = reversed_comparison_code_parts + (cond_code, cond, cop1, NULL)) + != UNKNOWN)) + x = simplify_gen_unary (NEG, mode, + simplify_gen_relational (reversed, + mode, VOIDmode, + cond, cop1), + mode); + + code = GET_CODE (x); + op0_mode = VOIDmode; + } + } + } + + /* First see if we can apply the inverse distributive law. */ + if (code == PLUS || code == MINUS + || code == AND || code == IOR || code == XOR) + { + x = apply_distributive_law (x); + code = GET_CODE (x); + op0_mode = VOIDmode; + } + + /* If CODE is an associative operation not otherwise handled, see if we + can associate some operands. This can win if they are constants or + if they are logically related (i.e. (a & b) & a). */ + if ((code == PLUS || code == MINUS || code == MULT || code == DIV + || code == AND || code == IOR || code == XOR + || code == SMAX || code == SMIN || code == UMAX || code == UMIN) + && ((INTEGRAL_MODE_P (mode) && code != DIV) + || (flag_associative_math && FLOAT_MODE_P (mode)))) + { + if (GET_CODE (XEXP (x, 0)) == code) + { + rtx other = XEXP (XEXP (x, 0), 0); + rtx inner_op0 = XEXP (XEXP (x, 0), 1); + rtx inner_op1 = XEXP (x, 1); + rtx inner; + + /* Make sure we pass the constant operand if any as the second + one if this is a commutative operation. */ + if (CONSTANT_P (inner_op0) && COMMUTATIVE_ARITH_P (x)) + std::swap (inner_op0, inner_op1); + inner = simplify_binary_operation (code == MINUS ? PLUS + : code == DIV ? MULT + : code, + mode, inner_op0, inner_op1); + + /* For commutative operations, try the other pair if that one + didn't simplify. */ + if (inner == 0 && COMMUTATIVE_ARITH_P (x)) + { + other = XEXP (XEXP (x, 0), 1); + inner = simplify_binary_operation (code, mode, + XEXP (XEXP (x, 0), 0), + XEXP (x, 1)); + } + + if (inner) + return simplify_gen_binary (code, mode, other, inner); + } + } + + /* A little bit of algebraic simplification here. */ + switch (code) + { + case MEM: + /* Ensure that our address has any ASHIFTs converted to MULT in case + address-recognizing predicates are called later. */ + temp = make_compound_operation (XEXP (x, 0), MEM); + SUBST (XEXP (x, 0), temp); + break; + + case SUBREG: + if (op0_mode == VOIDmode) + op0_mode = GET_MODE (SUBREG_REG (x)); + + /* See if this can be moved to simplify_subreg. */ + if (CONSTANT_P (SUBREG_REG (x)) + && known_eq (subreg_lowpart_offset (mode, op0_mode), SUBREG_BYTE (x)) + /* Don't call gen_lowpart if the inner mode + is VOIDmode and we cannot simplify it, as SUBREG without + inner mode is invalid. */ + && (GET_MODE (SUBREG_REG (x)) != VOIDmode + || gen_lowpart_common (mode, SUBREG_REG (x)))) + return gen_lowpart (mode, SUBREG_REG (x)); + + if (GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_CC) + break; + { + rtx temp; + temp = simplify_subreg (mode, SUBREG_REG (x), op0_mode, + SUBREG_BYTE (x)); + if (temp) + return temp; + + /* If op is known to have all lower bits zero, the result is zero. */ + scalar_int_mode int_mode, int_op0_mode; + if (!in_dest + && is_a <scalar_int_mode> (mode, &int_mode) + && is_a <scalar_int_mode> (op0_mode, &int_op0_mode) + && (GET_MODE_PRECISION (int_mode) + < GET_MODE_PRECISION (int_op0_mode)) + && known_eq (subreg_lowpart_offset (int_mode, int_op0_mode), + SUBREG_BYTE (x)) + && HWI_COMPUTABLE_MODE_P (int_op0_mode) + && ((nonzero_bits (SUBREG_REG (x), int_op0_mode) + & GET_MODE_MASK (int_mode)) == 0) + && !side_effects_p (SUBREG_REG (x))) + return CONST0_RTX (int_mode); + } + + /* Don't change the mode of the MEM if that would change the meaning + of the address. */ + if (MEM_P (SUBREG_REG (x)) + && (MEM_VOLATILE_P (SUBREG_REG (x)) + || mode_dependent_address_p (XEXP (SUBREG_REG (x), 0), + MEM_ADDR_SPACE (SUBREG_REG (x))))) + return gen_rtx_CLOBBER (mode, const0_rtx); + + /* Note that we cannot do any narrowing for non-constants since + we might have been counting on using the fact that some bits were + zero. We now do this in the SET. */ + + break; + + case NEG: + temp = expand_compound_operation (XEXP (x, 0)); + + /* For C equal to the width of MODE minus 1, (neg (ashiftrt X C)) can be + replaced by (lshiftrt X C). This will convert + (neg (sign_extract X 1 Y)) to (zero_extract X 1 Y). */ + + if (GET_CODE (temp) == ASHIFTRT + && CONST_INT_P (XEXP (temp, 1)) + && INTVAL (XEXP (temp, 1)) == GET_MODE_UNIT_PRECISION (mode) - 1) + return simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (temp, 0), + INTVAL (XEXP (temp, 1))); + + /* If X has only a single bit that might be nonzero, say, bit I, convert + (neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of + MODE minus 1. This will convert (neg (zero_extract X 1 Y)) to + (sign_extract X 1 Y). But only do this if TEMP isn't a register + or a SUBREG of one since we'd be making the expression more + complex if it was just a register. */ + + if (!REG_P (temp) + && ! (GET_CODE (temp) == SUBREG + && REG_P (SUBREG_REG (temp))) + && is_a <scalar_int_mode> (mode, &int_mode) + && (i = exact_log2 (nonzero_bits (temp, int_mode))) >= 0) + { + rtx temp1 = simplify_shift_const + (NULL_RTX, ASHIFTRT, int_mode, + simplify_shift_const (NULL_RTX, ASHIFT, int_mode, temp, + GET_MODE_PRECISION (int_mode) - 1 - i), + GET_MODE_PRECISION (int_mode) - 1 - i); + + /* If all we did was surround TEMP with the two shifts, we + haven't improved anything, so don't use it. Otherwise, + we are better off with TEMP1. */ + if (GET_CODE (temp1) != ASHIFTRT + || GET_CODE (XEXP (temp1, 0)) != ASHIFT + || XEXP (XEXP (temp1, 0), 0) != temp) + return temp1; + } + break; + + case TRUNCATE: + /* We can't handle truncation to a partial integer mode here + because we don't know the real bitsize of the partial + integer mode. */ + if (GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + break; + + if (HWI_COMPUTABLE_MODE_P (mode)) + SUBST (XEXP (x, 0), + force_to_mode (XEXP (x, 0), GET_MODE (XEXP (x, 0)), + GET_MODE_MASK (mode), 0)); + + /* We can truncate a constant value and return it. */ + { + poly_int64 c; + if (poly_int_rtx_p (XEXP (x, 0), &c)) + return gen_int_mode (c, mode); + } + + /* Similarly to what we do in simplify-rtx.c, a truncate of a register + whose value is a comparison can be replaced with a subreg if + STORE_FLAG_VALUE permits. */ + if (HWI_COMPUTABLE_MODE_P (mode) + && (STORE_FLAG_VALUE & ~GET_MODE_MASK (mode)) == 0 + && (temp = get_last_value (XEXP (x, 0))) + && COMPARISON_P (temp) + && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (XEXP (x, 0)))) + return gen_lowpart (mode, XEXP (x, 0)); + break; + + case CONST: + /* (const (const X)) can become (const X). Do it this way rather than + returning the inner CONST since CONST can be shared with a + REG_EQUAL note. */ + if (GET_CODE (XEXP (x, 0)) == CONST) + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + + case LO_SUM: + /* Convert (lo_sum (high FOO) FOO) to FOO. This is necessary so we + can add in an offset. find_split_point will split this address up + again if it doesn't match. */ + if (HAVE_lo_sum && GET_CODE (XEXP (x, 0)) == HIGH + && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))) + return XEXP (x, 1); + break; + + case PLUS: + /* (plus (xor (and <foo> (const_int pow2 - 1)) <c>) <-c>) + when c is (const_int (pow2 + 1) / 2) is a sign extension of a + bit-field and can be replaced by either a sign_extend or a + sign_extract. The `and' may be a zero_extend and the two + <c>, -<c> constants may be reversed. */ + if (GET_CODE (XEXP (x, 0)) == XOR + && is_a <scalar_int_mode> (mode, &int_mode) + && CONST_INT_P (XEXP (x, 1)) + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && INTVAL (XEXP (x, 1)) == -INTVAL (XEXP (XEXP (x, 0), 1)) + && ((i = exact_log2 (UINTVAL (XEXP (XEXP (x, 0), 1)))) >= 0 + || (i = exact_log2 (UINTVAL (XEXP (x, 1)))) >= 0) + && HWI_COMPUTABLE_MODE_P (int_mode) + && ((GET_CODE (XEXP (XEXP (x, 0), 0)) == AND + && CONST_INT_P (XEXP (XEXP (XEXP (x, 0), 0), 1)) + && (UINTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) + == (HOST_WIDE_INT_1U << (i + 1)) - 1)) + || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND + && known_eq ((GET_MODE_PRECISION + (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)))), + (unsigned int) i + 1)))) + return simplify_shift_const + (NULL_RTX, ASHIFTRT, int_mode, + simplify_shift_const (NULL_RTX, ASHIFT, int_mode, + XEXP (XEXP (XEXP (x, 0), 0), 0), + GET_MODE_PRECISION (int_mode) - (i + 1)), + GET_MODE_PRECISION (int_mode) - (i + 1)); + + /* If only the low-order bit of X is possibly nonzero, (plus x -1) + can become (ashiftrt (ashift (xor x 1) C) C) where C is + the bitsize of the mode - 1. This allows simplification of + "a = (b & 8) == 0;" */ + if (XEXP (x, 1) == constm1_rtx + && !REG_P (XEXP (x, 0)) + && ! (GET_CODE (XEXP (x, 0)) == SUBREG + && REG_P (SUBREG_REG (XEXP (x, 0)))) + && is_a <scalar_int_mode> (mode, &int_mode) + && nonzero_bits (XEXP (x, 0), int_mode) == 1) + return simplify_shift_const + (NULL_RTX, ASHIFTRT, int_mode, + simplify_shift_const (NULL_RTX, ASHIFT, int_mode, + gen_rtx_XOR (int_mode, XEXP (x, 0), + const1_rtx), + GET_MODE_PRECISION (int_mode) - 1), + GET_MODE_PRECISION (int_mode) - 1); + + /* If we are adding two things that have no bits in common, convert + the addition into an IOR. This will often be further simplified, + for example in cases like ((a & 1) + (a & 2)), which can + become a & 3. */ + + if (HWI_COMPUTABLE_MODE_P (mode) + && (nonzero_bits (XEXP (x, 0), mode) + & nonzero_bits (XEXP (x, 1), mode)) == 0) + { + /* Try to simplify the expression further. */ + rtx tor = simplify_gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1)); + temp = combine_simplify_rtx (tor, VOIDmode, in_dest, 0); + + /* If we could, great. If not, do not go ahead with the IOR + replacement, since PLUS appears in many special purpose + address arithmetic instructions. */ + if (GET_CODE (temp) != CLOBBER + && (GET_CODE (temp) != IOR + || ((XEXP (temp, 0) != XEXP (x, 0) + || XEXP (temp, 1) != XEXP (x, 1)) + && (XEXP (temp, 0) != XEXP (x, 1) + || XEXP (temp, 1) != XEXP (x, 0))))) + return temp; + } + + /* Canonicalize x + x into x << 1. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && rtx_equal_p (XEXP (x, 0), XEXP (x, 1)) + && !side_effects_p (XEXP (x, 0))) + return simplify_gen_binary (ASHIFT, mode, XEXP (x, 0), const1_rtx); + + break; + + case MINUS: + /* (minus <foo> (and <foo> (const_int -pow2))) becomes + (and <foo> (const_int pow2-1)) */ + if (is_a <scalar_int_mode> (mode, &int_mode) + && GET_CODE (XEXP (x, 1)) == AND + && CONST_INT_P (XEXP (XEXP (x, 1), 1)) + && pow2p_hwi (-UINTVAL (XEXP (XEXP (x, 1), 1))) + && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) + return simplify_and_const_int (NULL_RTX, int_mode, XEXP (x, 0), + -INTVAL (XEXP (XEXP (x, 1), 1)) - 1); + break; + + case MULT: + /* If we have (mult (plus A B) C), apply the distributive law and then + the inverse distributive law to see if things simplify. This + occurs mostly in addresses, often when unrolling loops. */ + + if (GET_CODE (XEXP (x, 0)) == PLUS) + { + rtx result = distribute_and_simplify_rtx (x, 0); + if (result) + return result; + } + + /* Try simplify a*(b/c) as (a*b)/c. */ + if (FLOAT_MODE_P (mode) && flag_associative_math + && GET_CODE (XEXP (x, 0)) == DIV) + { + rtx tem = simplify_binary_operation (MULT, mode, + XEXP (XEXP (x, 0), 0), + XEXP (x, 1)); + if (tem) + return simplify_gen_binary (DIV, mode, tem, XEXP (XEXP (x, 0), 1)); + } + break; + + case UDIV: + /* If this is a divide by a power of two, treat it as a shift if + its first operand is a shift. */ + if (is_a <scalar_int_mode> (mode, &int_mode) + && CONST_INT_P (XEXP (x, 1)) + && (i = exact_log2 (UINTVAL (XEXP (x, 1)))) >= 0 + && (GET_CODE (XEXP (x, 0)) == ASHIFT + || GET_CODE (XEXP (x, 0)) == LSHIFTRT + || GET_CODE (XEXP (x, 0)) == ASHIFTRT + || GET_CODE (XEXP (x, 0)) == ROTATE + || GET_CODE (XEXP (x, 0)) == ROTATERT)) + return simplify_shift_const (NULL_RTX, LSHIFTRT, int_mode, + XEXP (x, 0), i); + break; + + case EQ: case NE: + case GT: case GTU: case GE: case GEU: + case LT: case LTU: case LE: case LEU: + case UNEQ: case LTGT: + case UNGT: case UNGE: + case UNLT: case UNLE: + case UNORDERED: case ORDERED: + /* If the first operand is a condition code, we can't do anything + with it. */ + if (GET_CODE (XEXP (x, 0)) == COMPARE + || GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) != MODE_CC) + { + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + enum rtx_code new_code; + + if (GET_CODE (op0) == COMPARE) + op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); + + /* Simplify our comparison, if possible. */ + new_code = simplify_comparison (code, &op0, &op1); + + /* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X + if only the low-order bit is possibly nonzero in X (such as when + X is a ZERO_EXTRACT of one bit). Similarly, we can convert EQ to + (xor X 1) or (minus 1 X); we use the former. Finally, if X is + known to be either 0 or -1, NE becomes a NEG and EQ becomes + (plus X 1). + + Remove any ZERO_EXTRACT we made when thinking this was a + comparison. It may now be simpler to use, e.g., an AND. If a + ZERO_EXTRACT is indeed appropriate, it will be placed back by + the call to make_compound_operation in the SET case. + + Don't apply these optimizations if the caller would + prefer a comparison rather than a value. + E.g., for the condition in an IF_THEN_ELSE most targets need + an explicit comparison. */ + + if (in_cond) + ; + + else if (STORE_FLAG_VALUE == 1 + && new_code == NE + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && nonzero_bits (op0, int_mode) == 1) + return gen_lowpart (int_mode, + expand_compound_operation (op0)); + + else if (STORE_FLAG_VALUE == 1 + && new_code == NE + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && (num_sign_bit_copies (op0, int_mode) + == GET_MODE_PRECISION (int_mode))) + { + op0 = expand_compound_operation (op0); + return simplify_gen_unary (NEG, int_mode, + gen_lowpart (int_mode, op0), + int_mode); + } + + else if (STORE_FLAG_VALUE == 1 + && new_code == EQ + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && nonzero_bits (op0, int_mode) == 1) + { + op0 = expand_compound_operation (op0); + return simplify_gen_binary (XOR, int_mode, + gen_lowpart (int_mode, op0), + const1_rtx); + } + + else if (STORE_FLAG_VALUE == 1 + && new_code == EQ + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && (num_sign_bit_copies (op0, int_mode) + == GET_MODE_PRECISION (int_mode))) + { + op0 = expand_compound_operation (op0); + return plus_constant (int_mode, gen_lowpart (int_mode, op0), 1); + } + + /* If STORE_FLAG_VALUE is -1, we have cases similar to + those above. */ + if (in_cond) + ; + + else if (STORE_FLAG_VALUE == -1 + && new_code == NE + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && (num_sign_bit_copies (op0, int_mode) + == GET_MODE_PRECISION (int_mode))) + return gen_lowpart (int_mode, expand_compound_operation (op0)); + + else if (STORE_FLAG_VALUE == -1 + && new_code == NE + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && nonzero_bits (op0, int_mode) == 1) + { + op0 = expand_compound_operation (op0); + return simplify_gen_unary (NEG, int_mode, + gen_lowpart (int_mode, op0), + int_mode); + } + + else if (STORE_FLAG_VALUE == -1 + && new_code == EQ + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && (num_sign_bit_copies (op0, int_mode) + == GET_MODE_PRECISION (int_mode))) + { + op0 = expand_compound_operation (op0); + return simplify_gen_unary (NOT, int_mode, + gen_lowpart (int_mode, op0), + int_mode); + } + + /* If X is 0/1, (eq X 0) is X-1. */ + else if (STORE_FLAG_VALUE == -1 + && new_code == EQ + && is_int_mode (mode, &int_mode) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && nonzero_bits (op0, int_mode) == 1) + { + op0 = expand_compound_operation (op0); + return plus_constant (int_mode, gen_lowpart (int_mode, op0), -1); + } + + /* If STORE_FLAG_VALUE says to just test the sign bit and X has just + one bit that might be nonzero, we can convert (ne x 0) to + (ashift x c) where C puts the bit in the sign bit. Remove any + AND with STORE_FLAG_VALUE when we are done, since we are only + going to test the sign bit. */ + if (new_code == NE + && is_int_mode (mode, &int_mode) + && HWI_COMPUTABLE_MODE_P (int_mode) + && val_signbit_p (int_mode, STORE_FLAG_VALUE) + && op1 == const0_rtx + && int_mode == GET_MODE (op0) + && (i = exact_log2 (nonzero_bits (op0, int_mode))) >= 0) + { + x = simplify_shift_const (NULL_RTX, ASHIFT, int_mode, + expand_compound_operation (op0), + GET_MODE_PRECISION (int_mode) - 1 - i); + if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx) + return XEXP (x, 0); + else + return x; + } + + /* If the code changed, return a whole new comparison. + We also need to avoid using SUBST in cases where + simplify_comparison has widened a comparison with a CONST_INT, + since in that case the wider CONST_INT may fail the sanity + checks in do_SUBST. */ + if (new_code != code + || (CONST_INT_P (op1) + && GET_MODE (op0) != GET_MODE (XEXP (x, 0)) + && GET_MODE (op0) != GET_MODE (XEXP (x, 1)))) + return gen_rtx_fmt_ee (new_code, mode, op0, op1); + + /* Otherwise, keep this operation, but maybe change its operands. + This also converts (ne (compare FOO BAR) 0) to (ne FOO BAR). */ + SUBST (XEXP (x, 0), op0); + SUBST (XEXP (x, 1), op1); + } + break; + + case IF_THEN_ELSE: + return simplify_if_then_else (x); + + case ZERO_EXTRACT: + case SIGN_EXTRACT: + case ZERO_EXTEND: + case SIGN_EXTEND: + /* If we are processing SET_DEST, we are done. */ + if (in_dest) + return x; + + return expand_compound_operation (x); + + case SET: + return simplify_set (x); + + case AND: + case IOR: + return simplify_logical (x); + + case ASHIFT: + case LSHIFTRT: + case ASHIFTRT: + case ROTATE: + case ROTATERT: + /* If this is a shift by a constant amount, simplify it. */ + if (CONST_INT_P (XEXP (x, 1))) + return simplify_shift_const (x, code, mode, XEXP (x, 0), + INTVAL (XEXP (x, 1))); + + else if (SHIFT_COUNT_TRUNCATED && !REG_P (XEXP (x, 1))) + SUBST (XEXP (x, 1), + force_to_mode (XEXP (x, 1), GET_MODE (XEXP (x, 1)), + (HOST_WIDE_INT_1U + << exact_log2 (GET_MODE_UNIT_BITSIZE + (GET_MODE (x)))) + - 1, + 0)); + break; + case VEC_SELECT: + { + rtx trueop0 = XEXP (x, 0); + mode = GET_MODE (trueop0); + rtx trueop1 = XEXP (x, 1); + /* If we select a low-part subreg, return that. */ + if (vec_series_lowpart_p (GET_MODE (x), mode, trueop1)) + { + rtx new_rtx = lowpart_subreg (GET_MODE (x), trueop0, mode); + if (new_rtx != NULL_RTX) + return new_rtx; + } + } + + default: + break; + } + + return x; +} + +/* Simplify X, an IF_THEN_ELSE expression. Return the new expression. */ + +static rtx +simplify_if_then_else (rtx x) +{ + machine_mode mode = GET_MODE (x); + rtx cond = XEXP (x, 0); + rtx true_rtx = XEXP (x, 1); + rtx false_rtx = XEXP (x, 2); + enum rtx_code true_code = GET_CODE (cond); + int comparison_p = COMPARISON_P (cond); + rtx temp; + int i; + enum rtx_code false_code; + rtx reversed; + scalar_int_mode int_mode, inner_mode; + + /* Simplify storing of the truth value. */ + if (comparison_p && true_rtx == const_true_rtx && false_rtx == const0_rtx) + return simplify_gen_relational (true_code, mode, VOIDmode, + XEXP (cond, 0), XEXP (cond, 1)); + + /* Also when the truth value has to be reversed. */ + if (comparison_p + && true_rtx == const0_rtx && false_rtx == const_true_rtx + && (reversed = reversed_comparison (cond, mode))) + return reversed; + + /* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register used + in it is being compared against certain values. Get the true and false + comparisons and see if that says anything about the value of each arm. */ + + if (comparison_p + && ((false_code = reversed_comparison_code (cond, NULL)) + != UNKNOWN) + && REG_P (XEXP (cond, 0))) + { + HOST_WIDE_INT nzb; + rtx from = XEXP (cond, 0); + rtx true_val = XEXP (cond, 1); + rtx false_val = true_val; + int swapped = 0; + + /* If FALSE_CODE is EQ, swap the codes and arms. */ + + if (false_code == EQ) + { + swapped = 1, true_code = EQ, false_code = NE; + std::swap (true_rtx, false_rtx); + } + + scalar_int_mode from_mode; + if (is_a <scalar_int_mode> (GET_MODE (from), &from_mode)) + { + /* If we are comparing against zero and the expression being + tested has only a single bit that might be nonzero, that is + its value when it is not equal to zero. Similarly if it is + known to be -1 or 0. */ + if (true_code == EQ + && true_val == const0_rtx + && pow2p_hwi (nzb = nonzero_bits (from, from_mode))) + { + false_code = EQ; + false_val = gen_int_mode (nzb, from_mode); + } + else if (true_code == EQ + && true_val == const0_rtx + && (num_sign_bit_copies (from, from_mode) + == GET_MODE_PRECISION (from_mode))) + { + false_code = EQ; + false_val = constm1_rtx; + } + } + + /* Now simplify an arm if we know the value of the register in the + branch and it is used in the arm. Be careful due to the potential + of locally-shared RTL. */ + + if (reg_mentioned_p (from, true_rtx)) + true_rtx = subst (known_cond (copy_rtx (true_rtx), true_code, + from, true_val), + pc_rtx, pc_rtx, 0, 0, 0); + if (reg_mentioned_p (from, false_rtx)) + false_rtx = subst (known_cond (copy_rtx (false_rtx), false_code, + from, false_val), + pc_rtx, pc_rtx, 0, 0, 0); + + SUBST (XEXP (x, 1), swapped ? false_rtx : true_rtx); + SUBST (XEXP (x, 2), swapped ? true_rtx : false_rtx); + + true_rtx = XEXP (x, 1); + false_rtx = XEXP (x, 2); + true_code = GET_CODE (cond); + } + + /* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be + reversed, do so to avoid needing two sets of patterns for + subtract-and-branch insns. Similarly if we have a constant in the true + arm, the false arm is the same as the first operand of the comparison, or + the false arm is more complicated than the true arm. */ + + if (comparison_p + && reversed_comparison_code (cond, NULL) != UNKNOWN + && (true_rtx == pc_rtx + || (CONSTANT_P (true_rtx) + && !CONST_INT_P (false_rtx) && false_rtx != pc_rtx) + || true_rtx == const0_rtx + || (OBJECT_P (true_rtx) && !OBJECT_P (false_rtx)) + || (GET_CODE (true_rtx) == SUBREG && OBJECT_P (SUBREG_REG (true_rtx)) + && !OBJECT_P (false_rtx)) + || reg_mentioned_p (true_rtx, false_rtx) + || rtx_equal_p (false_rtx, XEXP (cond, 0)))) + { + SUBST (XEXP (x, 0), reversed_comparison (cond, GET_MODE (cond))); + SUBST (XEXP (x, 1), false_rtx); + SUBST (XEXP (x, 2), true_rtx); + + std::swap (true_rtx, false_rtx); + cond = XEXP (x, 0); + + /* It is possible that the conditional has been simplified out. */ + true_code = GET_CODE (cond); + comparison_p = COMPARISON_P (cond); + } + + /* If the two arms are identical, we don't need the comparison. */ + + if (rtx_equal_p (true_rtx, false_rtx) && ! side_effects_p (cond)) + return true_rtx; + + /* Convert a == b ? b : a to "a". */ + if (true_code == EQ && ! side_effects_p (cond) + && !HONOR_NANS (mode) + && rtx_equal_p (XEXP (cond, 0), false_rtx) + && rtx_equal_p (XEXP (cond, 1), true_rtx)) + return false_rtx; + else if (true_code == NE && ! side_effects_p (cond) + && !HONOR_NANS (mode) + && rtx_equal_p (XEXP (cond, 0), true_rtx) + && rtx_equal_p (XEXP (cond, 1), false_rtx)) + return true_rtx; + + /* Look for cases where we have (abs x) or (neg (abs X)). */ + + if (GET_MODE_CLASS (mode) == MODE_INT + && comparison_p + && XEXP (cond, 1) == const0_rtx + && GET_CODE (false_rtx) == NEG + && rtx_equal_p (true_rtx, XEXP (false_rtx, 0)) + && rtx_equal_p (true_rtx, XEXP (cond, 0)) + && ! side_effects_p (true_rtx)) + switch (true_code) + { + case GT: + case GE: + return simplify_gen_unary (ABS, mode, true_rtx, mode); + case LT: + case LE: + return + simplify_gen_unary (NEG, mode, + simplify_gen_unary (ABS, mode, true_rtx, mode), + mode); + default: + break; + } + + /* Look for MIN or MAX. */ + + if ((! FLOAT_MODE_P (mode) + || (flag_unsafe_math_optimizations + && !HONOR_NANS (mode) + && !HONOR_SIGNED_ZEROS (mode))) + && comparison_p + && rtx_equal_p (XEXP (cond, 0), true_rtx) + && rtx_equal_p (XEXP (cond, 1), false_rtx) + && ! side_effects_p (cond)) + switch (true_code) + { + case GE: + case GT: + return simplify_gen_binary (SMAX, mode, true_rtx, false_rtx); + case LE: + case LT: + return simplify_gen_binary (SMIN, mode, true_rtx, false_rtx); + case GEU: + case GTU: + return simplify_gen_binary (UMAX, mode, true_rtx, false_rtx); + case LEU: + case LTU: + return simplify_gen_binary (UMIN, mode, true_rtx, false_rtx); + default: + break; + } + + /* If we have (if_then_else COND (OP Z C1) Z) and OP is an identity when its + second operand is zero, this can be done as (OP Z (mult COND C2)) where + C2 = C1 * STORE_FLAG_VALUE. Similarly if OP has an outer ZERO_EXTEND or + SIGN_EXTEND as long as Z is already extended (so we don't destroy it). + We can do this kind of thing in some cases when STORE_FLAG_VALUE is + neither 1 or -1, but it isn't worth checking for. */ + + if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && comparison_p + && is_int_mode (mode, &int_mode) + && ! side_effects_p (x)) + { + rtx t = make_compound_operation (true_rtx, SET); + rtx f = make_compound_operation (false_rtx, SET); + rtx cond_op0 = XEXP (cond, 0); + rtx cond_op1 = XEXP (cond, 1); + enum rtx_code op = UNKNOWN, extend_op = UNKNOWN; + scalar_int_mode m = int_mode; + rtx z = 0, c1 = NULL_RTX; + + if ((GET_CODE (t) == PLUS || GET_CODE (t) == MINUS + || GET_CODE (t) == IOR || GET_CODE (t) == XOR + || GET_CODE (t) == ASHIFT + || GET_CODE (t) == LSHIFTRT || GET_CODE (t) == ASHIFTRT) + && rtx_equal_p (XEXP (t, 0), f)) + c1 = XEXP (t, 1), op = GET_CODE (t), z = f; + + /* If an identity-zero op is commutative, check whether there + would be a match if we swapped the operands. */ + else if ((GET_CODE (t) == PLUS || GET_CODE (t) == IOR + || GET_CODE (t) == XOR) + && rtx_equal_p (XEXP (t, 1), f)) + c1 = XEXP (t, 0), op = GET_CODE (t), z = f; + else if (GET_CODE (t) == SIGN_EXTEND + && is_a <scalar_int_mode> (GET_MODE (XEXP (t, 0)), &inner_mode) + && (GET_CODE (XEXP (t, 0)) == PLUS + || GET_CODE (XEXP (t, 0)) == MINUS + || GET_CODE (XEXP (t, 0)) == IOR + || GET_CODE (XEXP (t, 0)) == XOR + || GET_CODE (XEXP (t, 0)) == ASHIFT + || GET_CODE (XEXP (t, 0)) == LSHIFTRT + || GET_CODE (XEXP (t, 0)) == ASHIFTRT) + && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG + && subreg_lowpart_p (XEXP (XEXP (t, 0), 0)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f) + && (num_sign_bit_copies (f, GET_MODE (f)) + > (unsigned int) + (GET_MODE_PRECISION (int_mode) + - GET_MODE_PRECISION (inner_mode)))) + { + c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0)); + extend_op = SIGN_EXTEND; + m = inner_mode; + } + else if (GET_CODE (t) == SIGN_EXTEND + && is_a <scalar_int_mode> (GET_MODE (XEXP (t, 0)), &inner_mode) + && (GET_CODE (XEXP (t, 0)) == PLUS + || GET_CODE (XEXP (t, 0)) == IOR + || GET_CODE (XEXP (t, 0)) == XOR) + && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG + && subreg_lowpart_p (XEXP (XEXP (t, 0), 1)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f) + && (num_sign_bit_copies (f, GET_MODE (f)) + > (unsigned int) + (GET_MODE_PRECISION (int_mode) + - GET_MODE_PRECISION (inner_mode)))) + { + c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0)); + extend_op = SIGN_EXTEND; + m = inner_mode; + } + else if (GET_CODE (t) == ZERO_EXTEND + && is_a <scalar_int_mode> (GET_MODE (XEXP (t, 0)), &inner_mode) + && (GET_CODE (XEXP (t, 0)) == PLUS + || GET_CODE (XEXP (t, 0)) == MINUS + || GET_CODE (XEXP (t, 0)) == IOR + || GET_CODE (XEXP (t, 0)) == XOR + || GET_CODE (XEXP (t, 0)) == ASHIFT + || GET_CODE (XEXP (t, 0)) == LSHIFTRT + || GET_CODE (XEXP (t, 0)) == ASHIFTRT) + && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG + && HWI_COMPUTABLE_MODE_P (int_mode) + && subreg_lowpart_p (XEXP (XEXP (t, 0), 0)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f) + && ((nonzero_bits (f, GET_MODE (f)) + & ~GET_MODE_MASK (inner_mode)) + == 0)) + { + c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0)); + extend_op = ZERO_EXTEND; + m = inner_mode; + } + else if (GET_CODE (t) == ZERO_EXTEND + && is_a <scalar_int_mode> (GET_MODE (XEXP (t, 0)), &inner_mode) + && (GET_CODE (XEXP (t, 0)) == PLUS + || GET_CODE (XEXP (t, 0)) == IOR + || GET_CODE (XEXP (t, 0)) == XOR) + && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG + && HWI_COMPUTABLE_MODE_P (int_mode) + && subreg_lowpart_p (XEXP (XEXP (t, 0), 1)) + && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f) + && ((nonzero_bits (f, GET_MODE (f)) + & ~GET_MODE_MASK (inner_mode)) + == 0)) + { + c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0)); + extend_op = ZERO_EXTEND; + m = inner_mode; + } + + if (z) + { + machine_mode cm = m; + if ((op == ASHIFT || op == LSHIFTRT || op == ASHIFTRT) + && GET_MODE (c1) != VOIDmode) + cm = GET_MODE (c1); + temp = subst (simplify_gen_relational (true_code, cm, VOIDmode, + cond_op0, cond_op1), + pc_rtx, pc_rtx, 0, 0, 0); + temp = simplify_gen_binary (MULT, cm, temp, + simplify_gen_binary (MULT, cm, c1, + const_true_rtx)); + temp = subst (temp, pc_rtx, pc_rtx, 0, 0, 0); + temp = simplify_gen_binary (op, m, gen_lowpart (m, z), temp); + + if (extend_op != UNKNOWN) + temp = simplify_gen_unary (extend_op, int_mode, temp, m); + + return temp; + } + } + + /* If we have (if_then_else (ne A 0) C1 0) and either A is known to be 0 or + 1 and C1 is a single bit or A is known to be 0 or -1 and C1 is the + negation of a single bit, we can convert this operation to a shift. We + can actually do this more generally, but it doesn't seem worth it. */ + + if (true_code == NE + && is_a <scalar_int_mode> (mode, &int_mode) + && XEXP (cond, 1) == const0_rtx + && false_rtx == const0_rtx + && CONST_INT_P (true_rtx) + && ((nonzero_bits (XEXP (cond, 0), int_mode) == 1 + && (i = exact_log2 (UINTVAL (true_rtx))) >= 0) + || ((num_sign_bit_copies (XEXP (cond, 0), int_mode) + == GET_MODE_PRECISION (int_mode)) + && (i = exact_log2 (-UINTVAL (true_rtx))) >= 0))) + return + simplify_shift_const (NULL_RTX, ASHIFT, int_mode, + gen_lowpart (int_mode, XEXP (cond, 0)), i); + + /* (IF_THEN_ELSE (NE A 0) C1 0) is A or a zero-extend of A if the only + non-zero bit in A is C1. */ + if (true_code == NE && XEXP (cond, 1) == const0_rtx + && false_rtx == const0_rtx && CONST_INT_P (true_rtx) + && is_a <scalar_int_mode> (mode, &int_mode) + && is_a <scalar_int_mode> (GET_MODE (XEXP (cond, 0)), &inner_mode) + && (UINTVAL (true_rtx) & GET_MODE_MASK (int_mode)) + == nonzero_bits (XEXP (cond, 0), inner_mode) + && (i = exact_log2 (UINTVAL (true_rtx) & GET_MODE_MASK (int_mode))) >= 0) + { + rtx val = XEXP (cond, 0); + if (inner_mode == int_mode) + return val; + else if (GET_MODE_PRECISION (inner_mode) < GET_MODE_PRECISION (int_mode)) + return simplify_gen_unary (ZERO_EXTEND, int_mode, val, inner_mode); + } + + return x; +} + +/* Simplify X, a SET expression. Return the new expression. */ + +static rtx +simplify_set (rtx x) +{ + rtx src = SET_SRC (x); + rtx dest = SET_DEST (x); + machine_mode mode + = GET_MODE (src) != VOIDmode ? GET_MODE (src) : GET_MODE (dest); + rtx_insn *other_insn; + rtx *cc_use; + scalar_int_mode int_mode; + + /* (set (pc) (return)) gets written as (return). */ + if (GET_CODE (dest) == PC && ANY_RETURN_P (src)) + return src; + + /* Now that we know for sure which bits of SRC we are using, see if we can + simplify the expression for the object knowing that we only need the + low-order bits. */ + + if (GET_MODE_CLASS (mode) == MODE_INT && HWI_COMPUTABLE_MODE_P (mode)) + { + src = force_to_mode (src, mode, HOST_WIDE_INT_M1U, 0); + SUBST (SET_SRC (x), src); + } + + /* If the source is a COMPARE, look for the use of the comparison result + and try to simplify it unless we already have used undobuf.other_insn. */ + if ((GET_MODE_CLASS (mode) == MODE_CC || GET_CODE (src) == COMPARE) + && (cc_use = find_single_use (dest, subst_insn, &other_insn)) != 0 + && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn) + && COMPARISON_P (*cc_use) + && rtx_equal_p (XEXP (*cc_use, 0), dest)) + { + enum rtx_code old_code = GET_CODE (*cc_use); + enum rtx_code new_code; + rtx op0, op1, tmp; + int other_changed = 0; + rtx inner_compare = NULL_RTX; + machine_mode compare_mode = GET_MODE (dest); + + if (GET_CODE (src) == COMPARE) + { + op0 = XEXP (src, 0), op1 = XEXP (src, 1); + if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) + { + inner_compare = op0; + op0 = XEXP (inner_compare, 0), op1 = XEXP (inner_compare, 1); + } + } + else + op0 = src, op1 = CONST0_RTX (GET_MODE (src)); + + tmp = simplify_relational_operation (old_code, compare_mode, VOIDmode, + op0, op1); + if (!tmp) + new_code = old_code; + else if (!CONSTANT_P (tmp)) + { + new_code = GET_CODE (tmp); + op0 = XEXP (tmp, 0); + op1 = XEXP (tmp, 1); + } + else + { + rtx pat = PATTERN (other_insn); + undobuf.other_insn = other_insn; + SUBST (*cc_use, tmp); + + /* Attempt to simplify CC user. */ + if (GET_CODE (pat) == SET) + { + rtx new_rtx = simplify_rtx (SET_SRC (pat)); + if (new_rtx != NULL_RTX) + SUBST (SET_SRC (pat), new_rtx); + } + + /* Convert X into a no-op move. */ + SUBST (SET_DEST (x), pc_rtx); + SUBST (SET_SRC (x), pc_rtx); + return x; + } + + /* Simplify our comparison, if possible. */ + new_code = simplify_comparison (new_code, &op0, &op1); + +#ifdef SELECT_CC_MODE + /* If this machine has CC modes other than CCmode, check to see if we + need to use a different CC mode here. */ + if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) + compare_mode = GET_MODE (op0); + else if (inner_compare + && GET_MODE_CLASS (GET_MODE (inner_compare)) == MODE_CC + && new_code == old_code + && op0 == XEXP (inner_compare, 0) + && op1 == XEXP (inner_compare, 1)) + compare_mode = GET_MODE (inner_compare); + else + compare_mode = SELECT_CC_MODE (new_code, op0, op1); + + /* If the mode changed, we have to change SET_DEST, the mode in the + compare, and the mode in the place SET_DEST is used. If SET_DEST is + a hard register, just build new versions with the proper mode. If it + is a pseudo, we lose unless it is only time we set the pseudo, in + which case we can safely change its mode. */ + if (compare_mode != GET_MODE (dest)) + { + if (can_change_dest_mode (dest, 0, compare_mode)) + { + unsigned int regno = REGNO (dest); + rtx new_dest; + + if (regno < FIRST_PSEUDO_REGISTER) + new_dest = gen_rtx_REG (compare_mode, regno); + else + { + SUBST_MODE (regno_reg_rtx[regno], compare_mode); + new_dest = regno_reg_rtx[regno]; + } + + SUBST (SET_DEST (x), new_dest); + SUBST (XEXP (*cc_use, 0), new_dest); + other_changed = 1; + + dest = new_dest; + } + } +#endif /* SELECT_CC_MODE */ + + /* If the code changed, we have to build a new comparison in + undobuf.other_insn. */ + if (new_code != old_code) + { + int other_changed_previously = other_changed; + unsigned HOST_WIDE_INT mask; + rtx old_cc_use = *cc_use; + + SUBST (*cc_use, gen_rtx_fmt_ee (new_code, GET_MODE (*cc_use), + dest, const0_rtx)); + other_changed = 1; + + /* If the only change we made was to change an EQ into an NE or + vice versa, OP0 has only one bit that might be nonzero, and OP1 + is zero, check if changing the user of the condition code will + produce a valid insn. If it won't, we can keep the original code + in that insn by surrounding our operation with an XOR. */ + + if (((old_code == NE && new_code == EQ) + || (old_code == EQ && new_code == NE)) + && ! other_changed_previously && op1 == const0_rtx + && HWI_COMPUTABLE_MODE_P (GET_MODE (op0)) + && pow2p_hwi (mask = nonzero_bits (op0, GET_MODE (op0)))) + { + rtx pat = PATTERN (other_insn), note = 0; + + if ((recog_for_combine (&pat, other_insn, ¬e) < 0 + && ! check_asm_operands (pat))) + { + *cc_use = old_cc_use; + other_changed = 0; + + op0 = simplify_gen_binary (XOR, GET_MODE (op0), op0, + gen_int_mode (mask, + GET_MODE (op0))); + } + } + } + + if (other_changed) + undobuf.other_insn = other_insn; + + /* Don't generate a compare of a CC with 0, just use that CC. */ + if (GET_MODE (op0) == compare_mode && op1 == const0_rtx) + { + SUBST (SET_SRC (x), op0); + src = SET_SRC (x); + } + /* Otherwise, if we didn't previously have the same COMPARE we + want, create it from scratch. */ + else if (GET_CODE (src) != COMPARE || GET_MODE (src) != compare_mode + || XEXP (src, 0) != op0 || XEXP (src, 1) != op1) + { + SUBST (SET_SRC (x), gen_rtx_COMPARE (compare_mode, op0, op1)); + src = SET_SRC (x); + } + } + else + { + /* Get SET_SRC in a form where we have placed back any + compound expressions. Then do the checks below. */ + src = make_compound_operation (src, SET); + SUBST (SET_SRC (x), src); + } + + /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some operation, + and X being a REG or (subreg (reg)), we may be able to convert this to + (set (subreg:m2 x) (op)). + + We can always do this if M1 is narrower than M2 because that means that + we only care about the low bits of the result. + + However, on machines without WORD_REGISTER_OPERATIONS defined, we cannot + perform a narrower operation than requested since the high-order bits will + be undefined. On machine where it is defined, this transformation is safe + as long as M1 and M2 have the same number of words. */ + + if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src) + && !OBJECT_P (SUBREG_REG (src)) + && (known_equal_after_align_up + (GET_MODE_SIZE (GET_MODE (src)), + GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))), + UNITS_PER_WORD)) + && (WORD_REGISTER_OPERATIONS || !paradoxical_subreg_p (src)) + && ! (REG_P (dest) && REGNO (dest) < FIRST_PSEUDO_REGISTER + && !REG_CAN_CHANGE_MODE_P (REGNO (dest), + GET_MODE (SUBREG_REG (src)), + GET_MODE (src))) + && (REG_P (dest) + || (GET_CODE (dest) == SUBREG + && REG_P (SUBREG_REG (dest))))) + { + SUBST (SET_DEST (x), + gen_lowpart (GET_MODE (SUBREG_REG (src)), + dest)); + SUBST (SET_SRC (x), SUBREG_REG (src)); + + src = SET_SRC (x), dest = SET_DEST (x); + } + + /* If we have (set FOO (subreg:M (mem:N BAR) 0)) with M wider than N, this + would require a paradoxical subreg. Replace the subreg with a + zero_extend to avoid the reload that would otherwise be required. + Don't do this unless we have a scalar integer mode, otherwise the + transformation is incorrect. */ + + enum rtx_code extend_op; + if (paradoxical_subreg_p (src) + && MEM_P (SUBREG_REG (src)) + && SCALAR_INT_MODE_P (GET_MODE (src)) + && (extend_op = load_extend_op (GET_MODE (SUBREG_REG (src)))) != UNKNOWN) + { + SUBST (SET_SRC (x), + gen_rtx_fmt_e (extend_op, GET_MODE (src), SUBREG_REG (src))); + + src = SET_SRC (x); + } + + /* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE, and we + are comparing an item known to be 0 or -1 against 0, use a logical + operation instead. Check for one of the arms being an IOR of the other + arm with some value. We compute three terms to be IOR'ed together. In + practice, at most two will be nonzero. Then we do the IOR's. */ + + if (GET_CODE (dest) != PC + && GET_CODE (src) == IF_THEN_ELSE + && is_int_mode (GET_MODE (src), &int_mode) + && (GET_CODE (XEXP (src, 0)) == EQ || GET_CODE (XEXP (src, 0)) == NE) + && XEXP (XEXP (src, 0), 1) == const0_rtx + && int_mode == GET_MODE (XEXP (XEXP (src, 0), 0)) + && (!HAVE_conditional_move + || ! can_conditionally_move_p (int_mode)) + && (num_sign_bit_copies (XEXP (XEXP (src, 0), 0), int_mode) + == GET_MODE_PRECISION (int_mode)) + && ! side_effects_p (src)) + { + rtx true_rtx = (GET_CODE (XEXP (src, 0)) == NE + ? XEXP (src, 1) : XEXP (src, 2)); + rtx false_rtx = (GET_CODE (XEXP (src, 0)) == NE + ? XEXP (src, 2) : XEXP (src, 1)); + rtx term1 = const0_rtx, term2, term3; + + if (GET_CODE (true_rtx) == IOR + && rtx_equal_p (XEXP (true_rtx, 0), false_rtx)) + term1 = false_rtx, true_rtx = XEXP (true_rtx, 1), false_rtx = const0_rtx; + else if (GET_CODE (true_rtx) == IOR + && rtx_equal_p (XEXP (true_rtx, 1), false_rtx)) + term1 = false_rtx, true_rtx = XEXP (true_rtx, 0), false_rtx = const0_rtx; + else if (GET_CODE (false_rtx) == IOR + && rtx_equal_p (XEXP (false_rtx, 0), true_rtx)) + term1 = true_rtx, false_rtx = XEXP (false_rtx, 1), true_rtx = const0_rtx; + else if (GET_CODE (false_rtx) == IOR + && rtx_equal_p (XEXP (false_rtx, 1), true_rtx)) + term1 = true_rtx, false_rtx = XEXP (false_rtx, 0), true_rtx = const0_rtx; + + term2 = simplify_gen_binary (AND, int_mode, + XEXP (XEXP (src, 0), 0), true_rtx); + term3 = simplify_gen_binary (AND, int_mode, + simplify_gen_unary (NOT, int_mode, + XEXP (XEXP (src, 0), 0), + int_mode), + false_rtx); + + SUBST (SET_SRC (x), + simplify_gen_binary (IOR, int_mode, + simplify_gen_binary (IOR, int_mode, + term1, term2), + term3)); + + src = SET_SRC (x); + } + + /* If either SRC or DEST is a CLOBBER of (const_int 0), make this + whole thing fail. */ + if (GET_CODE (src) == CLOBBER && XEXP (src, 0) == const0_rtx) + return src; + else if (GET_CODE (dest) == CLOBBER && XEXP (dest, 0) == const0_rtx) + return dest; + else + /* Convert this into a field assignment operation, if possible. */ + return make_field_assignment (x); +} + +/* Simplify, X, and AND, IOR, or XOR operation, and return the simplified + result. */ + +static rtx +simplify_logical (rtx x) +{ + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + scalar_int_mode mode; + + switch (GET_CODE (x)) + { + case AND: + /* We can call simplify_and_const_int only if we don't lose + any (sign) bits when converting INTVAL (op1) to + "unsigned HOST_WIDE_INT". */ + if (is_a <scalar_int_mode> (GET_MODE (x), &mode) + && CONST_INT_P (op1) + && (HWI_COMPUTABLE_MODE_P (mode) + || INTVAL (op1) > 0)) + { + x = simplify_and_const_int (x, mode, op0, INTVAL (op1)); + if (GET_CODE (x) != AND) + return x; + + op0 = XEXP (x, 0); + op1 = XEXP (x, 1); + } + + /* If we have any of (and (ior A B) C) or (and (xor A B) C), + apply the distributive law and then the inverse distributive + law to see if things simplify. */ + if (GET_CODE (op0) == IOR || GET_CODE (op0) == XOR) + { + rtx result = distribute_and_simplify_rtx (x, 0); + if (result) + return result; + } + if (GET_CODE (op1) == IOR || GET_CODE (op1) == XOR) + { + rtx result = distribute_and_simplify_rtx (x, 1); + if (result) + return result; + } + break; + + case IOR: + /* If we have (ior (and A B) C), apply the distributive law and then + the inverse distributive law to see if things simplify. */ + + if (GET_CODE (op0) == AND) + { + rtx result = distribute_and_simplify_rtx (x, 0); + if (result) + return result; + } + + if (GET_CODE (op1) == AND) + { + rtx result = distribute_and_simplify_rtx (x, 1); + if (result) + return result; + } + break; + + default: + gcc_unreachable (); + } + + return x; +} + +/* We consider ZERO_EXTRACT, SIGN_EXTRACT, and SIGN_EXTEND as "compound + operations" because they can be replaced with two more basic operations. + ZERO_EXTEND is also considered "compound" because it can be replaced with + an AND operation, which is simpler, though only one operation. + + The function expand_compound_operation is called with an rtx expression + and will convert it to the appropriate shifts and AND operations, + simplifying at each stage. + + The function make_compound_operation is called to convert an expression + consisting of shifts and ANDs into the equivalent compound expression. + It is the inverse of this function, loosely speaking. */ + +static rtx +expand_compound_operation (rtx x) +{ + unsigned HOST_WIDE_INT pos = 0, len; + int unsignedp = 0; + unsigned int modewidth; + rtx tem; + scalar_int_mode inner_mode; + + switch (GET_CODE (x)) + { + case ZERO_EXTEND: + unsignedp = 1; + /* FALLTHRU */ + case SIGN_EXTEND: + /* We can't necessarily use a const_int for a multiword mode; + it depends on implicitly extending the value. + Since we don't know the right way to extend it, + we can't tell whether the implicit way is right. + + Even for a mode that is no wider than a const_int, + we can't win, because we need to sign extend one of its bits through + the rest of it, and we don't know which bit. */ + if (CONST_INT_P (XEXP (x, 0))) + return x; + + /* Reject modes that aren't scalar integers because turning vector + or complex modes into shifts causes problems. */ + if (!is_a <scalar_int_mode> (GET_MODE (XEXP (x, 0)), &inner_mode)) + return x; + + /* Return if (subreg:MODE FROM 0) is not a safe replacement for + (zero_extend:MODE FROM) or (sign_extend:MODE FROM). It is for any MEM + because (SUBREG (MEM...)) is guaranteed to cause the MEM to be + reloaded. If not for that, MEM's would very rarely be safe. + + Reject modes bigger than a word, because we might not be able + to reference a two-register group starting with an arbitrary register + (and currently gen_lowpart might crash for a SUBREG). */ + + if (GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD) + return x; + + len = GET_MODE_PRECISION (inner_mode); + /* If the inner object has VOIDmode (the only way this can happen + is if it is an ASM_OPERANDS), we can't do anything since we don't + know how much masking to do. */ + if (len == 0) + return x; + + break; + + case ZERO_EXTRACT: + unsignedp = 1; + + /* fall through */ + + case SIGN_EXTRACT: + /* If the operand is a CLOBBER, just return it. */ + if (GET_CODE (XEXP (x, 0)) == CLOBBER) + return XEXP (x, 0); + + if (!CONST_INT_P (XEXP (x, 1)) + || !CONST_INT_P (XEXP (x, 2))) + return x; + + /* Reject modes that aren't scalar integers because turning vector + or complex modes into shifts causes problems. */ + if (!is_a <scalar_int_mode> (GET_MODE (XEXP (x, 0)), &inner_mode)) + return x; + + len = INTVAL (XEXP (x, 1)); + pos = INTVAL (XEXP (x, 2)); + + /* This should stay within the object being extracted, fail otherwise. */ + if (len + pos > GET_MODE_PRECISION (inner_mode)) + return x; + + if (BITS_BIG_ENDIAN) + pos = GET_MODE_PRECISION (inner_mode) - len - pos; + + break; + + default: + return x; + } + + /* We've rejected non-scalar operations by now. */ + scalar_int_mode mode = as_a <scalar_int_mode> (GET_MODE (x)); + + /* Convert sign extension to zero extension, if we know that the high + bit is not set, as this is easier to optimize. It will be converted + back to cheaper alternative in make_extraction. */ + if (GET_CODE (x) == SIGN_EXTEND + && HWI_COMPUTABLE_MODE_P (mode) + && ((nonzero_bits (XEXP (x, 0), inner_mode) + & ~(((unsigned HOST_WIDE_INT) GET_MODE_MASK (inner_mode)) >> 1)) + == 0)) + { + rtx temp = gen_rtx_ZERO_EXTEND (mode, XEXP (x, 0)); + rtx temp2 = expand_compound_operation (temp); + + /* Make sure this is a profitable operation. */ + if (set_src_cost (x, mode, optimize_this_for_speed_p) + > set_src_cost (temp2, mode, optimize_this_for_speed_p)) + return temp2; + else if (set_src_cost (x, mode, optimize_this_for_speed_p) + > set_src_cost (temp, mode, optimize_this_for_speed_p)) + return temp; + else + return x; + } + + /* We can optimize some special cases of ZERO_EXTEND. */ + if (GET_CODE (x) == ZERO_EXTEND) + { + /* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI if we + know that the last value didn't have any inappropriate bits + set. */ + if (GET_CODE (XEXP (x, 0)) == TRUNCATE + && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode + && HWI_COMPUTABLE_MODE_P (mode) + && (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) + & ~GET_MODE_MASK (inner_mode)) == 0) + return XEXP (XEXP (x, 0), 0); + + /* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)). */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_MODE (SUBREG_REG (XEXP (x, 0))) == mode + && subreg_lowpart_p (XEXP (x, 0)) + && HWI_COMPUTABLE_MODE_P (mode) + && (nonzero_bits (SUBREG_REG (XEXP (x, 0)), mode) + & ~GET_MODE_MASK (inner_mode)) == 0) + return SUBREG_REG (XEXP (x, 0)); + + /* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI when foo + is a comparison and STORE_FLAG_VALUE permits. This is like + the first case, but it works even when MODE is larger + than HOST_WIDE_INT. */ + if (GET_CODE (XEXP (x, 0)) == TRUNCATE + && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode + && COMPARISON_P (XEXP (XEXP (x, 0), 0)) + && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE & ~GET_MODE_MASK (inner_mode)) == 0) + return XEXP (XEXP (x, 0), 0); + + /* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)). */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_MODE (SUBREG_REG (XEXP (x, 0))) == mode + && subreg_lowpart_p (XEXP (x, 0)) + && COMPARISON_P (SUBREG_REG (XEXP (x, 0))) + && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT + && (STORE_FLAG_VALUE & ~GET_MODE_MASK (inner_mode)) == 0) + return SUBREG_REG (XEXP (x, 0)); + + } + + /* If we reach here, we want to return a pair of shifts. The inner + shift is a left shift of BITSIZE - POS - LEN bits. The outer + shift is a right shift of BITSIZE - LEN bits. It is arithmetic or + logical depending on the value of UNSIGNEDP. + + If this was a ZERO_EXTEND or ZERO_EXTRACT, this pair of shifts will be + converted into an AND of a shift. + + We must check for the case where the left shift would have a negative + count. This can happen in a case like (x >> 31) & 255 on machines + that can't shift by a constant. On those machines, we would first + combine the shift with the AND to produce a variable-position + extraction. Then the constant of 31 would be substituted in + to produce such a position. */ + + modewidth = GET_MODE_PRECISION (mode); + if (modewidth >= pos + len) + { + tem = gen_lowpart (mode, XEXP (x, 0)); + if (!tem || GET_CODE (tem) == CLOBBER) + return x; + tem = simplify_shift_const (NULL_RTX, ASHIFT, mode, + tem, modewidth - pos - len); + tem = simplify_shift_const (NULL_RTX, unsignedp ? LSHIFTRT : ASHIFTRT, + mode, tem, modewidth - len); + } + else if (unsignedp && len < HOST_BITS_PER_WIDE_INT) + { + tem = simplify_shift_const (NULL_RTX, LSHIFTRT, inner_mode, + XEXP (x, 0), pos); + tem = gen_lowpart (mode, tem); + if (!tem || GET_CODE (tem) == CLOBBER) + return x; + tem = simplify_and_const_int (NULL_RTX, mode, tem, + (HOST_WIDE_INT_1U << len) - 1); + } + else + /* Any other cases we can't handle. */ + return x; + + /* If we couldn't do this for some reason, return the original + expression. */ + if (GET_CODE (tem) == CLOBBER) + return x; + + return tem; +} + +/* X is a SET which contains an assignment of one object into + a part of another (such as a bit-field assignment, STRICT_LOW_PART, + or certain SUBREGS). If possible, convert it into a series of + logical operations. + + We half-heartedly support variable positions, but do not at all + support variable lengths. */ + +static const_rtx +expand_field_assignment (const_rtx x) +{ + rtx inner; + rtx pos; /* Always counts from low bit. */ + int len, inner_len; + rtx mask, cleared, masked; + scalar_int_mode compute_mode; + + /* Loop until we find something we can't simplify. */ + while (1) + { + if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART + && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG) + { + rtx x0 = XEXP (SET_DEST (x), 0); + if (!GET_MODE_PRECISION (GET_MODE (x0)).is_constant (&len)) + break; + inner = SUBREG_REG (XEXP (SET_DEST (x), 0)); + pos = gen_int_mode (subreg_lsb (XEXP (SET_DEST (x), 0)), + MAX_MODE_INT); + } + else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT + && CONST_INT_P (XEXP (SET_DEST (x), 1))) + { + inner = XEXP (SET_DEST (x), 0); + if (!GET_MODE_PRECISION (GET_MODE (inner)).is_constant (&inner_len)) + break; + + len = INTVAL (XEXP (SET_DEST (x), 1)); + pos = XEXP (SET_DEST (x), 2); + + /* A constant position should stay within the width of INNER. */ + if (CONST_INT_P (pos) && INTVAL (pos) + len > inner_len) + break; + + if (BITS_BIG_ENDIAN) + { + if (CONST_INT_P (pos)) + pos = GEN_INT (inner_len - len - INTVAL (pos)); + else if (GET_CODE (pos) == MINUS + && CONST_INT_P (XEXP (pos, 1)) + && INTVAL (XEXP (pos, 1)) == inner_len - len) + /* If position is ADJUST - X, new position is X. */ + pos = XEXP (pos, 0); + else + pos = simplify_gen_binary (MINUS, GET_MODE (pos), + gen_int_mode (inner_len - len, + GET_MODE (pos)), + pos); + } + } + + /* If the destination is a subreg that overwrites the whole of the inner + register, we can move the subreg to the source. */ + else if (GET_CODE (SET_DEST (x)) == SUBREG + /* We need SUBREGs to compute nonzero_bits properly. */ + && nonzero_sign_valid + && !read_modify_subreg_p (SET_DEST (x))) + { + x = gen_rtx_SET (SUBREG_REG (SET_DEST (x)), + gen_lowpart + (GET_MODE (SUBREG_REG (SET_DEST (x))), + SET_SRC (x))); + continue; + } + else + break; + + while (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) + inner = SUBREG_REG (inner); + + /* Don't attempt bitwise arithmetic on non scalar integer modes. */ + if (!is_a <scalar_int_mode> (GET_MODE (inner), &compute_mode)) + { + /* Don't do anything for vector or complex integral types. */ + if (! FLOAT_MODE_P (GET_MODE (inner))) + break; + + /* Try to find an integral mode to pun with. */ + if (!int_mode_for_size (GET_MODE_BITSIZE (GET_MODE (inner)), 0) + .exists (&compute_mode)) + break; + + inner = gen_lowpart (compute_mode, inner); + } + + /* Compute a mask of LEN bits, if we can do this on the host machine. */ + if (len >= HOST_BITS_PER_WIDE_INT) + break; + + /* Don't try to compute in too wide unsupported modes. */ + if (!targetm.scalar_mode_supported_p (compute_mode)) + break; + + /* Now compute the equivalent expression. Make a copy of INNER + for the SET_DEST in case it is a MEM into which we will substitute; + we don't want shared RTL in that case. */ + mask = gen_int_mode ((HOST_WIDE_INT_1U << len) - 1, + compute_mode); + cleared = simplify_gen_binary (AND, compute_mode, + simplify_gen_unary (NOT, compute_mode, + simplify_gen_binary (ASHIFT, + compute_mode, + mask, pos), + compute_mode), + inner); + masked = simplify_gen_binary (ASHIFT, compute_mode, + simplify_gen_binary ( + AND, compute_mode, + gen_lowpart (compute_mode, SET_SRC (x)), + mask), + pos); + + x = gen_rtx_SET (copy_rtx (inner), + simplify_gen_binary (IOR, compute_mode, + cleared, masked)); + } + + return x; +} + +/* Return an RTX for a reference to LEN bits of INNER. If POS_RTX is nonzero, + it is an RTX that represents the (variable) starting position; otherwise, + POS is the (constant) starting bit position. Both are counted from the LSB. + + UNSIGNEDP is nonzero for an unsigned reference and zero for a signed one. + + IN_DEST is nonzero if this is a reference in the destination of a SET. + This is used when a ZERO_ or SIGN_EXTRACT isn't needed. If nonzero, + a STRICT_LOW_PART will be used, if zero, ZERO_EXTEND or SIGN_EXTEND will + be used. + + IN_COMPARE is nonzero if we are in a COMPARE. This means that a + ZERO_EXTRACT should be built even for bits starting at bit 0. + + MODE is the desired mode of the result (if IN_DEST == 0). + + The result is an RTX for the extraction or NULL_RTX if the target + can't handle it. */ + +static rtx +make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos, + rtx pos_rtx, unsigned HOST_WIDE_INT len, int unsignedp, + int in_dest, int in_compare) +{ + /* This mode describes the size of the storage area + to fetch the overall value from. Within that, we + ignore the POS lowest bits, etc. */ + machine_mode is_mode = GET_MODE (inner); + machine_mode inner_mode; + scalar_int_mode wanted_inner_mode; + scalar_int_mode wanted_inner_reg_mode = word_mode; + scalar_int_mode pos_mode = word_mode; + machine_mode extraction_mode = word_mode; + rtx new_rtx = 0; + rtx orig_pos_rtx = pos_rtx; + HOST_WIDE_INT orig_pos; + + if (pos_rtx && CONST_INT_P (pos_rtx)) + pos = INTVAL (pos_rtx), pos_rtx = 0; + + if (GET_CODE (inner) == SUBREG + && subreg_lowpart_p (inner) + && (paradoxical_subreg_p (inner) + /* If trying or potentionally trying to extract + bits outside of is_mode, don't look through + non-paradoxical SUBREGs. See PR82192. */ + || (pos_rtx == NULL_RTX + && known_le (pos + len, GET_MODE_PRECISION (is_mode))))) + { + /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...), + consider just the QI as the memory to extract from. + The subreg adds or removes high bits; its mode is + irrelevant to the meaning of this extraction, + since POS and LEN count from the lsb. */ + if (MEM_P (SUBREG_REG (inner))) + is_mode = GET_MODE (SUBREG_REG (inner)); + inner = SUBREG_REG (inner); + } + else if (GET_CODE (inner) == ASHIFT + && CONST_INT_P (XEXP (inner, 1)) + && pos_rtx == 0 && pos == 0 + && len > UINTVAL (XEXP (inner, 1))) + { + /* We're extracting the least significant bits of an rtx + (ashift X (const_int C)), where LEN > C. Extract the + least significant (LEN - C) bits of X, giving an rtx + whose mode is MODE, then shift it left C times. */ + new_rtx = make_extraction (mode, XEXP (inner, 0), + 0, 0, len - INTVAL (XEXP (inner, 1)), + unsignedp, in_dest, in_compare); + if (new_rtx != 0) + return gen_rtx_ASHIFT (mode, new_rtx, XEXP (inner, 1)); + } + else if (GET_CODE (inner) == MULT + && CONST_INT_P (XEXP (inner, 1)) + && pos_rtx == 0 && pos == 0) + { + /* We're extracting the least significant bits of an rtx + (mult X (const_int 2^C)), where LEN > C. Extract the + least significant (LEN - C) bits of X, giving an rtx + whose mode is MODE, then multiply it by 2^C. */ + const HOST_WIDE_INT shift_amt = exact_log2 (INTVAL (XEXP (inner, 1))); + if (IN_RANGE (shift_amt, 1, len - 1)) + { + new_rtx = make_extraction (mode, XEXP (inner, 0), + 0, 0, len - shift_amt, + unsignedp, in_dest, in_compare); + if (new_rtx) + return gen_rtx_MULT (mode, new_rtx, XEXP (inner, 1)); + } + } + else if (GET_CODE (inner) == TRUNCATE + /* If trying or potentionally trying to extract + bits outside of is_mode, don't look through + TRUNCATE. See PR82192. */ + && pos_rtx == NULL_RTX + && known_le (pos + len, GET_MODE_PRECISION (is_mode))) + inner = XEXP (inner, 0); + + inner_mode = GET_MODE (inner); + + /* See if this can be done without an extraction. We never can if the + width of the field is not the same as that of some integer mode. For + registers, we can only avoid the extraction if the position is at the + low-order bit and this is either not in the destination or we have the + appropriate STRICT_LOW_PART operation available. + + For MEM, we can avoid an extract if the field starts on an appropriate + boundary and we can change the mode of the memory reference. */ + + scalar_int_mode tmode; + if (int_mode_for_size (len, 1).exists (&tmode) + && ((pos_rtx == 0 && (pos % BITS_PER_WORD) == 0 + && !MEM_P (inner) + && (pos == 0 || REG_P (inner)) + && (inner_mode == tmode + || !REG_P (inner) + || TRULY_NOOP_TRUNCATION_MODES_P (tmode, inner_mode) + || reg_truncated_to_mode (tmode, inner)) + && (! in_dest + || (REG_P (inner) + && have_insn_for (STRICT_LOW_PART, tmode)))) + || (MEM_P (inner) && pos_rtx == 0 + && (pos + % (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode) + : BITS_PER_UNIT)) == 0 + /* We can't do this if we are widening INNER_MODE (it + may not be aligned, for one thing). */ + && !paradoxical_subreg_p (tmode, inner_mode) + && known_le (pos + len, GET_MODE_PRECISION (is_mode)) + && (inner_mode == tmode + || (! mode_dependent_address_p (XEXP (inner, 0), + MEM_ADDR_SPACE (inner)) + && ! MEM_VOLATILE_P (inner)))))) + { + /* If INNER is a MEM, make a new MEM that encompasses just the desired + field. If the original and current mode are the same, we need not + adjust the offset. Otherwise, we do if bytes big endian. + + If INNER is not a MEM, get a piece consisting of just the field + of interest (in this case POS % BITS_PER_WORD must be 0). */ + + if (MEM_P (inner)) + { + poly_int64 offset; + + /* POS counts from lsb, but make OFFSET count in memory order. */ + if (BYTES_BIG_ENDIAN) + offset = bits_to_bytes_round_down (GET_MODE_PRECISION (is_mode) + - len - pos); + else + offset = pos / BITS_PER_UNIT; + + new_rtx = adjust_address_nv (inner, tmode, offset); + } + else if (REG_P (inner)) + { + if (tmode != inner_mode) + { + /* We can't call gen_lowpart in a DEST since we + always want a SUBREG (see below) and it would sometimes + return a new hard register. */ + if (pos || in_dest) + { + poly_uint64 offset + = subreg_offset_from_lsb (tmode, inner_mode, pos); + + /* Avoid creating invalid subregs, for example when + simplifying (x>>32)&255. */ + if (!validate_subreg (tmode, inner_mode, inner, offset)) + return NULL_RTX; + + new_rtx = gen_rtx_SUBREG (tmode, inner, offset); + } + else + new_rtx = gen_lowpart (tmode, inner); + } + else + new_rtx = inner; + } + else + new_rtx = force_to_mode (inner, tmode, + len >= HOST_BITS_PER_WIDE_INT + ? HOST_WIDE_INT_M1U + : (HOST_WIDE_INT_1U << len) - 1, 0); + + /* If this extraction is going into the destination of a SET, + make a STRICT_LOW_PART unless we made a MEM. */ + + if (in_dest) + return (MEM_P (new_rtx) ? new_rtx + : (GET_CODE (new_rtx) != SUBREG + ? gen_rtx_CLOBBER (tmode, const0_rtx) + : gen_rtx_STRICT_LOW_PART (VOIDmode, new_rtx))); + + if (mode == tmode) + return new_rtx; + + if (CONST_SCALAR_INT_P (new_rtx)) + return simplify_unary_operation (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, + mode, new_rtx, tmode); + + /* If we know that no extraneous bits are set, and that the high + bit is not set, convert the extraction to the cheaper of + sign and zero extension, that are equivalent in these cases. */ + if (flag_expensive_optimizations + && (HWI_COMPUTABLE_MODE_P (tmode) + && ((nonzero_bits (new_rtx, tmode) + & ~(((unsigned HOST_WIDE_INT)GET_MODE_MASK (tmode)) >> 1)) + == 0))) + { + rtx temp = gen_rtx_ZERO_EXTEND (mode, new_rtx); + rtx temp1 = gen_rtx_SIGN_EXTEND (mode, new_rtx); + + /* Prefer ZERO_EXTENSION, since it gives more information to + backends. */ + if (set_src_cost (temp, mode, optimize_this_for_speed_p) + <= set_src_cost (temp1, mode, optimize_this_for_speed_p)) + return temp; + return temp1; + } + + /* Otherwise, sign- or zero-extend unless we already are in the + proper mode. */ + + return (gen_rtx_fmt_e (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, + mode, new_rtx)); + } + + /* Unless this is a COMPARE or we have a funny memory reference, + don't do anything with zero-extending field extracts starting at + the low-order bit since they are simple AND operations. */ + if (pos_rtx == 0 && pos == 0 && ! in_dest + && ! in_compare && unsignedp) + return 0; + + /* Unless INNER is not MEM, reject this if we would be spanning bytes or + if the position is not a constant and the length is not 1. In all + other cases, we would only be going outside our object in cases when + an original shift would have been undefined. */ + if (MEM_P (inner) + && ((pos_rtx == 0 && maybe_gt (pos + len, GET_MODE_PRECISION (is_mode))) + || (pos_rtx != 0 && len != 1))) + return 0; + + enum extraction_pattern pattern = (in_dest ? EP_insv + : unsignedp ? EP_extzv : EP_extv); + + /* If INNER is not from memory, we want it to have the mode of a register + extraction pattern's structure operand, or word_mode if there is no + such pattern. The same applies to extraction_mode and pos_mode + and their respective operands. + + For memory, assume that the desired extraction_mode and pos_mode + are the same as for a register operation, since at present we don't + have named patterns for aligned memory structures. */ + class extraction_insn insn; + unsigned int inner_size; + if (GET_MODE_BITSIZE (inner_mode).is_constant (&inner_size) + && get_best_reg_extraction_insn (&insn, pattern, inner_size, mode)) + { + wanted_inner_reg_mode = insn.struct_mode.require (); + pos_mode = insn.pos_mode; + extraction_mode = insn.field_mode; + } + + /* Never narrow an object, since that might not be safe. */ + + if (mode != VOIDmode + && partial_subreg_p (extraction_mode, mode)) + extraction_mode = mode; + + /* Punt if len is too large for extraction_mode. */ + if (maybe_gt (len, GET_MODE_PRECISION (extraction_mode))) + return NULL_RTX; + + if (!MEM_P (inner)) + wanted_inner_mode = wanted_inner_reg_mode; + else + { + /* Be careful not to go beyond the extracted object and maintain the + natural alignment of the memory. */ + wanted_inner_mode = smallest_int_mode_for_size (len); + while (pos % GET_MODE_BITSIZE (wanted_inner_mode) + len + > GET_MODE_BITSIZE (wanted_inner_mode)) + wanted_inner_mode = GET_MODE_WIDER_MODE (wanted_inner_mode).require (); + } + + orig_pos = pos; + + if (BITS_BIG_ENDIAN) + { + /* POS is passed as if BITS_BIG_ENDIAN == 0, so we need to convert it to + BITS_BIG_ENDIAN style. If position is constant, compute new + position. Otherwise, build subtraction. + Note that POS is relative to the mode of the original argument. + If it's a MEM we need to recompute POS relative to that. + However, if we're extracting from (or inserting into) a register, + we want to recompute POS relative to wanted_inner_mode. */ + int width; + if (!MEM_P (inner)) + width = GET_MODE_BITSIZE (wanted_inner_mode); + else if (!GET_MODE_BITSIZE (is_mode).is_constant (&width)) + return NULL_RTX; + + if (pos_rtx == 0) + pos = width - len - pos; + else + pos_rtx + = gen_rtx_MINUS (GET_MODE (pos_rtx), + gen_int_mode (width - len, GET_MODE (pos_rtx)), + pos_rtx); + /* POS may be less than 0 now, but we check for that below. + Note that it can only be less than 0 if !MEM_P (inner). */ + } + + /* If INNER has a wider mode, and this is a constant extraction, try to + make it smaller and adjust the byte to point to the byte containing + the value. */ + if (wanted_inner_mode != VOIDmode + && inner_mode != wanted_inner_mode + && ! pos_rtx + && partial_subreg_p (wanted_inner_mode, is_mode) + && MEM_P (inner) + && ! mode_dependent_address_p (XEXP (inner, 0), MEM_ADDR_SPACE (inner)) + && ! MEM_VOLATILE_P (inner)) + { + poly_int64 offset = 0; + + /* The computations below will be correct if the machine is big + endian in both bits and bytes or little endian in bits and bytes. + If it is mixed, we must adjust. */ + + /* If bytes are big endian and we had a paradoxical SUBREG, we must + adjust OFFSET to compensate. */ + if (BYTES_BIG_ENDIAN + && paradoxical_subreg_p (is_mode, inner_mode)) + offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode); + + /* We can now move to the desired byte. */ + offset += (pos / GET_MODE_BITSIZE (wanted_inner_mode)) + * GET_MODE_SIZE (wanted_inner_mode); + pos %= GET_MODE_BITSIZE (wanted_inner_mode); + + if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN + && is_mode != wanted_inner_mode) + offset = (GET_MODE_SIZE (is_mode) + - GET_MODE_SIZE (wanted_inner_mode) - offset); + + inner = adjust_address_nv (inner, wanted_inner_mode, offset); + } + + /* If INNER is not memory, get it into the proper mode. If we are changing + its mode, POS must be a constant and smaller than the size of the new + mode. */ + else if (!MEM_P (inner)) + { + /* On the LHS, don't create paradoxical subregs implicitely truncating + the register unless TARGET_TRULY_NOOP_TRUNCATION. */ + if (in_dest + && !TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (inner), + wanted_inner_mode)) + return NULL_RTX; + + if (GET_MODE (inner) != wanted_inner_mode + && (pos_rtx != 0 + || orig_pos + len > GET_MODE_BITSIZE (wanted_inner_mode))) + return NULL_RTX; + + if (orig_pos < 0) + return NULL_RTX; + + inner = force_to_mode (inner, wanted_inner_mode, + pos_rtx + || len + orig_pos >= HOST_BITS_PER_WIDE_INT + ? HOST_WIDE_INT_M1U + : (((HOST_WIDE_INT_1U << len) - 1) + << orig_pos), + 0); + } + + /* Adjust mode of POS_RTX, if needed. If we want a wider mode, we + have to zero extend. Otherwise, we can just use a SUBREG. + + We dealt with constant rtxes earlier, so pos_rtx cannot + have VOIDmode at this point. */ + if (pos_rtx != 0 + && (GET_MODE_SIZE (pos_mode) + > GET_MODE_SIZE (as_a <scalar_int_mode> (GET_MODE (pos_rtx))))) + { + rtx temp = simplify_gen_unary (ZERO_EXTEND, pos_mode, pos_rtx, + GET_MODE (pos_rtx)); + + /* If we know that no extraneous bits are set, and that the high + bit is not set, convert extraction to cheaper one - either + SIGN_EXTENSION or ZERO_EXTENSION, that are equivalent in these + cases. */ + if (flag_expensive_optimizations + && (HWI_COMPUTABLE_MODE_P (GET_MODE (pos_rtx)) + && ((nonzero_bits (pos_rtx, GET_MODE (pos_rtx)) + & ~(((unsigned HOST_WIDE_INT) + GET_MODE_MASK (GET_MODE (pos_rtx))) + >> 1)) + == 0))) + { + rtx temp1 = simplify_gen_unary (SIGN_EXTEND, pos_mode, pos_rtx, + GET_MODE (pos_rtx)); + + /* Prefer ZERO_EXTENSION, since it gives more information to + backends. */ + if (set_src_cost (temp1, pos_mode, optimize_this_for_speed_p) + < set_src_cost (temp, pos_mode, optimize_this_for_speed_p)) + temp = temp1; + } + pos_rtx = temp; + } + + /* Make POS_RTX unless we already have it and it is correct. If we don't + have a POS_RTX but we do have an ORIG_POS_RTX, the latter must + be a CONST_INT. */ + if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos) + pos_rtx = orig_pos_rtx; + + else if (pos_rtx == 0) + pos_rtx = GEN_INT (pos); + + /* Make the required operation. See if we can use existing rtx. */ + new_rtx = gen_rtx_fmt_eee (unsignedp ? ZERO_EXTRACT : SIGN_EXTRACT, + extraction_mode, inner, GEN_INT (len), pos_rtx); + if (! in_dest) + new_rtx = gen_lowpart (mode, new_rtx); + + return new_rtx; +} + +/* See if X (of mode MODE) contains an ASHIFT of COUNT or more bits that + can be commuted with any other operations in X. Return X without + that shift if so. */ + +static rtx +extract_left_shift (scalar_int_mode mode, rtx x, int count) +{ + enum rtx_code code = GET_CODE (x); + rtx tem; + + switch (code) + { + case ASHIFT: + /* This is the shift itself. If it is wide enough, we will return + either the value being shifted if the shift count is equal to + COUNT or a shift for the difference. */ + if (CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= count) + return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0), + INTVAL (XEXP (x, 1)) - count); + break; + + case NEG: case NOT: + if ((tem = extract_left_shift (mode, XEXP (x, 0), count)) != 0) + return simplify_gen_unary (code, mode, tem, mode); + + break; + + case PLUS: case IOR: case XOR: case AND: + /* If we can safely shift this constant and we find the inner shift, + make a new operation. */ + if (CONST_INT_P (XEXP (x, 1)) + && (UINTVAL (XEXP (x, 1)) + & (((HOST_WIDE_INT_1U << count)) - 1)) == 0 + && (tem = extract_left_shift (mode, XEXP (x, 0), count)) != 0) + { + HOST_WIDE_INT val = INTVAL (XEXP (x, 1)) >> count; + return simplify_gen_binary (code, mode, tem, + gen_int_mode (val, mode)); + } + break; + + default: + break; + } + + return 0; +} + +/* Subroutine of make_compound_operation. *X_PTR is the rtx at the current + level of the expression and MODE is its mode. IN_CODE is as for + make_compound_operation. *NEXT_CODE_PTR is the value of IN_CODE + that should be used when recursing on operands of *X_PTR. + + There are two possible actions: + + - Return null. This tells the caller to recurse on *X_PTR with IN_CODE + equal to *NEXT_CODE_PTR, after which *X_PTR holds the final value. + + - Return a new rtx, which the caller returns directly. */ + +static rtx +make_compound_operation_int (scalar_int_mode mode, rtx *x_ptr, + enum rtx_code in_code, + enum rtx_code *next_code_ptr) +{ + rtx x = *x_ptr; + enum rtx_code next_code = *next_code_ptr; + enum rtx_code code = GET_CODE (x); + int mode_width = GET_MODE_PRECISION (mode); + rtx rhs, lhs; + rtx new_rtx = 0; + int i; + rtx tem; + scalar_int_mode inner_mode; + bool equality_comparison = false; + + if (in_code == EQ) + { + equality_comparison = true; + in_code = COMPARE; + } + + /* Process depending on the code of this operation. If NEW is set + nonzero, it will be returned. */ + + switch (code) + { + case ASHIFT: + /* Convert shifts by constants into multiplications if inside + an address. */ + if (in_code == MEM && CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT + && INTVAL (XEXP (x, 1)) >= 0) + { + HOST_WIDE_INT count = INTVAL (XEXP (x, 1)); + HOST_WIDE_INT multval = HOST_WIDE_INT_1 << count; + + new_rtx = make_compound_operation (XEXP (x, 0), next_code); + if (GET_CODE (new_rtx) == NEG) + { + new_rtx = XEXP (new_rtx, 0); + multval = -multval; + } + multval = trunc_int_for_mode (multval, mode); + new_rtx = gen_rtx_MULT (mode, new_rtx, gen_int_mode (multval, mode)); + } + break; + + case PLUS: + lhs = XEXP (x, 0); + rhs = XEXP (x, 1); + lhs = make_compound_operation (lhs, next_code); + rhs = make_compound_operation (rhs, next_code); + if (GET_CODE (lhs) == MULT && GET_CODE (XEXP (lhs, 0)) == NEG) + { + tem = simplify_gen_binary (MULT, mode, XEXP (XEXP (lhs, 0), 0), + XEXP (lhs, 1)); + new_rtx = simplify_gen_binary (MINUS, mode, rhs, tem); + } + else if (GET_CODE (lhs) == MULT + && (CONST_INT_P (XEXP (lhs, 1)) && INTVAL (XEXP (lhs, 1)) < 0)) + { + tem = simplify_gen_binary (MULT, mode, XEXP (lhs, 0), + simplify_gen_unary (NEG, mode, + XEXP (lhs, 1), + mode)); + new_rtx = simplify_gen_binary (MINUS, mode, rhs, tem); + } + else + { + SUBST (XEXP (x, 0), lhs); + SUBST (XEXP (x, 1), rhs); + } + maybe_swap_commutative_operands (x); + return x; + + case MINUS: + lhs = XEXP (x, 0); + rhs = XEXP (x, 1); + lhs = make_compound_operation (lhs, next_code); + rhs = make_compound_operation (rhs, next_code); + if (GET_CODE (rhs) == MULT && GET_CODE (XEXP (rhs, 0)) == NEG) + { + tem = simplify_gen_binary (MULT, mode, XEXP (XEXP (rhs, 0), 0), + XEXP (rhs, 1)); + return simplify_gen_binary (PLUS, mode, tem, lhs); + } + else if (GET_CODE (rhs) == MULT + && (CONST_INT_P (XEXP (rhs, 1)) && INTVAL (XEXP (rhs, 1)) < 0)) + { + tem = simplify_gen_binary (MULT, mode, XEXP (rhs, 0), + simplify_gen_unary (NEG, mode, + XEXP (rhs, 1), + mode)); + return simplify_gen_binary (PLUS, mode, tem, lhs); + } + else + { + SUBST (XEXP (x, 0), lhs); + SUBST (XEXP (x, 1), rhs); + return x; + } + + case AND: + /* If the second operand is not a constant, we can't do anything + with it. */ + if (!CONST_INT_P (XEXP (x, 1))) + break; + + /* If the constant is a power of two minus one and the first operand + is a logical right shift, make an extraction. */ + if (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && (i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0) + { + new_rtx = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); + new_rtx = make_extraction (mode, new_rtx, 0, XEXP (XEXP (x, 0), 1), + i, 1, 0, in_code == COMPARE); + } + + /* Same as previous, but for (subreg (lshiftrt ...)) in first op. */ + else if (GET_CODE (XEXP (x, 0)) == SUBREG + && subreg_lowpart_p (XEXP (x, 0)) + && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (XEXP (x, 0))), + &inner_mode) + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT + && (i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0) + { + rtx inner_x0 = SUBREG_REG (XEXP (x, 0)); + new_rtx = make_compound_operation (XEXP (inner_x0, 0), next_code); + new_rtx = make_extraction (inner_mode, new_rtx, 0, + XEXP (inner_x0, 1), + i, 1, 0, in_code == COMPARE); + + /* If we narrowed the mode when dropping the subreg, then we lose. */ + if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)) + new_rtx = NULL; + + /* If that didn't give anything, see if the AND simplifies on + its own. */ + if (!new_rtx && i >= 0) + { + new_rtx = make_compound_operation (XEXP (x, 0), next_code); + new_rtx = make_extraction (mode, new_rtx, 0, NULL_RTX, i, 1, + 0, in_code == COMPARE); + } + } + /* Same as previous, but for (xor/ior (lshiftrt...) (lshiftrt...)). */ + else if ((GET_CODE (XEXP (x, 0)) == XOR + || GET_CODE (XEXP (x, 0)) == IOR) + && GET_CODE (XEXP (XEXP (x, 0), 0)) == LSHIFTRT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == LSHIFTRT + && (i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0) + { + /* Apply the distributive law, and then try to make extractions. */ + new_rtx = gen_rtx_fmt_ee (GET_CODE (XEXP (x, 0)), mode, + gen_rtx_AND (mode, XEXP (XEXP (x, 0), 0), + XEXP (x, 1)), + gen_rtx_AND (mode, XEXP (XEXP (x, 0), 1), + XEXP (x, 1))); + new_rtx = make_compound_operation (new_rtx, in_code); + } + + /* If we are have (and (rotate X C) M) and C is larger than the number + of bits in M, this is an extraction. */ + + else if (GET_CODE (XEXP (x, 0)) == ROTATE + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && (i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0 + && i <= INTVAL (XEXP (XEXP (x, 0), 1))) + { + new_rtx = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code); + new_rtx = make_extraction (mode, new_rtx, + (GET_MODE_PRECISION (mode) + - INTVAL (XEXP (XEXP (x, 0), 1))), + NULL_RTX, i, 1, 0, in_code == COMPARE); + } + + /* On machines without logical shifts, if the operand of the AND is + a logical shift and our mask turns off all the propagated sign + bits, we can replace the logical shift with an arithmetic shift. */ + else if (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && !have_insn_for (LSHIFTRT, mode) + && have_insn_for (ASHIFTRT, mode) + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT + && mode_width <= HOST_BITS_PER_WIDE_INT) + { + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + + mask >>= INTVAL (XEXP (XEXP (x, 0), 1)); + if ((INTVAL (XEXP (x, 1)) & ~mask) == 0) + SUBST (XEXP (x, 0), + gen_rtx_ASHIFTRT (mode, + make_compound_operation (XEXP (XEXP (x, + 0), + 0), + next_code), + XEXP (XEXP (x, 0), 1))); + } + + /* If the constant is one less than a power of two, this might be + representable by an extraction even if no shift is present. + If it doesn't end up being a ZERO_EXTEND, we will ignore it unless + we are in a COMPARE. */ + else if ((i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0) + new_rtx = make_extraction (mode, + make_compound_operation (XEXP (x, 0), + next_code), + 0, NULL_RTX, i, 1, 0, in_code == COMPARE); + + /* If we are in a comparison and this is an AND with a power of two, + convert this into the appropriate bit extract. */ + else if (in_code == COMPARE + && (i = exact_log2 (UINTVAL (XEXP (x, 1)))) >= 0 + && (equality_comparison || i < GET_MODE_PRECISION (mode) - 1)) + new_rtx = make_extraction (mode, + make_compound_operation (XEXP (x, 0), + next_code), + i, NULL_RTX, 1, 1, 0, 1); + + /* If the one operand is a paradoxical subreg of a register or memory and + the constant (limited to the smaller mode) has only zero bits where + the sub expression has known zero bits, this can be expressed as + a zero_extend. */ + else if (GET_CODE (XEXP (x, 0)) == SUBREG) + { + rtx sub; + + sub = XEXP (XEXP (x, 0), 0); + machine_mode sub_mode = GET_MODE (sub); + int sub_width; + if ((REG_P (sub) || MEM_P (sub)) + && GET_MODE_PRECISION (sub_mode).is_constant (&sub_width) + && sub_width < mode_width) + { + unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (sub_mode); + unsigned HOST_WIDE_INT mask; + + /* original AND constant with all the known zero bits set */ + mask = UINTVAL (XEXP (x, 1)) | (~nonzero_bits (sub, sub_mode)); + if ((mask & mode_mask) == mode_mask) + { + new_rtx = make_compound_operation (sub, next_code); + new_rtx = make_extraction (mode, new_rtx, 0, 0, sub_width, + 1, 0, in_code == COMPARE); + } + } + } + + break; + + case LSHIFTRT: + /* If the sign bit is known to be zero, replace this with an + arithmetic shift. */ + if (have_insn_for (ASHIFTRT, mode) + && ! have_insn_for (LSHIFTRT, mode) + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0) + { + new_rtx = gen_rtx_ASHIFTRT (mode, + make_compound_operation (XEXP (x, 0), + next_code), + XEXP (x, 1)); + break; + } + + /* fall through */ + + case ASHIFTRT: + lhs = XEXP (x, 0); + rhs = XEXP (x, 1); + + /* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1, + this is a SIGN_EXTRACT. */ + if (CONST_INT_P (rhs) + && GET_CODE (lhs) == ASHIFT + && CONST_INT_P (XEXP (lhs, 1)) + && INTVAL (rhs) >= INTVAL (XEXP (lhs, 1)) + && INTVAL (XEXP (lhs, 1)) >= 0 + && INTVAL (rhs) < mode_width) + { + new_rtx = make_compound_operation (XEXP (lhs, 0), next_code); + new_rtx = make_extraction (mode, new_rtx, + INTVAL (rhs) - INTVAL (XEXP (lhs, 1)), + NULL_RTX, mode_width - INTVAL (rhs), + code == LSHIFTRT, 0, in_code == COMPARE); + break; + } + + /* See if we have operations between an ASHIFTRT and an ASHIFT. + If so, try to merge the shifts into a SIGN_EXTEND. We could + also do this for some cases of SIGN_EXTRACT, but it doesn't + seem worth the effort; the case checked for occurs on Alpha. */ + + if (!OBJECT_P (lhs) + && ! (GET_CODE (lhs) == SUBREG + && (OBJECT_P (SUBREG_REG (lhs)))) + && CONST_INT_P (rhs) + && INTVAL (rhs) >= 0 + && INTVAL (rhs) < HOST_BITS_PER_WIDE_INT + && INTVAL (rhs) < mode_width + && (new_rtx = extract_left_shift (mode, lhs, INTVAL (rhs))) != 0) + new_rtx = make_extraction (mode, make_compound_operation (new_rtx, + next_code), + 0, NULL_RTX, mode_width - INTVAL (rhs), + code == LSHIFTRT, 0, in_code == COMPARE); + + break; + + case SUBREG: + /* Call ourselves recursively on the inner expression. If we are + narrowing the object and it has a different RTL code from + what it originally did, do this SUBREG as a force_to_mode. */ + { + rtx inner = SUBREG_REG (x), simplified; + enum rtx_code subreg_code = in_code; + + /* If the SUBREG is masking of a logical right shift, + make an extraction. */ + if (GET_CODE (inner) == LSHIFTRT + && is_a <scalar_int_mode> (GET_MODE (inner), &inner_mode) + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (inner_mode) + && CONST_INT_P (XEXP (inner, 1)) + && UINTVAL (XEXP (inner, 1)) < GET_MODE_PRECISION (inner_mode) + && subreg_lowpart_p (x)) + { + new_rtx = make_compound_operation (XEXP (inner, 0), next_code); + int width = GET_MODE_PRECISION (inner_mode) + - INTVAL (XEXP (inner, 1)); + if (width > mode_width) + width = mode_width; + new_rtx = make_extraction (mode, new_rtx, 0, XEXP (inner, 1), + width, 1, 0, in_code == COMPARE); + break; + } + + /* If in_code is COMPARE, it isn't always safe to pass it through + to the recursive make_compound_operation call. */ + if (subreg_code == COMPARE + && (!subreg_lowpart_p (x) + || GET_CODE (inner) == SUBREG + /* (subreg:SI (and:DI (reg:DI) (const_int 0x800000000)) 0) + is (const_int 0), rather than + (subreg:SI (lshiftrt:DI (reg:DI) (const_int 35)) 0). + Similarly (subreg:QI (and:SI (reg:SI) (const_int 0x80)) 0) + for non-equality comparisons against 0 is not equivalent + to (subreg:QI (lshiftrt:SI (reg:SI) (const_int 7)) 0). */ + || (GET_CODE (inner) == AND + && CONST_INT_P (XEXP (inner, 1)) + && partial_subreg_p (x) + && exact_log2 (UINTVAL (XEXP (inner, 1))) + >= GET_MODE_BITSIZE (mode) - 1))) + subreg_code = SET; + + tem = make_compound_operation (inner, subreg_code); + + simplified + = simplify_subreg (mode, tem, GET_MODE (inner), SUBREG_BYTE (x)); + if (simplified) + tem = simplified; + + if (GET_CODE (tem) != GET_CODE (inner) + && partial_subreg_p (x) + && subreg_lowpart_p (x)) + { + rtx newer + = force_to_mode (tem, mode, HOST_WIDE_INT_M1U, 0); + + /* If we have something other than a SUBREG, we might have + done an expansion, so rerun ourselves. */ + if (GET_CODE (newer) != SUBREG) + newer = make_compound_operation (newer, in_code); + + /* force_to_mode can expand compounds. If it just re-expanded + the compound, use gen_lowpart to convert to the desired + mode. */ + if (rtx_equal_p (newer, x) + /* Likewise if it re-expanded the compound only partially. + This happens for SUBREG of ZERO_EXTRACT if they extract + the same number of bits. */ + || (GET_CODE (newer) == SUBREG + && (GET_CODE (SUBREG_REG (newer)) == LSHIFTRT + || GET_CODE (SUBREG_REG (newer)) == ASHIFTRT) + && GET_CODE (inner) == AND + && rtx_equal_p (SUBREG_REG (newer), XEXP (inner, 0)))) + return gen_lowpart (GET_MODE (x), tem); + + return newer; + } + + if (simplified) + return tem; + } + break; + + default: + break; + } + + if (new_rtx) + *x_ptr = gen_lowpart (mode, new_rtx); + *next_code_ptr = next_code; + return NULL_RTX; +} + +/* Look at the expression rooted at X. Look for expressions + equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND. + Form these expressions. + + Return the new rtx, usually just X. + + Also, for machines like the VAX that don't have logical shift insns, + try to convert logical to arithmetic shift operations in cases where + they are equivalent. This undoes the canonicalizations to logical + shifts done elsewhere. + + We try, as much as possible, to re-use rtl expressions to save memory. + + IN_CODE says what kind of expression we are processing. Normally, it is + SET. In a memory address it is MEM. When processing the arguments of + a comparison or a COMPARE against zero, it is COMPARE, or EQ if more + precisely it is an equality comparison against zero. */ + +rtx +make_compound_operation (rtx x, enum rtx_code in_code) +{ + enum rtx_code code = GET_CODE (x); + const char *fmt; + int i, j; + enum rtx_code next_code; + rtx new_rtx, tem; + + /* Select the code to be used in recursive calls. Once we are inside an + address, we stay there. If we have a comparison, set to COMPARE, + but once inside, go back to our default of SET. */ + + next_code = (code == MEM ? MEM + : ((code == COMPARE || COMPARISON_P (x)) + && XEXP (x, 1) == const0_rtx) ? COMPARE + : in_code == COMPARE || in_code == EQ ? SET : in_code); + + scalar_int_mode mode; + if (is_a <scalar_int_mode> (GET_MODE (x), &mode)) + { + rtx new_rtx = make_compound_operation_int (mode, &x, in_code, + &next_code); + if (new_rtx) + return new_rtx; + code = GET_CODE (x); + } + + /* Now recursively process each operand of this operation. We need to + handle ZERO_EXTEND specially so that we don't lose track of the + inner mode. */ + if (code == ZERO_EXTEND) + { + new_rtx = make_compound_operation (XEXP (x, 0), next_code); + tem = simplify_const_unary_operation (ZERO_EXTEND, GET_MODE (x), + new_rtx, GET_MODE (XEXP (x, 0))); + if (tem) + return tem; + SUBST (XEXP (x, 0), new_rtx); + return x; + } + + fmt = GET_RTX_FORMAT (code); + for (i = 0; i < GET_RTX_LENGTH (code); i++) + if (fmt[i] == 'e') + { + new_rtx = make_compound_operation (XEXP (x, i), next_code); + SUBST (XEXP (x, i), new_rtx); + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + { + new_rtx = make_compound_operation (XVECEXP (x, i, j), next_code); + SUBST (XVECEXP (x, i, j), new_rtx); + } + + maybe_swap_commutative_operands (x); + return x; +} + +/* Given M see if it is a value that would select a field of bits + within an item, but not the entire word. Return -1 if not. + Otherwise, return the starting position of the field, where 0 is the + low-order bit. + + *PLEN is set to the length of the field. */ + +static int +get_pos_from_mask (unsigned HOST_WIDE_INT m, unsigned HOST_WIDE_INT *plen) +{ + /* Get the bit number of the first 1 bit from the right, -1 if none. */ + int pos = m ? ctz_hwi (m) : -1; + int len = 0; + + if (pos >= 0) + /* Now shift off the low-order zero bits and see if we have a + power of two minus 1. */ + len = exact_log2 ((m >> pos) + 1); + + if (len <= 0) + pos = -1; + + *plen = len; + return pos; +} + +/* If X refers to a register that equals REG in value, replace these + references with REG. */ +static rtx +canon_reg_for_combine (rtx x, rtx reg) +{ + rtx op0, op1, op2; + const char *fmt; + int i; + bool copied; + + enum rtx_code code = GET_CODE (x); + switch (GET_RTX_CLASS (code)) + { + case RTX_UNARY: + op0 = canon_reg_for_combine (XEXP (x, 0), reg); + if (op0 != XEXP (x, 0)) + return simplify_gen_unary (GET_CODE (x), GET_MODE (x), op0, + GET_MODE (reg)); + break; + + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: + op0 = canon_reg_for_combine (XEXP (x, 0), reg); + op1 = canon_reg_for_combine (XEXP (x, 1), reg); + if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) + return simplify_gen_binary (GET_CODE (x), GET_MODE (x), op0, op1); + break; + + case RTX_COMPARE: + case RTX_COMM_COMPARE: + op0 = canon_reg_for_combine (XEXP (x, 0), reg); + op1 = canon_reg_for_combine (XEXP (x, 1), reg); + if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) + return simplify_gen_relational (GET_CODE (x), GET_MODE (x), + GET_MODE (op0), op0, op1); + break; + + case RTX_TERNARY: + case RTX_BITFIELD_OPS: + op0 = canon_reg_for_combine (XEXP (x, 0), reg); + op1 = canon_reg_for_combine (XEXP (x, 1), reg); + op2 = canon_reg_for_combine (XEXP (x, 2), reg); + if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1) || op2 != XEXP (x, 2)) + return simplify_gen_ternary (GET_CODE (x), GET_MODE (x), + GET_MODE (op0), op0, op1, op2); + /* FALLTHRU */ + + case RTX_OBJ: + if (REG_P (x)) + { + if (rtx_equal_p (get_last_value (reg), x) + || rtx_equal_p (reg, get_last_value (x))) + return reg; + else + break; + } + + /* fall through */ + + default: + fmt = GET_RTX_FORMAT (code); + copied = false; + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + rtx op = canon_reg_for_combine (XEXP (x, i), reg); + if (op != XEXP (x, i)) + { + if (!copied) + { + copied = true; + x = copy_rtx (x); + } + XEXP (x, i) = op; + } + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + { + rtx op = canon_reg_for_combine (XVECEXP (x, i, j), reg); + if (op != XVECEXP (x, i, j)) + { + if (!copied) + { + copied = true; + x = copy_rtx (x); + } + XVECEXP (x, i, j) = op; + } + } + } + + break; + } + + return x; +} + +/* Return X converted to MODE. If the value is already truncated to + MODE we can just return a subreg even though in the general case we + would need an explicit truncation. */ + +static rtx +gen_lowpart_or_truncate (machine_mode mode, rtx x) +{ + if (!CONST_INT_P (x) + && partial_subreg_p (mode, GET_MODE (x)) + && !TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (x)) + && !(REG_P (x) && reg_truncated_to_mode (mode, x))) + { + /* Bit-cast X into an integer mode. */ + if (!SCALAR_INT_MODE_P (GET_MODE (x))) + x = gen_lowpart (int_mode_for_mode (GET_MODE (x)).require (), x); + x = simplify_gen_unary (TRUNCATE, int_mode_for_mode (mode).require (), + x, GET_MODE (x)); + } + + return gen_lowpart (mode, x); +} + +/* See if X can be simplified knowing that we will only refer to it in + MODE and will only refer to those bits that are nonzero in MASK. + If other bits are being computed or if masking operations are done + that select a superset of the bits in MASK, they can sometimes be + ignored. + + Return a possibly simplified expression, but always convert X to + MODE. If X is a CONST_INT, AND the CONST_INT with MASK. + + If JUST_SELECT is nonzero, don't optimize by noticing that bits in MASK + are all off in X. This is used when X will be complemented, by either + NOT, NEG, or XOR. */ + +static rtx +force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask, + int just_select) +{ + enum rtx_code code = GET_CODE (x); + int next_select = just_select || code == XOR || code == NOT || code == NEG; + machine_mode op_mode; + unsigned HOST_WIDE_INT nonzero; + + /* If this is a CALL or ASM_OPERANDS, don't do anything. Some of the + code below will do the wrong thing since the mode of such an + expression is VOIDmode. + + Also do nothing if X is a CLOBBER; this can happen if X was + the return value from a call to gen_lowpart. */ + if (code == CALL || code == ASM_OPERANDS || code == CLOBBER) + return x; + + /* We want to perform the operation in its present mode unless we know + that the operation is valid in MODE, in which case we do the operation + in MODE. */ + op_mode = ((GET_MODE_CLASS (mode) == GET_MODE_CLASS (GET_MODE (x)) + && have_insn_for (code, mode)) + ? mode : GET_MODE (x)); + + /* It is not valid to do a right-shift in a narrower mode + than the one it came in with. */ + if ((code == LSHIFTRT || code == ASHIFTRT) + && partial_subreg_p (mode, GET_MODE (x))) + op_mode = GET_MODE (x); + + /* Truncate MASK to fit OP_MODE. */ + if (op_mode) + mask &= GET_MODE_MASK (op_mode); + + /* Determine what bits of X are guaranteed to be (non)zero. */ + nonzero = nonzero_bits (x, mode); + + /* If none of the bits in X are needed, return a zero. */ + if (!just_select && (nonzero & mask) == 0 && !side_effects_p (x)) + x = const0_rtx; + + /* If X is a CONST_INT, return a new one. Do this here since the + test below will fail. */ + if (CONST_INT_P (x)) + { + if (SCALAR_INT_MODE_P (mode)) + return gen_int_mode (INTVAL (x) & mask, mode); + else + { + x = GEN_INT (INTVAL (x) & mask); + return gen_lowpart_common (mode, x); + } + } + + /* If X is narrower than MODE and we want all the bits in X's mode, just + get X in the proper mode. */ + if (paradoxical_subreg_p (mode, GET_MODE (x)) + && (GET_MODE_MASK (GET_MODE (x)) & ~mask) == 0) + return gen_lowpart (mode, x); + + /* We can ignore the effect of a SUBREG if it narrows the mode or + if the constant masks to zero all the bits the mode doesn't have. */ + if (GET_CODE (x) == SUBREG + && subreg_lowpart_p (x) + && (partial_subreg_p (x) + || (mask + & GET_MODE_MASK (GET_MODE (x)) + & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))) == 0)) + return force_to_mode (SUBREG_REG (x), mode, mask, next_select); + + scalar_int_mode int_mode, xmode; + if (is_a <scalar_int_mode> (mode, &int_mode) + && is_a <scalar_int_mode> (GET_MODE (x), &xmode)) + /* OP_MODE is either MODE or XMODE, so it must be a scalar + integer too. */ + return force_int_to_mode (x, int_mode, xmode, + as_a <scalar_int_mode> (op_mode), + mask, just_select); + + return gen_lowpart_or_truncate (mode, x); +} + +/* Subroutine of force_to_mode that handles cases in which both X and + the result are scalar integers. MODE is the mode of the result, + XMODE is the mode of X, and OP_MODE says which of MODE or XMODE + is preferred for simplified versions of X. The other arguments + are as for force_to_mode. */ + +static rtx +force_int_to_mode (rtx x, scalar_int_mode mode, scalar_int_mode xmode, + scalar_int_mode op_mode, unsigned HOST_WIDE_INT mask, + int just_select) +{ + enum rtx_code code = GET_CODE (x); + int next_select = just_select || code == XOR || code == NOT || code == NEG; + unsigned HOST_WIDE_INT fuller_mask; + rtx op0, op1, temp; + poly_int64 const_op0; + + /* When we have an arithmetic operation, or a shift whose count we + do not know, we need to assume that all bits up to the highest-order + bit in MASK will be needed. This is how we form such a mask. */ + if (mask & (HOST_WIDE_INT_1U << (HOST_BITS_PER_WIDE_INT - 1))) + fuller_mask = HOST_WIDE_INT_M1U; + else + fuller_mask = ((HOST_WIDE_INT_1U << (floor_log2 (mask) + 1)) + - 1); + + switch (code) + { + case CLOBBER: + /* If X is a (clobber (const_int)), return it since we know we are + generating something that won't match. */ + return x; + + case SIGN_EXTEND: + case ZERO_EXTEND: + case ZERO_EXTRACT: + case SIGN_EXTRACT: + x = expand_compound_operation (x); + if (GET_CODE (x) != code) + return force_to_mode (x, mode, mask, next_select); + break; + + case TRUNCATE: + /* Similarly for a truncate. */ + return force_to_mode (XEXP (x, 0), mode, mask, next_select); + + case AND: + /* If this is an AND with a constant, convert it into an AND + whose constant is the AND of that constant with MASK. If it + remains an AND of MASK, delete it since it is redundant. */ + + if (CONST_INT_P (XEXP (x, 1))) + { + x = simplify_and_const_int (x, op_mode, XEXP (x, 0), + mask & INTVAL (XEXP (x, 1))); + xmode = op_mode; + + /* If X is still an AND, see if it is an AND with a mask that + is just some low-order bits. If so, and it is MASK, we don't + need it. */ + + if (GET_CODE (x) == AND && CONST_INT_P (XEXP (x, 1)) + && (INTVAL (XEXP (x, 1)) & GET_MODE_MASK (xmode)) == mask) + x = XEXP (x, 0); + + /* If it remains an AND, try making another AND with the bits + in the mode mask that aren't in MASK turned on. If the + constant in the AND is wide enough, this might make a + cheaper constant. */ + + if (GET_CODE (x) == AND && CONST_INT_P (XEXP (x, 1)) + && GET_MODE_MASK (xmode) != mask + && HWI_COMPUTABLE_MODE_P (xmode)) + { + unsigned HOST_WIDE_INT cval + = UINTVAL (XEXP (x, 1)) | (GET_MODE_MASK (xmode) & ~mask); + rtx y; + + y = simplify_gen_binary (AND, xmode, XEXP (x, 0), + gen_int_mode (cval, xmode)); + if (set_src_cost (y, xmode, optimize_this_for_speed_p) + < set_src_cost (x, xmode, optimize_this_for_speed_p)) + x = y; + } + + break; + } + + goto binop; + + case PLUS: + /* In (and (plus FOO C1) M), if M is a mask that just turns off + low-order bits (as in an alignment operation) and FOO is already + aligned to that boundary, mask C1 to that boundary as well. + This may eliminate that PLUS and, later, the AND. */ + + { + unsigned int width = GET_MODE_PRECISION (mode); + unsigned HOST_WIDE_INT smask = mask; + + /* If MODE is narrower than HOST_WIDE_INT and mask is a negative + number, sign extend it. */ + + if (width < HOST_BITS_PER_WIDE_INT + && (smask & (HOST_WIDE_INT_1U << (width - 1))) != 0) + smask |= HOST_WIDE_INT_M1U << width; + + if (CONST_INT_P (XEXP (x, 1)) + && pow2p_hwi (- smask) + && (nonzero_bits (XEXP (x, 0), mode) & ~smask) == 0 + && (INTVAL (XEXP (x, 1)) & ~smask) != 0) + return force_to_mode (plus_constant (xmode, XEXP (x, 0), + (INTVAL (XEXP (x, 1)) & smask)), + mode, smask, next_select); + } + + /* fall through */ + + case MULT: + /* Substituting into the operands of a widening MULT is not likely to + create RTL matching a machine insn. */ + if (code == MULT + && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND + || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND) + && (GET_CODE (XEXP (x, 1)) == ZERO_EXTEND + || GET_CODE (XEXP (x, 1)) == SIGN_EXTEND) + && REG_P (XEXP (XEXP (x, 0), 0)) + && REG_P (XEXP (XEXP (x, 1), 0))) + return gen_lowpart_or_truncate (mode, x); + + /* For PLUS, MINUS and MULT, we need any bits less significant than the + most significant bit in MASK since carries from those bits will + affect the bits we are interested in. */ + mask = fuller_mask; + goto binop; + + case MINUS: + /* If X is (minus C Y) where C's least set bit is larger than any bit + in the mask, then we may replace with (neg Y). */ + if (poly_int_rtx_p (XEXP (x, 0), &const_op0) + && known_alignment (poly_uint64 (const_op0)) > mask) + { + x = simplify_gen_unary (NEG, xmode, XEXP (x, 1), xmode); + return force_to_mode (x, mode, mask, next_select); + } + + /* Similarly, if C contains every bit in the fuller_mask, then we may + replace with (not Y). */ + if (CONST_INT_P (XEXP (x, 0)) + && ((UINTVAL (XEXP (x, 0)) | fuller_mask) == UINTVAL (XEXP (x, 0)))) + { + x = simplify_gen_unary (NOT, xmode, XEXP (x, 1), xmode); + return force_to_mode (x, mode, mask, next_select); + } + + mask = fuller_mask; + goto binop; + + case IOR: + case XOR: + /* If X is (ior (lshiftrt FOO C1) C2), try to commute the IOR and + LSHIFTRT so we end up with an (and (lshiftrt (ior ...) ...) ...) + operation which may be a bitfield extraction. Ensure that the + constant we form is not wider than the mode of X. */ + + if (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT + && CONST_INT_P (XEXP (x, 1)) + && ((INTVAL (XEXP (XEXP (x, 0), 1)) + + floor_log2 (INTVAL (XEXP (x, 1)))) + < GET_MODE_PRECISION (xmode)) + && (UINTVAL (XEXP (x, 1)) + & ~nonzero_bits (XEXP (x, 0), xmode)) == 0) + { + temp = gen_int_mode ((INTVAL (XEXP (x, 1)) & mask) + << INTVAL (XEXP (XEXP (x, 0), 1)), + xmode); + temp = simplify_gen_binary (GET_CODE (x), xmode, + XEXP (XEXP (x, 0), 0), temp); + x = simplify_gen_binary (LSHIFTRT, xmode, temp, + XEXP (XEXP (x, 0), 1)); + return force_to_mode (x, mode, mask, next_select); + } + + binop: + /* For most binary operations, just propagate into the operation and + change the mode if we have an operation of that mode. */ + + op0 = force_to_mode (XEXP (x, 0), mode, mask, next_select); + op1 = force_to_mode (XEXP (x, 1), mode, mask, next_select); + + /* If we ended up truncating both operands, truncate the result of the + operation instead. */ + if (GET_CODE (op0) == TRUNCATE + && GET_CODE (op1) == TRUNCATE) + { + op0 = XEXP (op0, 0); + op1 = XEXP (op1, 0); + } + + op0 = gen_lowpart_or_truncate (op_mode, op0); + op1 = gen_lowpart_or_truncate (op_mode, op1); + + if (op_mode != xmode || op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) + { + x = simplify_gen_binary (code, op_mode, op0, op1); + xmode = op_mode; + } + break; + + case ASHIFT: + /* For left shifts, do the same, but just for the first operand. + However, we cannot do anything with shifts where we cannot + guarantee that the counts are smaller than the size of the mode + because such a count will have a different meaning in a + wider mode. */ + + if (! (CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) < GET_MODE_PRECISION (mode)) + && ! (GET_MODE (XEXP (x, 1)) != VOIDmode + && (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1))) + < (unsigned HOST_WIDE_INT) GET_MODE_PRECISION (mode)))) + break; + + /* If the shift count is a constant and we can do arithmetic in + the mode of the shift, refine which bits we need. Otherwise, use the + conservative form of the mask. */ + if (CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) < GET_MODE_PRECISION (op_mode) + && HWI_COMPUTABLE_MODE_P (op_mode)) + mask >>= INTVAL (XEXP (x, 1)); + else + mask = fuller_mask; + + op0 = gen_lowpart_or_truncate (op_mode, + force_to_mode (XEXP (x, 0), mode, + mask, next_select)); + + if (op_mode != xmode || op0 != XEXP (x, 0)) + { + x = simplify_gen_binary (code, op_mode, op0, XEXP (x, 1)); + xmode = op_mode; + } + break; + + case LSHIFTRT: + /* Here we can only do something if the shift count is a constant, + this shift constant is valid for the host, and we can do arithmetic + in OP_MODE. */ + + if (CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT + && HWI_COMPUTABLE_MODE_P (op_mode)) + { + rtx inner = XEXP (x, 0); + unsigned HOST_WIDE_INT inner_mask; + + /* Select the mask of the bits we need for the shift operand. */ + inner_mask = mask << INTVAL (XEXP (x, 1)); + + /* We can only change the mode of the shift if we can do arithmetic + in the mode of the shift and INNER_MASK is no wider than the + width of X's mode. */ + if ((inner_mask & ~GET_MODE_MASK (xmode)) != 0) + op_mode = xmode; + + inner = force_to_mode (inner, op_mode, inner_mask, next_select); + + if (xmode != op_mode || inner != XEXP (x, 0)) + { + x = simplify_gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1)); + xmode = op_mode; + } + } + + /* If we have (and (lshiftrt FOO C1) C2) where the combination of the + shift and AND produces only copies of the sign bit (C2 is one less + than a power of two), we can do this with just a shift. */ + + if (GET_CODE (x) == LSHIFTRT + && CONST_INT_P (XEXP (x, 1)) + /* The shift puts one of the sign bit copies in the least significant + bit. */ + && ((INTVAL (XEXP (x, 1)) + + num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))) + >= GET_MODE_PRECISION (xmode)) + && pow2p_hwi (mask + 1) + /* Number of bits left after the shift must be more than the mask + needs. */ + && ((INTVAL (XEXP (x, 1)) + exact_log2 (mask + 1)) + <= GET_MODE_PRECISION (xmode)) + /* Must be more sign bit copies than the mask needs. */ + && ((int) num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))) + >= exact_log2 (mask + 1))) + { + int nbits = GET_MODE_PRECISION (xmode) - exact_log2 (mask + 1); + x = simplify_gen_binary (LSHIFTRT, xmode, XEXP (x, 0), + gen_int_shift_amount (xmode, nbits)); + } + goto shiftrt; + + case ASHIFTRT: + /* If we are just looking for the sign bit, we don't need this shift at + all, even if it has a variable count. */ + if (val_signbit_p (xmode, mask)) + return force_to_mode (XEXP (x, 0), mode, mask, next_select); + + /* If this is a shift by a constant, get a mask that contains those bits + that are not copies of the sign bit. We then have two cases: If + MASK only includes those bits, this can be a logical shift, which may + allow simplifications. If MASK is a single-bit field not within + those bits, we are requesting a copy of the sign bit and hence can + shift the sign bit to the appropriate location. */ + + if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) >= 0 + && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT) + { + unsigned HOST_WIDE_INT nonzero; + int i; + + /* If the considered data is wider than HOST_WIDE_INT, we can't + represent a mask for all its bits in a single scalar. + But we only care about the lower bits, so calculate these. */ + + if (GET_MODE_PRECISION (xmode) > HOST_BITS_PER_WIDE_INT) + { + nonzero = HOST_WIDE_INT_M1U; + + /* GET_MODE_PRECISION (GET_MODE (x)) - INTVAL (XEXP (x, 1)) + is the number of bits a full-width mask would have set. + We need only shift if these are fewer than nonzero can + hold. If not, we must keep all bits set in nonzero. */ + + if (GET_MODE_PRECISION (xmode) - INTVAL (XEXP (x, 1)) + < HOST_BITS_PER_WIDE_INT) + nonzero >>= INTVAL (XEXP (x, 1)) + + HOST_BITS_PER_WIDE_INT + - GET_MODE_PRECISION (xmode); + } + else + { + nonzero = GET_MODE_MASK (xmode); + nonzero >>= INTVAL (XEXP (x, 1)); + } + + if ((mask & ~nonzero) == 0) + { + x = simplify_shift_const (NULL_RTX, LSHIFTRT, xmode, + XEXP (x, 0), INTVAL (XEXP (x, 1))); + if (GET_CODE (x) != ASHIFTRT) + return force_to_mode (x, mode, mask, next_select); + } + + else if ((i = exact_log2 (mask)) >= 0) + { + x = simplify_shift_const + (NULL_RTX, LSHIFTRT, xmode, XEXP (x, 0), + GET_MODE_PRECISION (xmode) - 1 - i); + + if (GET_CODE (x) != ASHIFTRT) + return force_to_mode (x, mode, mask, next_select); + } + } + + /* If MASK is 1, convert this to an LSHIFTRT. This can be done + even if the shift count isn't a constant. */ + if (mask == 1) + x = simplify_gen_binary (LSHIFTRT, xmode, XEXP (x, 0), XEXP (x, 1)); + + shiftrt: + + /* If this is a zero- or sign-extension operation that just affects bits + we don't care about, remove it. Be sure the call above returned + something that is still a shift. */ + + if ((GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ASHIFTRT) + && CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 0 + && (INTVAL (XEXP (x, 1)) + <= GET_MODE_PRECISION (xmode) - (floor_log2 (mask) + 1)) + && GET_CODE (XEXP (x, 0)) == ASHIFT + && XEXP (XEXP (x, 0), 1) == XEXP (x, 1)) + return force_to_mode (XEXP (XEXP (x, 0), 0), mode, mask, + next_select); + + break; + + case ROTATE: + case ROTATERT: + /* If the shift count is constant and we can do computations + in the mode of X, compute where the bits we care about are. + Otherwise, we can't do anything. Don't change the mode of + the shift or propagate MODE into the shift, though. */ + if (CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) >= 0) + { + temp = simplify_binary_operation (code == ROTATE ? ROTATERT : ROTATE, + xmode, gen_int_mode (mask, xmode), + XEXP (x, 1)); + if (temp && CONST_INT_P (temp)) + x = simplify_gen_binary (code, xmode, + force_to_mode (XEXP (x, 0), xmode, + INTVAL (temp), next_select), + XEXP (x, 1)); + } + break; + + case NEG: + /* If we just want the low-order bit, the NEG isn't needed since it + won't change the low-order bit. */ + if (mask == 1) + return force_to_mode (XEXP (x, 0), mode, mask, just_select); + + /* We need any bits less significant than the most significant bit in + MASK since carries from those bits will affect the bits we are + interested in. */ + mask = fuller_mask; + goto unop; + + case NOT: + /* (not FOO) is (xor FOO CONST), so if FOO is an LSHIFTRT, we can do the + same as the XOR case above. Ensure that the constant we form is not + wider than the mode of X. */ + + if (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && CONST_INT_P (XEXP (XEXP (x, 0), 1)) + && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0 + && (INTVAL (XEXP (XEXP (x, 0), 1)) + floor_log2 (mask) + < GET_MODE_PRECISION (xmode)) + && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT) + { + temp = gen_int_mode (mask << INTVAL (XEXP (XEXP (x, 0), 1)), xmode); + temp = simplify_gen_binary (XOR, xmode, XEXP (XEXP (x, 0), 0), temp); + x = simplify_gen_binary (LSHIFTRT, xmode, + temp, XEXP (XEXP (x, 0), 1)); + + return force_to_mode (x, mode, mask, next_select); + } + + /* (and (not FOO) CONST) is (not (or FOO (not CONST))), so we must + use the full mask inside the NOT. */ + mask = fuller_mask; + + unop: + op0 = gen_lowpart_or_truncate (op_mode, + force_to_mode (XEXP (x, 0), mode, mask, + next_select)); + if (op_mode != xmode || op0 != XEXP (x, 0)) + { + x = simplify_gen_unary (code, op_mode, op0, op_mode); + xmode = op_mode; + } + break; + + case NE: + /* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is included + in STORE_FLAG_VALUE and FOO has a single bit that might be nonzero, + which is equal to STORE_FLAG_VALUE. */ + if ((mask & ~STORE_FLAG_VALUE) == 0 + && XEXP (x, 1) == const0_rtx + && GET_MODE (XEXP (x, 0)) == mode + && pow2p_hwi (nonzero_bits (XEXP (x, 0), mode)) + && (nonzero_bits (XEXP (x, 0), mode) + == (unsigned HOST_WIDE_INT) STORE_FLAG_VALUE)) + return force_to_mode (XEXP (x, 0), mode, mask, next_select); + + break; + + case IF_THEN_ELSE: + /* We have no way of knowing if the IF_THEN_ELSE can itself be + written in a narrower mode. We play it safe and do not do so. */ + + op0 = gen_lowpart_or_truncate (xmode, + force_to_mode (XEXP (x, 1), mode, + mask, next_select)); + op1 = gen_lowpart_or_truncate (xmode, + force_to_mode (XEXP (x, 2), mode, + mask, next_select)); + if (op0 != XEXP (x, 1) || op1 != XEXP (x, 2)) + x = simplify_gen_ternary (IF_THEN_ELSE, xmode, + GET_MODE (XEXP (x, 0)), XEXP (x, 0), + op0, op1); + break; + + default: + break; + } + + /* Ensure we return a value of the proper mode. */ + return gen_lowpart_or_truncate (mode, x); +} + +/* Return nonzero if X is an expression that has one of two values depending on + whether some other value is zero or nonzero. In that case, we return the + value that is being tested, *PTRUE is set to the value if the rtx being + returned has a nonzero value, and *PFALSE is set to the other alternative. + + If we return zero, we set *PTRUE and *PFALSE to X. */ + +static rtx +if_then_else_cond (rtx x, rtx *ptrue, rtx *pfalse) +{ + machine_mode mode = GET_MODE (x); + enum rtx_code code = GET_CODE (x); + rtx cond0, cond1, true0, true1, false0, false1; + unsigned HOST_WIDE_INT nz; + scalar_int_mode int_mode; + + /* If we are comparing a value against zero, we are done. */ + if ((code == NE || code == EQ) + && XEXP (x, 1) == const0_rtx) + { + *ptrue = (code == NE) ? const_true_rtx : const0_rtx; + *pfalse = (code == NE) ? const0_rtx : const_true_rtx; + return XEXP (x, 0); + } + + /* If this is a unary operation whose operand has one of two values, apply + our opcode to compute those values. */ + else if (UNARY_P (x) + && (cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0)) != 0) + { + *ptrue = simplify_gen_unary (code, mode, true0, GET_MODE (XEXP (x, 0))); + *pfalse = simplify_gen_unary (code, mode, false0, + GET_MODE (XEXP (x, 0))); + return cond0; + } + + /* If this is a COMPARE, do nothing, since the IF_THEN_ELSE we would + make can't possibly match and would suppress other optimizations. */ + else if (code == COMPARE) + ; + + /* If this is a binary operation, see if either side has only one of two + values. If either one does or if both do and they are conditional on + the same value, compute the new true and false values. */ + else if (BINARY_P (x)) + { + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + cond0 = if_then_else_cond (op0, &true0, &false0); + cond1 = if_then_else_cond (op1, &true1, &false1); + + if ((cond0 != 0 && cond1 != 0 && !rtx_equal_p (cond0, cond1)) + && (REG_P (op0) || REG_P (op1))) + { + /* Try to enable a simplification by undoing work done by + if_then_else_cond if it converted a REG into something more + complex. */ + if (REG_P (op0)) + { + cond0 = 0; + true0 = false0 = op0; + } + else + { + cond1 = 0; + true1 = false1 = op1; + } + } + + if ((cond0 != 0 || cond1 != 0) + && ! (cond0 != 0 && cond1 != 0 && !rtx_equal_p (cond0, cond1))) + { + /* If if_then_else_cond returned zero, then true/false are the + same rtl. We must copy one of them to prevent invalid rtl + sharing. */ + if (cond0 == 0) + true0 = copy_rtx (true0); + else if (cond1 == 0) + true1 = copy_rtx (true1); + + if (COMPARISON_P (x)) + { + *ptrue = simplify_gen_relational (code, mode, VOIDmode, + true0, true1); + *pfalse = simplify_gen_relational (code, mode, VOIDmode, + false0, false1); + } + else + { + *ptrue = simplify_gen_binary (code, mode, true0, true1); + *pfalse = simplify_gen_binary (code, mode, false0, false1); + } + + return cond0 ? cond0 : cond1; + } + + /* See if we have PLUS, IOR, XOR, MINUS or UMAX, where one of the + operands is zero when the other is nonzero, and vice-versa, + and STORE_FLAG_VALUE is 1 or -1. */ + + if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && (code == PLUS || code == IOR || code == XOR || code == MINUS + || code == UMAX) + && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT) + { + rtx op0 = XEXP (XEXP (x, 0), 1); + rtx op1 = XEXP (XEXP (x, 1), 1); + + cond0 = XEXP (XEXP (x, 0), 0); + cond1 = XEXP (XEXP (x, 1), 0); + + if (COMPARISON_P (cond0) + && COMPARISON_P (cond1) + && SCALAR_INT_MODE_P (mode) + && ((GET_CODE (cond0) == reversed_comparison_code (cond1, NULL) + && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0)) + && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1))) + || ((swap_condition (GET_CODE (cond0)) + == reversed_comparison_code (cond1, NULL)) + && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1)) + && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0)))) + && ! side_effects_p (x)) + { + *ptrue = simplify_gen_binary (MULT, mode, op0, const_true_rtx); + *pfalse = simplify_gen_binary (MULT, mode, + (code == MINUS + ? simplify_gen_unary (NEG, mode, + op1, mode) + : op1), + const_true_rtx); + return cond0; + } + } + + /* Similarly for MULT, AND and UMIN, except that for these the result + is always zero. */ + if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && (code == MULT || code == AND || code == UMIN) + && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT) + { + cond0 = XEXP (XEXP (x, 0), 0); + cond1 = XEXP (XEXP (x, 1), 0); + + if (COMPARISON_P (cond0) + && COMPARISON_P (cond1) + && ((GET_CODE (cond0) == reversed_comparison_code (cond1, NULL) + && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0)) + && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1))) + || ((swap_condition (GET_CODE (cond0)) + == reversed_comparison_code (cond1, NULL)) + && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1)) + && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0)))) + && ! side_effects_p (x)) + { + *ptrue = *pfalse = const0_rtx; + return cond0; + } + } + } + + else if (code == IF_THEN_ELSE) + { + /* If we have IF_THEN_ELSE already, extract the condition and + canonicalize it if it is NE or EQ. */ + cond0 = XEXP (x, 0); + *ptrue = XEXP (x, 1), *pfalse = XEXP (x, 2); + if (GET_CODE (cond0) == NE && XEXP (cond0, 1) == const0_rtx) + return XEXP (cond0, 0); + else if (GET_CODE (cond0) == EQ && XEXP (cond0, 1) == const0_rtx) + { + *ptrue = XEXP (x, 2), *pfalse = XEXP (x, 1); + return XEXP (cond0, 0); + } + else + return cond0; + } + + /* If X is a SUBREG, we can narrow both the true and false values + if the inner expression, if there is a condition. */ + else if (code == SUBREG + && (cond0 = if_then_else_cond (SUBREG_REG (x), &true0, + &false0)) != 0) + { + true0 = simplify_gen_subreg (mode, true0, + GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); + false0 = simplify_gen_subreg (mode, false0, + GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); + if (true0 && false0) + { + *ptrue = true0; + *pfalse = false0; + return cond0; + } + } + + /* If X is a constant, this isn't special and will cause confusions + if we treat it as such. Likewise if it is equivalent to a constant. */ + else if (CONSTANT_P (x) + || ((cond0 = get_last_value (x)) != 0 && CONSTANT_P (cond0))) + ; + + /* If we're in BImode, canonicalize on 0 and STORE_FLAG_VALUE, as that + will be least confusing to the rest of the compiler. */ + else if (mode == BImode) + { + *ptrue = GEN_INT (STORE_FLAG_VALUE), *pfalse = const0_rtx; + return x; + } + + /* If X is known to be either 0 or -1, those are the true and + false values when testing X. */ + else if (x == constm1_rtx || x == const0_rtx + || (is_a <scalar_int_mode> (mode, &int_mode) + && (num_sign_bit_copies (x, int_mode) + == GET_MODE_PRECISION (int_mode)))) + { + *ptrue = constm1_rtx, *pfalse = const0_rtx; + return x; + } + + /* Likewise for 0 or a single bit. */ + else if (HWI_COMPUTABLE_MODE_P (mode) + && pow2p_hwi (nz = nonzero_bits (x, mode))) + { + *ptrue = gen_int_mode (nz, mode), *pfalse = const0_rtx; + return x; + } + + /* Otherwise fail; show no condition with true and false values the same. */ + *ptrue = *pfalse = x; + return 0; +} + +/* Return the value of expression X given the fact that condition COND + is known to be true when applied to REG as its first operand and VAL + as its second. X is known to not be shared and so can be modified in + place. + + We only handle the simplest cases, and specifically those cases that + arise with IF_THEN_ELSE expressions. */ + +static rtx +known_cond (rtx x, enum rtx_code cond, rtx reg, rtx val) +{ + enum rtx_code code = GET_CODE (x); + const char *fmt; + int i, j; + + if (side_effects_p (x)) + return x; + + /* If either operand of the condition is a floating point value, + then we have to avoid collapsing an EQ comparison. */ + if (cond == EQ + && rtx_equal_p (x, reg) + && ! FLOAT_MODE_P (GET_MODE (x)) + && ! FLOAT_MODE_P (GET_MODE (val))) + return val; + + if (cond == UNEQ && rtx_equal_p (x, reg)) + return val; + + /* If X is (abs REG) and we know something about REG's relationship + with zero, we may be able to simplify this. */ + + if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx) + switch (cond) + { + case GE: case GT: case EQ: + return XEXP (x, 0); + case LT: case LE: + return simplify_gen_unary (NEG, GET_MODE (XEXP (x, 0)), + XEXP (x, 0), + GET_MODE (XEXP (x, 0))); + default: + break; + } + + /* The only other cases we handle are MIN, MAX, and comparisons if the + operands are the same as REG and VAL. */ + + else if (COMPARISON_P (x) || COMMUTATIVE_ARITH_P (x)) + { + if (rtx_equal_p (XEXP (x, 0), val)) + { + std::swap (val, reg); + cond = swap_condition (cond); + } + + if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val)) + { + if (COMPARISON_P (x)) + { + if (comparison_dominates_p (cond, code)) + return VECTOR_MODE_P (GET_MODE (x)) ? x : const_true_rtx; + + code = reversed_comparison_code (x, NULL); + if (code != UNKNOWN + && comparison_dominates_p (cond, code)) + return CONST0_RTX (GET_MODE (x)); + else + return x; + } + else if (code == SMAX || code == SMIN + || code == UMIN || code == UMAX) + { + int unsignedp = (code == UMIN || code == UMAX); + + /* Do not reverse the condition when it is NE or EQ. + This is because we cannot conclude anything about + the value of 'SMAX (x, y)' when x is not equal to y, + but we can when x equals y. */ + if ((code == SMAX || code == UMAX) + && ! (cond == EQ || cond == NE)) + cond = reverse_condition (cond); + + switch (cond) + { + case GE: case GT: + return unsignedp ? x : XEXP (x, 1); + case LE: case LT: + return unsignedp ? x : XEXP (x, 0); + case GEU: case GTU: + return unsignedp ? XEXP (x, 1) : x; + case LEU: case LTU: + return unsignedp ? XEXP (x, 0) : x; + default: + break; + } + } + } + } + else if (code == SUBREG) + { + machine_mode inner_mode = GET_MODE (SUBREG_REG (x)); + rtx new_rtx, r = known_cond (SUBREG_REG (x), cond, reg, val); + + if (SUBREG_REG (x) != r) + { + /* We must simplify subreg here, before we lose track of the + original inner_mode. */ + new_rtx = simplify_subreg (GET_MODE (x), r, + inner_mode, SUBREG_BYTE (x)); + if (new_rtx) + return new_rtx; + else + SUBST (SUBREG_REG (x), r); + } + + return x; + } + /* We don't have to handle SIGN_EXTEND here, because even in the + case of replacing something with a modeless CONST_INT, a + CONST_INT is already (supposed to be) a valid sign extension for + its narrower mode, which implies it's already properly + sign-extended for the wider mode. Now, for ZERO_EXTEND, the + story is different. */ + else if (code == ZERO_EXTEND) + { + machine_mode inner_mode = GET_MODE (XEXP (x, 0)); + rtx new_rtx, r = known_cond (XEXP (x, 0), cond, reg, val); + + if (XEXP (x, 0) != r) + { + /* We must simplify the zero_extend here, before we lose + track of the original inner_mode. */ + new_rtx = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x), + r, inner_mode); + if (new_rtx) + return new_rtx; + else + SUBST (XEXP (x, 0), r); + } + + return x; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val)); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j), + cond, reg, val)); + } + + return x; +} + +/* See if X and Y are equal for the purposes of seeing if we can rewrite an + assignment as a field assignment. */ + +static int +rtx_equal_for_field_assignment_p (rtx x, rtx y, bool widen_x) +{ + if (widen_x && GET_MODE (x) != GET_MODE (y)) + { + if (paradoxical_subreg_p (GET_MODE (x), GET_MODE (y))) + return 0; + if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN) + return 0; + x = adjust_address_nv (x, GET_MODE (y), + byte_lowpart_offset (GET_MODE (y), + GET_MODE (x))); + } + + if (x == y || rtx_equal_p (x, y)) + return 1; + + if (x == 0 || y == 0 || GET_MODE (x) != GET_MODE (y)) + return 0; + + /* Check for a paradoxical SUBREG of a MEM compared with the MEM. + Note that all SUBREGs of MEM are paradoxical; otherwise they + would have been rewritten. */ + if (MEM_P (x) && GET_CODE (y) == SUBREG + && MEM_P (SUBREG_REG (y)) + && rtx_equal_p (SUBREG_REG (y), + gen_lowpart (GET_MODE (SUBREG_REG (y)), x))) + return 1; + + if (MEM_P (y) && GET_CODE (x) == SUBREG + && MEM_P (SUBREG_REG (x)) + && rtx_equal_p (SUBREG_REG (x), + gen_lowpart (GET_MODE (SUBREG_REG (x)), y))) + return 1; + + /* We used to see if get_last_value of X and Y were the same but that's + not correct. In one direction, we'll cause the assignment to have + the wrong destination and in the case, we'll import a register into this + insn that might have already have been dead. So fail if none of the + above cases are true. */ + return 0; +} + +/* See if X, a SET operation, can be rewritten as a bit-field assignment. + Return that assignment if so. + + We only handle the most common cases. */ + +static rtx +make_field_assignment (rtx x) +{ + rtx dest = SET_DEST (x); + rtx src = SET_SRC (x); + rtx assign; + rtx rhs, lhs; + HOST_WIDE_INT c1; + HOST_WIDE_INT pos; + unsigned HOST_WIDE_INT len; + rtx other; + + /* All the rules in this function are specific to scalar integers. */ + scalar_int_mode mode; + if (!is_a <scalar_int_mode> (GET_MODE (dest), &mode)) + return x; + + /* If SRC was (and (not (ashift (const_int 1) POS)) DEST), this is + a clear of a one-bit field. We will have changed it to + (and (rotate (const_int -2) POS) DEST), so check for that. Also check + for a SUBREG. */ + + if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE + && CONST_INT_P (XEXP (XEXP (src, 0), 0)) + && INTVAL (XEXP (XEXP (src, 0), 0)) == -2 + && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1))) + { + assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), + 1, 1, 1, 0); + if (assign != 0) + return gen_rtx_SET (assign, const0_rtx); + return x; + } + + if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG + && subreg_lowpart_p (XEXP (src, 0)) + && partial_subreg_p (XEXP (src, 0)) + && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE + && CONST_INT_P (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) + && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2 + && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1))) + { + assign = make_extraction (VOIDmode, dest, 0, + XEXP (SUBREG_REG (XEXP (src, 0)), 1), + 1, 1, 1, 0); + if (assign != 0) + return gen_rtx_SET (assign, const0_rtx); + return x; + } + + /* If SRC is (ior (ashift (const_int 1) POS) DEST), this is a set of a + one-bit field. */ + if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT + && XEXP (XEXP (src, 0), 0) == const1_rtx + && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1))) + { + assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1), + 1, 1, 1, 0); + if (assign != 0) + return gen_rtx_SET (assign, const1_rtx); + return x; + } + + /* If DEST is already a field assignment, i.e. ZERO_EXTRACT, and the + SRC is an AND with all bits of that field set, then we can discard + the AND. */ + if (GET_CODE (dest) == ZERO_EXTRACT + && CONST_INT_P (XEXP (dest, 1)) + && GET_CODE (src) == AND + && CONST_INT_P (XEXP (src, 1))) + { + HOST_WIDE_INT width = INTVAL (XEXP (dest, 1)); + unsigned HOST_WIDE_INT and_mask = INTVAL (XEXP (src, 1)); + unsigned HOST_WIDE_INT ze_mask; + + if (width >= HOST_BITS_PER_WIDE_INT) + ze_mask = -1; + else + ze_mask = ((unsigned HOST_WIDE_INT)1 << width) - 1; + + /* Complete overlap. We can remove the source AND. */ + if ((and_mask & ze_mask) == ze_mask) + return gen_rtx_SET (dest, XEXP (src, 0)); + + /* Partial overlap. We can reduce the source AND. */ + if ((and_mask & ze_mask) != and_mask) + { + src = gen_rtx_AND (mode, XEXP (src, 0), + gen_int_mode (and_mask & ze_mask, mode)); + return gen_rtx_SET (dest, src); + } + } + + /* The other case we handle is assignments into a constant-position + field. They look like (ior/xor (and DEST C1) OTHER). If C1 represents + a mask that has all one bits except for a group of zero bits and + OTHER is known to have zeros where C1 has ones, this is such an + assignment. Compute the position and length from C1. Shift OTHER + to the appropriate position, force it to the required mode, and + make the extraction. Check for the AND in both operands. */ + + /* One or more SUBREGs might obscure the constant-position field + assignment. The first one we are likely to encounter is an outer + narrowing SUBREG, which we can just strip for the purposes of + identifying the constant-field assignment. */ + scalar_int_mode src_mode = mode; + if (GET_CODE (src) == SUBREG + && subreg_lowpart_p (src) + && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (src)), &src_mode)) + src = SUBREG_REG (src); + + if (GET_CODE (src) != IOR && GET_CODE (src) != XOR) + return x; + + rhs = expand_compound_operation (XEXP (src, 0)); + lhs = expand_compound_operation (XEXP (src, 1)); + + if (GET_CODE (rhs) == AND + && CONST_INT_P (XEXP (rhs, 1)) + && rtx_equal_for_field_assignment_p (XEXP (rhs, 0), dest)) + c1 = INTVAL (XEXP (rhs, 1)), other = lhs; + /* The second SUBREG that might get in the way is a paradoxical + SUBREG around the first operand of the AND. We want to + pretend the operand is as wide as the destination here. We + do this by adjusting the MEM to wider mode for the sole + purpose of the call to rtx_equal_for_field_assignment_p. Also + note this trick only works for MEMs. */ + else if (GET_CODE (rhs) == AND + && paradoxical_subreg_p (XEXP (rhs, 0)) + && MEM_P (SUBREG_REG (XEXP (rhs, 0))) + && CONST_INT_P (XEXP (rhs, 1)) + && rtx_equal_for_field_assignment_p (SUBREG_REG (XEXP (rhs, 0)), + dest, true)) + c1 = INTVAL (XEXP (rhs, 1)), other = lhs; + else if (GET_CODE (lhs) == AND + && CONST_INT_P (XEXP (lhs, 1)) + && rtx_equal_for_field_assignment_p (XEXP (lhs, 0), dest)) + c1 = INTVAL (XEXP (lhs, 1)), other = rhs; + /* The second SUBREG that might get in the way is a paradoxical + SUBREG around the first operand of the AND. We want to + pretend the operand is as wide as the destination here. We + do this by adjusting the MEM to wider mode for the sole + purpose of the call to rtx_equal_for_field_assignment_p. Also + note this trick only works for MEMs. */ + else if (GET_CODE (lhs) == AND + && paradoxical_subreg_p (XEXP (lhs, 0)) + && MEM_P (SUBREG_REG (XEXP (lhs, 0))) + && CONST_INT_P (XEXP (lhs, 1)) + && rtx_equal_for_field_assignment_p (SUBREG_REG (XEXP (lhs, 0)), + dest, true)) + c1 = INTVAL (XEXP (lhs, 1)), other = rhs; + else + return x; + + pos = get_pos_from_mask ((~c1) & GET_MODE_MASK (mode), &len); + if (pos < 0 + || pos + len > GET_MODE_PRECISION (mode) + || GET_MODE_PRECISION (mode) > HOST_BITS_PER_WIDE_INT + || (c1 & nonzero_bits (other, mode)) != 0) + return x; + + assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0); + if (assign == 0) + return x; + + /* The mode to use for the source is the mode of the assignment, or of + what is inside a possible STRICT_LOW_PART. */ + machine_mode new_mode = (GET_CODE (assign) == STRICT_LOW_PART + ? GET_MODE (XEXP (assign, 0)) : GET_MODE (assign)); + + /* Shift OTHER right POS places and make it the source, restricting it + to the proper length and mode. */ + + src = canon_reg_for_combine (simplify_shift_const (NULL_RTX, LSHIFTRT, + src_mode, other, pos), + dest); + src = force_to_mode (src, new_mode, + len >= HOST_BITS_PER_WIDE_INT + ? HOST_WIDE_INT_M1U + : (HOST_WIDE_INT_1U << len) - 1, + 0); + + /* If SRC is masked by an AND that does not make a difference in + the value being stored, strip it. */ + if (GET_CODE (assign) == ZERO_EXTRACT + && CONST_INT_P (XEXP (assign, 1)) + && INTVAL (XEXP (assign, 1)) < HOST_BITS_PER_WIDE_INT + && GET_CODE (src) == AND + && CONST_INT_P (XEXP (src, 1)) + && UINTVAL (XEXP (src, 1)) + == (HOST_WIDE_INT_1U << INTVAL (XEXP (assign, 1))) - 1) + src = XEXP (src, 0); + + return gen_rtx_SET (assign, src); +} + +/* See if X is of the form (+ (* a c) (* b c)) and convert to (* (+ a b) c) + if so. */ + +static rtx +apply_distributive_law (rtx x) +{ + enum rtx_code code = GET_CODE (x); + enum rtx_code inner_code; + rtx lhs, rhs, other; + rtx tem; + + /* Distributivity is not true for floating point as it can change the + value. So we don't do it unless -funsafe-math-optimizations. */ + if (FLOAT_MODE_P (GET_MODE (x)) + && ! flag_unsafe_math_optimizations) + return x; + + /* The outer operation can only be one of the following: */ + if (code != IOR && code != AND && code != XOR + && code != PLUS && code != MINUS) + return x; + + lhs = XEXP (x, 0); + rhs = XEXP (x, 1); + + /* If either operand is a primitive we can't do anything, so get out + fast. */ + if (OBJECT_P (lhs) || OBJECT_P (rhs)) + return x; + + lhs = expand_compound_operation (lhs); + rhs = expand_compound_operation (rhs); + inner_code = GET_CODE (lhs); + if (inner_code != GET_CODE (rhs)) + return x; + + /* See if the inner and outer operations distribute. */ + switch (inner_code) + { + case LSHIFTRT: + case ASHIFTRT: + case AND: + case IOR: + /* These all distribute except over PLUS. */ + if (code == PLUS || code == MINUS) + return x; + break; + + case MULT: + if (code != PLUS && code != MINUS) + return x; + break; + + case ASHIFT: + /* This is also a multiply, so it distributes over everything. */ + break; + + /* This used to handle SUBREG, but this turned out to be counter- + productive, since (subreg (op ...)) usually is not handled by + insn patterns, and this "optimization" therefore transformed + recognizable patterns into unrecognizable ones. Therefore the + SUBREG case was removed from here. + + It is possible that distributing SUBREG over arithmetic operations + leads to an intermediate result than can then be optimized further, + e.g. by moving the outer SUBREG to the other side of a SET as done + in simplify_set. This seems to have been the original intent of + handling SUBREGs here. + + However, with current GCC this does not appear to actually happen, + at least on major platforms. If some case is found where removing + the SUBREG case here prevents follow-on optimizations, distributing + SUBREGs ought to be re-added at that place, e.g. in simplify_set. */ + + default: + return x; + } + + /* Set LHS and RHS to the inner operands (A and B in the example + above) and set OTHER to the common operand (C in the example). + There is only one way to do this unless the inner operation is + commutative. */ + if (COMMUTATIVE_ARITH_P (lhs) + && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 0))) + other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 1); + else if (COMMUTATIVE_ARITH_P (lhs) + && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 1))) + other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 0); + else if (COMMUTATIVE_ARITH_P (lhs) + && rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 0))) + other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 1); + else if (rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 1))) + other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 0); + else + return x; + + /* Form the new inner operation, seeing if it simplifies first. */ + tem = simplify_gen_binary (code, GET_MODE (x), lhs, rhs); + + /* There is one exception to the general way of distributing: + (a | c) ^ (b | c) -> (a ^ b) & ~c */ + if (code == XOR && inner_code == IOR) + { + inner_code = AND; + other = simplify_gen_unary (NOT, GET_MODE (x), other, GET_MODE (x)); + } + + /* We may be able to continuing distributing the result, so call + ourselves recursively on the inner operation before forming the + outer operation, which we return. */ + return simplify_gen_binary (inner_code, GET_MODE (x), + apply_distributive_law (tem), other); +} + +/* See if X is of the form (* (+ A B) C), and if so convert to + (+ (* A C) (* B C)) and try to simplify. + + Most of the time, this results in no change. However, if some of + the operands are the same or inverses of each other, simplifications + will result. + + For example, (and (ior A B) (not B)) can occur as the result of + expanding a bit field assignment. When we apply the distributive + law to this, we get (ior (and (A (not B))) (and (B (not B)))), + which then simplifies to (and (A (not B))). + + Note that no checks happen on the validity of applying the inverse + distributive law. This is pointless since we can do it in the + few places where this routine is called. + + N is the index of the term that is decomposed (the arithmetic operation, + i.e. (+ A B) in the first example above). !N is the index of the term that + is distributed, i.e. of C in the first example above. */ +static rtx +distribute_and_simplify_rtx (rtx x, int n) +{ + machine_mode mode; + enum rtx_code outer_code, inner_code; + rtx decomposed, distributed, inner_op0, inner_op1, new_op0, new_op1, tmp; + + /* Distributivity is not true for floating point as it can change the + value. So we don't do it unless -funsafe-math-optimizations. */ + if (FLOAT_MODE_P (GET_MODE (x)) + && ! flag_unsafe_math_optimizations) + return NULL_RTX; + + decomposed = XEXP (x, n); + if (!ARITHMETIC_P (decomposed)) + return NULL_RTX; + + mode = GET_MODE (x); + outer_code = GET_CODE (x); + distributed = XEXP (x, !n); + + inner_code = GET_CODE (decomposed); + inner_op0 = XEXP (decomposed, 0); + inner_op1 = XEXP (decomposed, 1); + + /* Special case (and (xor B C) (not A)), which is equivalent to + (xor (ior A B) (ior A C)) */ + if (outer_code == AND && inner_code == XOR && GET_CODE (distributed) == NOT) + { + distributed = XEXP (distributed, 0); + outer_code = IOR; + } + + if (n == 0) + { + /* Distribute the second term. */ + new_op0 = simplify_gen_binary (outer_code, mode, inner_op0, distributed); + new_op1 = simplify_gen_binary (outer_code, mode, inner_op1, distributed); + } + else + { + /* Distribute the first term. */ + new_op0 = simplify_gen_binary (outer_code, mode, distributed, inner_op0); + new_op1 = simplify_gen_binary (outer_code, mode, distributed, inner_op1); + } + + tmp = apply_distributive_law (simplify_gen_binary (inner_code, mode, + new_op0, new_op1)); + if (GET_CODE (tmp) != outer_code + && (set_src_cost (tmp, mode, optimize_this_for_speed_p) + < set_src_cost (x, mode, optimize_this_for_speed_p))) + return tmp; + + return NULL_RTX; +} + +/* Simplify a logical `and' of VAROP with the constant CONSTOP, to be done + in MODE. Return an equivalent form, if different from (and VAROP + (const_int CONSTOP)). Otherwise, return NULL_RTX. */ + +static rtx +simplify_and_const_int_1 (scalar_int_mode mode, rtx varop, + unsigned HOST_WIDE_INT constop) +{ + unsigned HOST_WIDE_INT nonzero; + unsigned HOST_WIDE_INT orig_constop; + rtx orig_varop; + int i; + + orig_varop = varop; + orig_constop = constop; + if (GET_CODE (varop) == CLOBBER) + return NULL_RTX; + + /* Simplify VAROP knowing that we will be only looking at some of the + bits in it. + + Note by passing in CONSTOP, we guarantee that the bits not set in + CONSTOP are not significant and will never be examined. We must + ensure that is the case by explicitly masking out those bits + before returning. */ + varop = force_to_mode (varop, mode, constop, 0); + + /* If VAROP is a CLOBBER, we will fail so return it. */ + if (GET_CODE (varop) == CLOBBER) + return varop; + + /* If VAROP is a CONST_INT, then we need to apply the mask in CONSTOP + to VAROP and return the new constant. */ + if (CONST_INT_P (varop)) + return gen_int_mode (INTVAL (varop) & constop, mode); + + /* See what bits may be nonzero in VAROP. Unlike the general case of + a call to nonzero_bits, here we don't care about bits outside + MODE. */ + + nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode); + + /* Turn off all bits in the constant that are known to already be zero. + Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS + which is tested below. */ + + constop &= nonzero; + + /* If we don't have any bits left, return zero. */ + if (constop == 0 && !side_effects_p (varop)) + return const0_rtx; + + /* If VAROP is a NEG of something known to be zero or 1 and CONSTOP is + a power of two, we can replace this with an ASHIFT. */ + if (GET_CODE (varop) == NEG && nonzero_bits (XEXP (varop, 0), mode) == 1 + && (i = exact_log2 (constop)) >= 0) + return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (varop, 0), i); + + /* If VAROP is an IOR or XOR, apply the AND to both branches of the IOR + or XOR, then try to apply the distributive law. This may eliminate + operations if either branch can be simplified because of the AND. + It may also make some cases more complex, but those cases probably + won't match a pattern either with or without this. */ + + if (GET_CODE (varop) == IOR || GET_CODE (varop) == XOR) + { + scalar_int_mode varop_mode = as_a <scalar_int_mode> (GET_MODE (varop)); + return + gen_lowpart + (mode, + apply_distributive_law + (simplify_gen_binary (GET_CODE (varop), varop_mode, + simplify_and_const_int (NULL_RTX, varop_mode, + XEXP (varop, 0), + constop), + simplify_and_const_int (NULL_RTX, varop_mode, + XEXP (varop, 1), + constop)))); + } + + /* If VAROP is PLUS, and the constant is a mask of low bits, distribute + the AND and see if one of the operands simplifies to zero. If so, we + may eliminate it. */ + + if (GET_CODE (varop) == PLUS + && pow2p_hwi (constop + 1)) + { + rtx o0, o1; + + o0 = simplify_and_const_int (NULL_RTX, mode, XEXP (varop, 0), constop); + o1 = simplify_and_const_int (NULL_RTX, mode, XEXP (varop, 1), constop); + if (o0 == const0_rtx) + return o1; + if (o1 == const0_rtx) + return o0; + } + + /* Make a SUBREG if necessary. If we can't make it, fail. */ + varop = gen_lowpart (mode, varop); + if (varop == NULL_RTX || GET_CODE (varop) == CLOBBER) + return NULL_RTX; + + /* If we are only masking insignificant bits, return VAROP. */ + if (constop == nonzero) + return varop; + + if (varop == orig_varop && constop == orig_constop) + return NULL_RTX; + + /* Otherwise, return an AND. */ + return simplify_gen_binary (AND, mode, varop, gen_int_mode (constop, mode)); +} + + +/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done + in MODE. + + Return an equivalent form, if different from X. Otherwise, return X. If + X is zero, we are to always construct the equivalent form. */ + +static rtx +simplify_and_const_int (rtx x, scalar_int_mode mode, rtx varop, + unsigned HOST_WIDE_INT constop) +{ + rtx tem = simplify_and_const_int_1 (mode, varop, constop); + if (tem) + return tem; + + if (!x) + x = simplify_gen_binary (AND, GET_MODE (varop), varop, + gen_int_mode (constop, mode)); + if (GET_MODE (x) != mode) + x = gen_lowpart (mode, x); + return x; +} + +/* Given a REG X of mode XMODE, compute which bits in X can be nonzero. + We don't care about bits outside of those defined in MODE. + We DO care about all the bits in MODE, even if XMODE is smaller than MODE. + + For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is + a shift, AND, or zero_extract, we can do better. */ + +static rtx +reg_nonzero_bits_for_combine (const_rtx x, scalar_int_mode xmode, + scalar_int_mode mode, + unsigned HOST_WIDE_INT *nonzero) +{ + rtx tem; + reg_stat_type *rsp; + + /* If X is a register whose nonzero bits value is current, use it. + Otherwise, if X is a register whose value we can find, use that + value. Otherwise, use the previously-computed global nonzero bits + for this register. */ + + rsp = ®_stat[REGNO (x)]; + if (rsp->last_set_value != 0 + && (rsp->last_set_mode == mode + || (REGNO (x) >= FIRST_PSEUDO_REGISTER + && GET_MODE_CLASS (rsp->last_set_mode) == MODE_INT + && GET_MODE_CLASS (mode) == MODE_INT)) + && ((rsp->last_set_label >= label_tick_ebb_start + && rsp->last_set_label < label_tick) + || (rsp->last_set_label == label_tick + && DF_INSN_LUID (rsp->last_set) < subst_low_luid) + || (REGNO (x) >= FIRST_PSEUDO_REGISTER + && REGNO (x) < reg_n_sets_max + && REG_N_SETS (REGNO (x)) == 1 + && !REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), + REGNO (x))))) + { + /* Note that, even if the precision of last_set_mode is lower than that + of mode, record_value_for_reg invoked nonzero_bits on the register + with nonzero_bits_mode (because last_set_mode is necessarily integral + and HWI_COMPUTABLE_MODE_P in this case) so bits in nonzero_bits_mode + are all valid, hence in mode too since nonzero_bits_mode is defined + to the largest HWI_COMPUTABLE_MODE_P mode. */ + *nonzero &= rsp->last_set_nonzero_bits; + return NULL; + } + + tem = get_last_value (x); + if (tem) + { + if (SHORT_IMMEDIATES_SIGN_EXTEND) + tem = sign_extend_short_imm (tem, xmode, GET_MODE_PRECISION (mode)); + + return tem; + } + + if (nonzero_sign_valid && rsp->nonzero_bits) + { + unsigned HOST_WIDE_INT mask = rsp->nonzero_bits; + + if (GET_MODE_PRECISION (xmode) < GET_MODE_PRECISION (mode)) + /* We don't know anything about the upper bits. */ + mask |= GET_MODE_MASK (mode) ^ GET_MODE_MASK (xmode); + + *nonzero &= mask; + } + + return NULL; +} + +/* Given a reg X of mode XMODE, return the number of bits at the high-order + end of X that are known to be equal to the sign bit. X will be used + in mode MODE; the returned value will always be between 1 and the + number of bits in MODE. */ + +static rtx +reg_num_sign_bit_copies_for_combine (const_rtx x, scalar_int_mode xmode, + scalar_int_mode mode, + unsigned int *result) +{ + rtx tem; + reg_stat_type *rsp; + + rsp = ®_stat[REGNO (x)]; + if (rsp->last_set_value != 0 + && rsp->last_set_mode == mode + && ((rsp->last_set_label >= label_tick_ebb_start + && rsp->last_set_label < label_tick) + || (rsp->last_set_label == label_tick + && DF_INSN_LUID (rsp->last_set) < subst_low_luid) + || (REGNO (x) >= FIRST_PSEUDO_REGISTER + && REGNO (x) < reg_n_sets_max + && REG_N_SETS (REGNO (x)) == 1 + && !REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), + REGNO (x))))) + { + *result = rsp->last_set_sign_bit_copies; + return NULL; + } + + tem = get_last_value (x); + if (tem != 0) + return tem; + + if (nonzero_sign_valid && rsp->sign_bit_copies != 0 + && GET_MODE_PRECISION (xmode) == GET_MODE_PRECISION (mode)) + *result = rsp->sign_bit_copies; + + return NULL; +} + +/* Return the number of "extended" bits there are in X, when interpreted + as a quantity in MODE whose signedness is indicated by UNSIGNEDP. For + unsigned quantities, this is the number of high-order zero bits. + For signed quantities, this is the number of copies of the sign bit + minus 1. In both case, this function returns the number of "spare" + bits. For example, if two quantities for which this function returns + at least 1 are added, the addition is known not to overflow. + + This function will always return 0 unless called during combine, which + implies that it must be called from a define_split. */ + +unsigned int +extended_count (const_rtx x, machine_mode mode, int unsignedp) +{ + if (nonzero_sign_valid == 0) + return 0; + + scalar_int_mode int_mode; + return (unsignedp + ? (is_a <scalar_int_mode> (mode, &int_mode) + && HWI_COMPUTABLE_MODE_P (int_mode) + ? (unsigned int) (GET_MODE_PRECISION (int_mode) - 1 + - floor_log2 (nonzero_bits (x, int_mode))) + : 0) + : num_sign_bit_copies (x, mode) - 1); +} + +/* This function is called from `simplify_shift_const' to merge two + outer operations. Specifically, we have already found that we need + to perform operation *POP0 with constant *PCONST0 at the outermost + position. We would now like to also perform OP1 with constant CONST1 + (with *POP0 being done last). + + Return 1 if we can do the operation and update *POP0 and *PCONST0 with + the resulting operation. *PCOMP_P is set to 1 if we would need to + complement the innermost operand, otherwise it is unchanged. + + MODE is the mode in which the operation will be done. No bits outside + the width of this mode matter. It is assumed that the width of this mode + is smaller than or equal to HOST_BITS_PER_WIDE_INT. + + If *POP0 or OP1 are UNKNOWN, it means no operation is required. Only NEG, PLUS, + IOR, XOR, and AND are supported. We may set *POP0 to SET if the proper + result is simply *PCONST0. + + If the resulting operation cannot be expressed as one operation, we + return 0 and do not change *POP0, *PCONST0, and *PCOMP_P. */ + +static int +merge_outer_ops (enum rtx_code *pop0, HOST_WIDE_INT *pconst0, enum rtx_code op1, HOST_WIDE_INT const1, machine_mode mode, int *pcomp_p) +{ + enum rtx_code op0 = *pop0; + HOST_WIDE_INT const0 = *pconst0; + + const0 &= GET_MODE_MASK (mode); + const1 &= GET_MODE_MASK (mode); + + /* If OP0 is an AND, clear unimportant bits in CONST1. */ + if (op0 == AND) + const1 &= const0; + + /* If OP0 or OP1 is UNKNOWN, this is easy. Similarly if they are the same or + if OP0 is SET. */ + + if (op1 == UNKNOWN || op0 == SET) + return 1; + + else if (op0 == UNKNOWN) + op0 = op1, const0 = const1; + + else if (op0 == op1) + { + switch (op0) + { + case AND: + const0 &= const1; + break; + case IOR: + const0 |= const1; + break; + case XOR: + const0 ^= const1; + break; + case PLUS: + const0 += const1; + break; + case NEG: + op0 = UNKNOWN; + break; + default: + break; + } + } + + /* Otherwise, if either is a PLUS or NEG, we can't do anything. */ + else if (op0 == PLUS || op1 == PLUS || op0 == NEG || op1 == NEG) + return 0; + + /* If the two constants aren't the same, we can't do anything. The + remaining six cases can all be done. */ + else if (const0 != const1) + return 0; + + else + switch (op0) + { + case IOR: + if (op1 == AND) + /* (a & b) | b == b */ + op0 = SET; + else /* op1 == XOR */ + /* (a ^ b) | b == a | b */ + {;} + break; + + case XOR: + if (op1 == AND) + /* (a & b) ^ b == (~a) & b */ + op0 = AND, *pcomp_p = 1; + else /* op1 == IOR */ + /* (a | b) ^ b == a & ~b */ + op0 = AND, const0 = ~const0; + break; + + case AND: + if (op1 == IOR) + /* (a | b) & b == b */ + op0 = SET; + else /* op1 == XOR */ + /* (a ^ b) & b) == (~a) & b */ + *pcomp_p = 1; + break; + default: + break; + } + + /* Check for NO-OP cases. */ + const0 &= GET_MODE_MASK (mode); + if (const0 == 0 + && (op0 == IOR || op0 == XOR || op0 == PLUS)) + op0 = UNKNOWN; + else if (const0 == 0 && op0 == AND) + op0 = SET; + else if ((unsigned HOST_WIDE_INT) const0 == GET_MODE_MASK (mode) + && op0 == AND) + op0 = UNKNOWN; + + *pop0 = op0; + + /* ??? Slightly redundant with the above mask, but not entirely. + Moving this above means we'd have to sign-extend the mode mask + for the final test. */ + if (op0 != UNKNOWN && op0 != NEG) + *pconst0 = trunc_int_for_mode (const0, mode); + + return 1; +} + +/* A helper to simplify_shift_const_1 to determine the mode we can perform + the shift in. The original shift operation CODE is performed on OP in + ORIG_MODE. Return the wider mode MODE if we can perform the operation + in that mode. Return ORIG_MODE otherwise. We can also assume that the + result of the shift is subject to operation OUTER_CODE with operand + OUTER_CONST. */ + +static scalar_int_mode +try_widen_shift_mode (enum rtx_code code, rtx op, int count, + scalar_int_mode orig_mode, scalar_int_mode mode, + enum rtx_code outer_code, HOST_WIDE_INT outer_const) +{ + gcc_assert (GET_MODE_PRECISION (mode) > GET_MODE_PRECISION (orig_mode)); + + /* In general we can't perform in wider mode for right shift and rotate. */ + switch (code) + { + case ASHIFTRT: + /* We can still widen if the bits brought in from the left are identical + to the sign bit of ORIG_MODE. */ + if (num_sign_bit_copies (op, mode) + > (unsigned) (GET_MODE_PRECISION (mode) + - GET_MODE_PRECISION (orig_mode))) + return mode; + return orig_mode; + + case LSHIFTRT: + /* Similarly here but with zero bits. */ + if (HWI_COMPUTABLE_MODE_P (mode) + && (nonzero_bits (op, mode) & ~GET_MODE_MASK (orig_mode)) == 0) + return mode; + + /* We can also widen if the bits brought in will be masked off. This + operation is performed in ORIG_MODE. */ + if (outer_code == AND) + { + int care_bits = low_bitmask_len (orig_mode, outer_const); + + if (care_bits >= 0 + && GET_MODE_PRECISION (orig_mode) - care_bits >= count) + return mode; + } + /* fall through */ + + case ROTATE: + return orig_mode; + + case ROTATERT: + gcc_unreachable (); + + default: + return mode; + } +} + +/* Simplify a shift of VAROP by ORIG_COUNT bits. CODE says what kind + of shift. The result of the shift is RESULT_MODE. Return NULL_RTX + if we cannot simplify it. Otherwise, return a simplified value. + + The shift is normally computed in the widest mode we find in VAROP, as + long as it isn't a different number of words than RESULT_MODE. Exceptions + are ASHIFTRT and ROTATE, which are always done in their original mode. */ + +static rtx +simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode, + rtx varop, int orig_count) +{ + enum rtx_code orig_code = code; + rtx orig_varop = varop; + int count, log2; + machine_mode mode = result_mode; + machine_mode shift_mode; + scalar_int_mode tmode, inner_mode, int_mode, int_varop_mode, int_result_mode; + /* We form (outer_op (code varop count) (outer_const)). */ + enum rtx_code outer_op = UNKNOWN; + HOST_WIDE_INT outer_const = 0; + int complement_p = 0; + rtx new_rtx, x; + + /* Make sure and truncate the "natural" shift on the way in. We don't + want to do this inside the loop as it makes it more difficult to + combine shifts. */ + if (SHIFT_COUNT_TRUNCATED) + orig_count &= GET_MODE_UNIT_BITSIZE (mode) - 1; + + /* If we were given an invalid count, don't do anything except exactly + what was requested. */ + + if (orig_count < 0 || orig_count >= (int) GET_MODE_UNIT_PRECISION (mode)) + return NULL_RTX; + + count = orig_count; + + /* Unless one of the branches of the `if' in this loop does a `continue', + we will `break' the loop after the `if'. */ + + while (count != 0) + { + /* If we have an operand of (clobber (const_int 0)), fail. */ + if (GET_CODE (varop) == CLOBBER) + return NULL_RTX; + + /* Convert ROTATERT to ROTATE. */ + if (code == ROTATERT) + { + unsigned int bitsize = GET_MODE_UNIT_PRECISION (result_mode); + code = ROTATE; + count = bitsize - count; + } + + shift_mode = result_mode; + if (shift_mode != mode) + { + /* We only change the modes of scalar shifts. */ + int_mode = as_a <scalar_int_mode> (mode); + int_result_mode = as_a <scalar_int_mode> (result_mode); + shift_mode = try_widen_shift_mode (code, varop, count, + int_result_mode, int_mode, + outer_op, outer_const); + } + + scalar_int_mode shift_unit_mode + = as_a <scalar_int_mode> (GET_MODE_INNER (shift_mode)); + + /* Handle cases where the count is greater than the size of the mode + minus 1. For ASHIFT, use the size minus one as the count (this can + occur when simplifying (lshiftrt (ashiftrt ..))). For rotates, + take the count modulo the size. For other shifts, the result is + zero. + + Since these shifts are being produced by the compiler by combining + multiple operations, each of which are defined, we know what the + result is supposed to be. */ + + if (count > (GET_MODE_PRECISION (shift_unit_mode) - 1)) + { + if (code == ASHIFTRT) + count = GET_MODE_PRECISION (shift_unit_mode) - 1; + else if (code == ROTATE || code == ROTATERT) + count %= GET_MODE_PRECISION (shift_unit_mode); + else + { + /* We can't simply return zero because there may be an + outer op. */ + varop = const0_rtx; + count = 0; + break; + } + } + + /* If we discovered we had to complement VAROP, leave. Making a NOT + here would cause an infinite loop. */ + if (complement_p) + break; + + if (shift_mode == shift_unit_mode) + { + /* An arithmetic right shift of a quantity known to be -1 or 0 + is a no-op. */ + if (code == ASHIFTRT + && (num_sign_bit_copies (varop, shift_unit_mode) + == GET_MODE_PRECISION (shift_unit_mode))) + { + count = 0; + break; + } + + /* If we are doing an arithmetic right shift and discarding all but + the sign bit copies, this is equivalent to doing a shift by the + bitsize minus one. Convert it into that shift because it will + often allow other simplifications. */ + + if (code == ASHIFTRT + && (count + num_sign_bit_copies (varop, shift_unit_mode) + >= GET_MODE_PRECISION (shift_unit_mode))) + count = GET_MODE_PRECISION (shift_unit_mode) - 1; + + /* We simplify the tests below and elsewhere by converting + ASHIFTRT to LSHIFTRT if we know the sign bit is clear. + `make_compound_operation' will convert it to an ASHIFTRT for + those machines (such as VAX) that don't have an LSHIFTRT. */ + if (code == ASHIFTRT + && HWI_COMPUTABLE_MODE_P (shift_unit_mode) + && val_signbit_known_clear_p (shift_unit_mode, + nonzero_bits (varop, + shift_unit_mode))) + code = LSHIFTRT; + + if (((code == LSHIFTRT + && HWI_COMPUTABLE_MODE_P (shift_unit_mode) + && !(nonzero_bits (varop, shift_unit_mode) >> count)) + || (code == ASHIFT + && HWI_COMPUTABLE_MODE_P (shift_unit_mode) + && !((nonzero_bits (varop, shift_unit_mode) << count) + & GET_MODE_MASK (shift_unit_mode)))) + && !side_effects_p (varop)) + varop = const0_rtx; + } + + switch (GET_CODE (varop)) + { + case SIGN_EXTEND: + case ZERO_EXTEND: + case SIGN_EXTRACT: + case ZERO_EXTRACT: + new_rtx = expand_compound_operation (varop); + if (new_rtx != varop) + { + varop = new_rtx; + continue; + } + break; + + case MEM: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_mode = as_a <scalar_int_mode> (mode); + + /* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH + minus the width of a smaller mode, we can do this with a + SIGN_EXTEND or ZERO_EXTEND from the narrower memory location. */ + if ((code == ASHIFTRT || code == LSHIFTRT) + && ! mode_dependent_address_p (XEXP (varop, 0), + MEM_ADDR_SPACE (varop)) + && ! MEM_VOLATILE_P (varop) + && (int_mode_for_size (GET_MODE_BITSIZE (int_mode) - count, 1) + .exists (&tmode))) + { + new_rtx = adjust_address_nv (varop, tmode, + BYTES_BIG_ENDIAN ? 0 + : count / BITS_PER_UNIT); + + varop = gen_rtx_fmt_e (code == ASHIFTRT ? SIGN_EXTEND + : ZERO_EXTEND, int_mode, new_rtx); + count = 0; + continue; + } + break; + + case SUBREG: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_mode = as_a <scalar_int_mode> (mode); + int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop)); + + /* If VAROP is a SUBREG, strip it as long as the inner operand has + the same number of words as what we've seen so far. Then store + the widest mode in MODE. */ + if (subreg_lowpart_p (varop) + && is_int_mode (GET_MODE (SUBREG_REG (varop)), &inner_mode) + && GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (int_varop_mode) + && (CEIL (GET_MODE_SIZE (inner_mode), UNITS_PER_WORD) + == CEIL (GET_MODE_SIZE (int_mode), UNITS_PER_WORD)) + && GET_MODE_CLASS (int_varop_mode) == MODE_INT) + { + varop = SUBREG_REG (varop); + if (GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (int_mode)) + mode = inner_mode; + continue; + } + break; + + case MULT: + /* Some machines use MULT instead of ASHIFT because MULT + is cheaper. But it is still better on those machines to + merge two shifts into one. */ + if (CONST_INT_P (XEXP (varop, 1)) + && (log2 = exact_log2 (UINTVAL (XEXP (varop, 1)))) >= 0) + { + rtx log2_rtx = gen_int_shift_amount (GET_MODE (varop), log2); + varop = simplify_gen_binary (ASHIFT, GET_MODE (varop), + XEXP (varop, 0), log2_rtx); + continue; + } + break; + + case UDIV: + /* Similar, for when divides are cheaper. */ + if (CONST_INT_P (XEXP (varop, 1)) + && (log2 = exact_log2 (UINTVAL (XEXP (varop, 1)))) >= 0) + { + rtx log2_rtx = gen_int_shift_amount (GET_MODE (varop), log2); + varop = simplify_gen_binary (LSHIFTRT, GET_MODE (varop), + XEXP (varop, 0), log2_rtx); + continue; + } + break; + + case ASHIFTRT: + /* If we are extracting just the sign bit of an arithmetic + right shift, that shift is not needed. However, the sign + bit of a wider mode may be different from what would be + interpreted as the sign bit in a narrower mode, so, if + the result is narrower, don't discard the shift. */ + if (code == LSHIFTRT + && count == (GET_MODE_UNIT_BITSIZE (result_mode) - 1) + && (GET_MODE_UNIT_BITSIZE (result_mode) + >= GET_MODE_UNIT_BITSIZE (GET_MODE (varop)))) + { + varop = XEXP (varop, 0); + continue; + } + + /* fall through */ + + case LSHIFTRT: + case ASHIFT: + case ROTATE: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_mode = as_a <scalar_int_mode> (mode); + int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop)); + int_result_mode = as_a <scalar_int_mode> (result_mode); + + /* Here we have two nested shifts. The result is usually the + AND of a new shift with a mask. We compute the result below. */ + if (CONST_INT_P (XEXP (varop, 1)) + && INTVAL (XEXP (varop, 1)) >= 0 + && INTVAL (XEXP (varop, 1)) < GET_MODE_PRECISION (int_varop_mode) + && HWI_COMPUTABLE_MODE_P (int_result_mode) + && HWI_COMPUTABLE_MODE_P (int_mode)) + { + enum rtx_code first_code = GET_CODE (varop); + unsigned int first_count = INTVAL (XEXP (varop, 1)); + unsigned HOST_WIDE_INT mask; + rtx mask_rtx; + + /* We have one common special case. We can't do any merging if + the inner code is an ASHIFTRT of a smaller mode. However, if + we have (ashift:M1 (subreg:M1 (ashiftrt:M2 FOO C1) 0) C2) + with C2 == GET_MODE_BITSIZE (M1) - GET_MODE_BITSIZE (M2), + we can convert it to + (ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0) C3) C2) C1). + This simplifies certain SIGN_EXTEND operations. */ + if (code == ASHIFT && first_code == ASHIFTRT + && count == (GET_MODE_PRECISION (int_result_mode) + - GET_MODE_PRECISION (int_varop_mode))) + { + /* C3 has the low-order C1 bits zero. */ + + mask = GET_MODE_MASK (int_mode) + & ~((HOST_WIDE_INT_1U << first_count) - 1); + + varop = simplify_and_const_int (NULL_RTX, int_result_mode, + XEXP (varop, 0), mask); + varop = simplify_shift_const (NULL_RTX, ASHIFT, + int_result_mode, varop, count); + count = first_count; + code = ASHIFTRT; + continue; + } + + /* If this was (ashiftrt (ashift foo C1) C2) and FOO has more + than C1 high-order bits equal to the sign bit, we can convert + this to either an ASHIFT or an ASHIFTRT depending on the + two counts. + + We cannot do this if VAROP's mode is not SHIFT_UNIT_MODE. */ + + if (code == ASHIFTRT && first_code == ASHIFT + && int_varop_mode == shift_unit_mode + && (num_sign_bit_copies (XEXP (varop, 0), shift_unit_mode) + > first_count)) + { + varop = XEXP (varop, 0); + count -= first_count; + if (count < 0) + { + count = -count; + code = ASHIFT; + } + + continue; + } + + /* There are some cases we can't do. If CODE is ASHIFTRT, + we can only do this if FIRST_CODE is also ASHIFTRT. + + We can't do the case when CODE is ROTATE and FIRST_CODE is + ASHIFTRT. + + If the mode of this shift is not the mode of the outer shift, + we can't do this if either shift is a right shift or ROTATE. + + Finally, we can't do any of these if the mode is too wide + unless the codes are the same. + + Handle the case where the shift codes are the same + first. */ + + if (code == first_code) + { + if (int_varop_mode != int_result_mode + && (code == ASHIFTRT || code == LSHIFTRT + || code == ROTATE)) + break; + + count += first_count; + varop = XEXP (varop, 0); + continue; + } + + if (code == ASHIFTRT + || (code == ROTATE && first_code == ASHIFTRT) + || GET_MODE_PRECISION (int_mode) > HOST_BITS_PER_WIDE_INT + || (int_varop_mode != int_result_mode + && (first_code == ASHIFTRT || first_code == LSHIFTRT + || first_code == ROTATE + || code == ROTATE))) + break; + + /* To compute the mask to apply after the shift, shift the + nonzero bits of the inner shift the same way the + outer shift will. */ + + mask_rtx = gen_int_mode (nonzero_bits (varop, int_varop_mode), + int_result_mode); + rtx count_rtx = gen_int_shift_amount (int_result_mode, count); + mask_rtx + = simplify_const_binary_operation (code, int_result_mode, + mask_rtx, count_rtx); + + /* Give up if we can't compute an outer operation to use. */ + if (mask_rtx == 0 + || !CONST_INT_P (mask_rtx) + || ! merge_outer_ops (&outer_op, &outer_const, AND, + INTVAL (mask_rtx), + int_result_mode, &complement_p)) + break; + + /* If the shifts are in the same direction, we add the + counts. Otherwise, we subtract them. */ + if ((code == ASHIFTRT || code == LSHIFTRT) + == (first_code == ASHIFTRT || first_code == LSHIFTRT)) + count += first_count; + else + count -= first_count; + + /* If COUNT is positive, the new shift is usually CODE, + except for the two exceptions below, in which case it is + FIRST_CODE. If the count is negative, FIRST_CODE should + always be used */ + if (count > 0 + && ((first_code == ROTATE && code == ASHIFT) + || (first_code == ASHIFTRT && code == LSHIFTRT))) + code = first_code; + else if (count < 0) + code = first_code, count = -count; + + varop = XEXP (varop, 0); + continue; + } + + /* If we have (A << B << C) for any shift, we can convert this to + (A << C << B). This wins if A is a constant. Only try this if + B is not a constant. */ + + else if (GET_CODE (varop) == code + && CONST_INT_P (XEXP (varop, 0)) + && !CONST_INT_P (XEXP (varop, 1))) + { + /* For ((unsigned) (cstULL >> count)) >> cst2 we have to make + sure the result will be masked. See PR70222. */ + if (code == LSHIFTRT + && int_mode != int_result_mode + && !merge_outer_ops (&outer_op, &outer_const, AND, + GET_MODE_MASK (int_result_mode) + >> orig_count, int_result_mode, + &complement_p)) + break; + /* For ((int) (cstLL >> count)) >> cst2 just give up. Queuing + up outer sign extension (often left and right shift) is + hardly more efficient than the original. See PR70429. + Similarly punt for rotates with different modes. + See PR97386. */ + if ((code == ASHIFTRT || code == ROTATE) + && int_mode != int_result_mode) + break; + + rtx count_rtx = gen_int_shift_amount (int_result_mode, count); + rtx new_rtx = simplify_const_binary_operation (code, int_mode, + XEXP (varop, 0), + count_rtx); + varop = gen_rtx_fmt_ee (code, int_mode, new_rtx, XEXP (varop, 1)); + count = 0; + continue; + } + break; + + case NOT: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + + /* Make this fit the case below. */ + varop = gen_rtx_XOR (mode, XEXP (varop, 0), constm1_rtx); + continue; + + case IOR: + case AND: + case XOR: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop)); + int_result_mode = as_a <scalar_int_mode> (result_mode); + + /* If we have (xshiftrt (ior (plus X (const_int -1)) X) C) + with C the size of VAROP - 1 and the shift is logical if + STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, + we have an (le X 0) operation. If we have an arithmetic shift + and STORE_FLAG_VALUE is 1 or we have a logical shift with + STORE_FLAG_VALUE of -1, we have a (neg (le X 0)) operation. */ + + if (GET_CODE (varop) == IOR && GET_CODE (XEXP (varop, 0)) == PLUS + && XEXP (XEXP (varop, 0), 1) == constm1_rtx + && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && (code == LSHIFTRT || code == ASHIFTRT) + && count == (GET_MODE_PRECISION (int_varop_mode) - 1) + && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) + { + count = 0; + varop = gen_rtx_LE (int_varop_mode, XEXP (varop, 1), + const0_rtx); + + if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) + varop = gen_rtx_NEG (int_varop_mode, varop); + + continue; + } + + /* If we have (shift (logical)), move the logical to the outside + to allow it to possibly combine with another logical and the + shift to combine with another shift. This also canonicalizes to + what a ZERO_EXTRACT looks like. Also, some machines have + (and (shift)) insns. */ + + if (CONST_INT_P (XEXP (varop, 1)) + /* We can't do this if we have (ashiftrt (xor)) and the + constant has its sign bit set in shift_unit_mode with + shift_unit_mode wider than result_mode. */ + && !(code == ASHIFTRT && GET_CODE (varop) == XOR + && int_result_mode != shift_unit_mode + && trunc_int_for_mode (INTVAL (XEXP (varop, 1)), + shift_unit_mode) < 0) + && (new_rtx = simplify_const_binary_operation + (code, int_result_mode, + gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode), + gen_int_shift_amount (int_result_mode, count))) != 0 + && CONST_INT_P (new_rtx) + && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop), + INTVAL (new_rtx), int_result_mode, + &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + + /* If we can't do that, try to simplify the shift in each arm of the + logical expression, make a new logical expression, and apply + the inverse distributive law. This also can't be done for + (ashiftrt (xor)) where we've widened the shift and the constant + changes the sign bit. */ + if (CONST_INT_P (XEXP (varop, 1)) + && !(code == ASHIFTRT && GET_CODE (varop) == XOR + && int_result_mode != shift_unit_mode + && trunc_int_for_mode (INTVAL (XEXP (varop, 1)), + shift_unit_mode) < 0)) + { + rtx lhs = simplify_shift_const (NULL_RTX, code, shift_unit_mode, + XEXP (varop, 0), count); + rtx rhs = simplify_shift_const (NULL_RTX, code, shift_unit_mode, + XEXP (varop, 1), count); + + varop = simplify_gen_binary (GET_CODE (varop), shift_unit_mode, + lhs, rhs); + varop = apply_distributive_law (varop); + + count = 0; + continue; + } + break; + + case EQ: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_result_mode = as_a <scalar_int_mode> (result_mode); + + /* Convert (lshiftrt (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE + says that the sign bit can be tested, FOO has mode MODE, C is + GET_MODE_PRECISION (MODE) - 1, and FOO has only its low-order bit + that may be nonzero. */ + if (code == LSHIFTRT + && XEXP (varop, 1) == const0_rtx + && GET_MODE (XEXP (varop, 0)) == int_result_mode + && count == (GET_MODE_PRECISION (int_result_mode) - 1) + && HWI_COMPUTABLE_MODE_P (int_result_mode) + && STORE_FLAG_VALUE == -1 + && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1 + && merge_outer_ops (&outer_op, &outer_const, XOR, 1, + int_result_mode, &complement_p)) + { + varop = XEXP (varop, 0); + count = 0; + continue; + } + break; + + case NEG: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_result_mode = as_a <scalar_int_mode> (result_mode); + + /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less + than the number of bits in the mode is equivalent to A. */ + if (code == LSHIFTRT + && count == (GET_MODE_PRECISION (int_result_mode) - 1) + && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1) + { + varop = XEXP (varop, 0); + count = 0; + continue; + } + + /* NEG commutes with ASHIFT since it is multiplication. Move the + NEG outside to allow shifts to combine. */ + if (code == ASHIFT + && merge_outer_ops (&outer_op, &outer_const, NEG, 0, + int_result_mode, &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + break; + + case PLUS: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_result_mode = as_a <scalar_int_mode> (result_mode); + + /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C + is one less than the number of bits in the mode is + equivalent to (xor A 1). */ + if (code == LSHIFTRT + && count == (GET_MODE_PRECISION (int_result_mode) - 1) + && XEXP (varop, 1) == constm1_rtx + && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1 + && merge_outer_ops (&outer_op, &outer_const, XOR, 1, + int_result_mode, &complement_p)) + { + count = 0; + varop = XEXP (varop, 0); + continue; + } + + /* If we have (xshiftrt (plus FOO BAR) C), and the only bits + that might be nonzero in BAR are those being shifted out and those + bits are known zero in FOO, we can replace the PLUS with FOO. + Similarly in the other operand order. This code occurs when + we are computing the size of a variable-size array. */ + + if ((code == ASHIFTRT || code == LSHIFTRT) + && count < HOST_BITS_PER_WIDE_INT + && nonzero_bits (XEXP (varop, 1), int_result_mode) >> count == 0 + && (nonzero_bits (XEXP (varop, 1), int_result_mode) + & nonzero_bits (XEXP (varop, 0), int_result_mode)) == 0) + { + varop = XEXP (varop, 0); + continue; + } + else if ((code == ASHIFTRT || code == LSHIFTRT) + && count < HOST_BITS_PER_WIDE_INT + && HWI_COMPUTABLE_MODE_P (int_result_mode) + && (nonzero_bits (XEXP (varop, 0), int_result_mode) + >> count) == 0 + && (nonzero_bits (XEXP (varop, 0), int_result_mode) + & nonzero_bits (XEXP (varop, 1), int_result_mode)) == 0) + { + varop = XEXP (varop, 1); + continue; + } + + /* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */ + if (code == ASHIFT + && CONST_INT_P (XEXP (varop, 1)) + && (new_rtx = simplify_const_binary_operation + (ASHIFT, int_result_mode, + gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode), + gen_int_shift_amount (int_result_mode, count))) != 0 + && CONST_INT_P (new_rtx) + && merge_outer_ops (&outer_op, &outer_const, PLUS, + INTVAL (new_rtx), int_result_mode, + &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + + /* Check for 'PLUS signbit', which is the canonical form of 'XOR + signbit', and attempt to change the PLUS to an XOR and move it to + the outer operation as is done above in the AND/IOR/XOR case + leg for shift(logical). See details in logical handling above + for reasoning in doing so. */ + if (code == LSHIFTRT + && CONST_INT_P (XEXP (varop, 1)) + && mode_signbit_p (int_result_mode, XEXP (varop, 1)) + && (new_rtx = simplify_const_binary_operation + (code, int_result_mode, + gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode), + gen_int_shift_amount (int_result_mode, count))) != 0 + && CONST_INT_P (new_rtx) + && merge_outer_ops (&outer_op, &outer_const, XOR, + INTVAL (new_rtx), int_result_mode, + &complement_p)) + { + varop = XEXP (varop, 0); + continue; + } + + break; + + case MINUS: + /* The following rules apply only to scalars. */ + if (shift_mode != shift_unit_mode) + break; + int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop)); + + /* If we have (xshiftrt (minus (ashiftrt X C)) X) C) + with C the size of VAROP - 1 and the shift is logical if + STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1, + we have a (gt X 0) operation. If the shift is arithmetic with + STORE_FLAG_VALUE of 1 or logical with STORE_FLAG_VALUE == -1, + we have a (neg (gt X 0)) operation. */ + + if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) + && GET_CODE (XEXP (varop, 0)) == ASHIFTRT + && count == (GET_MODE_PRECISION (int_varop_mode) - 1) + && (code == LSHIFTRT || code == ASHIFTRT) + && CONST_INT_P (XEXP (XEXP (varop, 0), 1)) + && INTVAL (XEXP (XEXP (varop, 0), 1)) == count + && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1))) + { + count = 0; + varop = gen_rtx_GT (int_varop_mode, XEXP (varop, 1), + const0_rtx); + + if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT) + varop = gen_rtx_NEG (int_varop_mode, varop); + + continue; + } + break; + + case TRUNCATE: + /* Change (lshiftrt (truncate (lshiftrt))) to (truncate (lshiftrt)) + if the truncate does not affect the value. */ + if (code == LSHIFTRT + && GET_CODE (XEXP (varop, 0)) == LSHIFTRT + && CONST_INT_P (XEXP (XEXP (varop, 0), 1)) + && (INTVAL (XEXP (XEXP (varop, 0), 1)) + >= (GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (varop, 0))) + - GET_MODE_UNIT_PRECISION (GET_MODE (varop))))) + { + rtx varop_inner = XEXP (varop, 0); + int new_count = count + INTVAL (XEXP (varop_inner, 1)); + rtx new_count_rtx = gen_int_shift_amount (GET_MODE (varop_inner), + new_count); + varop_inner = gen_rtx_LSHIFTRT (GET_MODE (varop_inner), + XEXP (varop_inner, 0), + new_count_rtx); + varop = gen_rtx_TRUNCATE (GET_MODE (varop), varop_inner); + count = 0; + continue; + } + break; + + default: + break; + } + + break; + } + + shift_mode = result_mode; + if (shift_mode != mode) + { + /* We only change the modes of scalar shifts. */ + int_mode = as_a <scalar_int_mode> (mode); + int_result_mode = as_a <scalar_int_mode> (result_mode); + shift_mode = try_widen_shift_mode (code, varop, count, int_result_mode, + int_mode, outer_op, outer_const); + } + + /* We have now finished analyzing the shift. The result should be + a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If + OUTER_OP is non-UNKNOWN, it is an operation that needs to be applied + to the result of the shift. OUTER_CONST is the relevant constant, + but we must turn off all bits turned off in the shift. */ + + if (outer_op == UNKNOWN + && orig_code == code && orig_count == count + && varop == orig_varop + && shift_mode == GET_MODE (varop)) + return NULL_RTX; + + /* Make a SUBREG if necessary. If we can't make it, fail. */ + varop = gen_lowpart (shift_mode, varop); + if (varop == NULL_RTX || GET_CODE (varop) == CLOBBER) + return NULL_RTX; + + /* If we have an outer operation and we just made a shift, it is + possible that we could have simplified the shift were it not + for the outer operation. So try to do the simplification + recursively. */ + + if (outer_op != UNKNOWN) + x = simplify_shift_const_1 (code, shift_mode, varop, count); + else + x = NULL_RTX; + + if (x == NULL_RTX) + x = simplify_gen_binary (code, shift_mode, varop, + gen_int_shift_amount (shift_mode, count)); + + /* If we were doing an LSHIFTRT in a wider mode than it was originally, + turn off all the bits that the shift would have turned off. */ + if (orig_code == LSHIFTRT && result_mode != shift_mode) + /* We only change the modes of scalar shifts. */ + x = simplify_and_const_int (NULL_RTX, as_a <scalar_int_mode> (shift_mode), + x, GET_MODE_MASK (result_mode) >> orig_count); + + /* Do the remainder of the processing in RESULT_MODE. */ + x = gen_lowpart_or_truncate (result_mode, x); + + /* If COMPLEMENT_P is set, we have to complement X before doing the outer + operation. */ + if (complement_p) + x = simplify_gen_unary (NOT, result_mode, x, result_mode); + + if (outer_op != UNKNOWN) + { + int_result_mode = as_a <scalar_int_mode> (result_mode); + + if (GET_RTX_CLASS (outer_op) != RTX_UNARY + && GET_MODE_PRECISION (int_result_mode) < HOST_BITS_PER_WIDE_INT) + outer_const = trunc_int_for_mode (outer_const, int_result_mode); + + if (outer_op == AND) + x = simplify_and_const_int (NULL_RTX, int_result_mode, x, outer_const); + else if (outer_op == SET) + { + /* This means that we have determined that the result is + equivalent to a constant. This should be rare. */ + if (!side_effects_p (x)) + x = GEN_INT (outer_const); + } + else if (GET_RTX_CLASS (outer_op) == RTX_UNARY) + x = simplify_gen_unary (outer_op, int_result_mode, x, int_result_mode); + else + x = simplify_gen_binary (outer_op, int_result_mode, x, + GEN_INT (outer_const)); + } + + return x; +} + +/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift. + The result of the shift is RESULT_MODE. If we cannot simplify it, + return X or, if it is NULL, synthesize the expression with + simplify_gen_binary. Otherwise, return a simplified value. + + The shift is normally computed in the widest mode we find in VAROP, as + long as it isn't a different number of words than RESULT_MODE. Exceptions + are ASHIFTRT and ROTATE, which are always done in their original mode. */ + +static rtx +simplify_shift_const (rtx x, enum rtx_code code, machine_mode result_mode, + rtx varop, int count) +{ + rtx tem = simplify_shift_const_1 (code, result_mode, varop, count); + if (tem) + return tem; + + if (!x) + x = simplify_gen_binary (code, GET_MODE (varop), varop, + gen_int_shift_amount (GET_MODE (varop), count)); + if (GET_MODE (x) != result_mode) + x = gen_lowpart (result_mode, x); + return x; +} + + +/* A subroutine of recog_for_combine. See there for arguments and + return value. */ + +static int +recog_for_combine_1 (rtx *pnewpat, rtx_insn *insn, rtx *pnotes) +{ + rtx pat = *pnewpat; + rtx pat_without_clobbers; + int insn_code_number; + int num_clobbers_to_add = 0; + int i; + rtx notes = NULL_RTX; + rtx old_notes, old_pat; + int old_icode; + + /* If PAT is a PARALLEL, check to see if it contains the CLOBBER + we use to indicate that something didn't match. If we find such a + thing, force rejection. */ + if (GET_CODE (pat) == PARALLEL) + for (i = XVECLEN (pat, 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER + && XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx) + return -1; + + old_pat = PATTERN (insn); + old_notes = REG_NOTES (insn); + PATTERN (insn) = pat; + REG_NOTES (insn) = NULL_RTX; + + insn_code_number = recog (pat, insn, &num_clobbers_to_add); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + if (insn_code_number < 0) + fputs ("Failed to match this instruction:\n", dump_file); + else + fputs ("Successfully matched this instruction:\n", dump_file); + print_rtl_single (dump_file, pat); + } + + /* If it isn't, there is the possibility that we previously had an insn + that clobbered some register as a side effect, but the combined + insn doesn't need to do that. So try once more without the clobbers + unless this represents an ASM insn. */ + + if (insn_code_number < 0 && ! check_asm_operands (pat) + && GET_CODE (pat) == PARALLEL) + { + int pos; + + for (pos = 0, i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER) + { + if (i != pos) + SUBST (XVECEXP (pat, 0, pos), XVECEXP (pat, 0, i)); + pos++; + } + + SUBST_INT (XVECLEN (pat, 0), pos); + + if (pos == 1) + pat = XVECEXP (pat, 0, 0); + + PATTERN (insn) = pat; + insn_code_number = recog (pat, insn, &num_clobbers_to_add); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + if (insn_code_number < 0) + fputs ("Failed to match this instruction:\n", dump_file); + else + fputs ("Successfully matched this instruction:\n", dump_file); + print_rtl_single (dump_file, pat); + } + } + + pat_without_clobbers = pat; + + PATTERN (insn) = old_pat; + REG_NOTES (insn) = old_notes; + + /* Recognize all noop sets, these will be killed by followup pass. */ + if (insn_code_number < 0 && GET_CODE (pat) == SET && set_noop_p (pat)) + insn_code_number = NOOP_MOVE_INSN_CODE, num_clobbers_to_add = 0; + + /* If we had any clobbers to add, make a new pattern than contains + them. Then check to make sure that all of them are dead. */ + if (num_clobbers_to_add) + { + rtx newpat = gen_rtx_PARALLEL (VOIDmode, + rtvec_alloc (GET_CODE (pat) == PARALLEL + ? (XVECLEN (pat, 0) + + num_clobbers_to_add) + : num_clobbers_to_add + 1)); + + if (GET_CODE (pat) == PARALLEL) + for (i = 0; i < XVECLEN (pat, 0); i++) + XVECEXP (newpat, 0, i) = XVECEXP (pat, 0, i); + else + XVECEXP (newpat, 0, 0) = pat; + + add_clobbers (newpat, insn_code_number); + + for (i = XVECLEN (newpat, 0) - num_clobbers_to_add; + i < XVECLEN (newpat, 0); i++) + { + if (REG_P (XEXP (XVECEXP (newpat, 0, i), 0)) + && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn)) + return -1; + if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) != SCRATCH) + { + gcc_assert (REG_P (XEXP (XVECEXP (newpat, 0, i), 0))); + notes = alloc_reg_note (REG_UNUSED, + XEXP (XVECEXP (newpat, 0, i), 0), notes); + } + } + pat = newpat; + } + + if (insn_code_number >= 0 + && insn_code_number != NOOP_MOVE_INSN_CODE) + { + old_pat = PATTERN (insn); + old_notes = REG_NOTES (insn); + old_icode = INSN_CODE (insn); + PATTERN (insn) = pat; + REG_NOTES (insn) = notes; + INSN_CODE (insn) = insn_code_number; + + /* Allow targets to reject combined insn. */ + if (!targetm.legitimate_combined_insn (insn)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fputs ("Instruction not appropriate for target.", + dump_file); + + /* Callers expect recog_for_combine to strip + clobbers from the pattern on failure. */ + pat = pat_without_clobbers; + notes = NULL_RTX; + + insn_code_number = -1; + } + + PATTERN (insn) = old_pat; + REG_NOTES (insn) = old_notes; + INSN_CODE (insn) = old_icode; + } + + *pnewpat = pat; + *pnotes = notes; + + return insn_code_number; +} + +/* Change every ZERO_EXTRACT and ZERO_EXTEND of a SUBREG that can be + expressed as an AND and maybe an LSHIFTRT, to that formulation. + Return whether anything was so changed. */ + +static bool +change_zero_ext (rtx pat) +{ + bool changed = false; + rtx *src = &SET_SRC (pat); + + subrtx_ptr_iterator::array_type array; + FOR_EACH_SUBRTX_PTR (iter, array, src, NONCONST) + { + rtx x = **iter; + scalar_int_mode mode, inner_mode; + if (!is_a <scalar_int_mode> (GET_MODE (x), &mode)) + continue; + int size; + + if (GET_CODE (x) == ZERO_EXTRACT + && CONST_INT_P (XEXP (x, 1)) + && CONST_INT_P (XEXP (x, 2)) + && is_a <scalar_int_mode> (GET_MODE (XEXP (x, 0)), &inner_mode) + && GET_MODE_PRECISION (inner_mode) <= GET_MODE_PRECISION (mode)) + { + size = INTVAL (XEXP (x, 1)); + + int start = INTVAL (XEXP (x, 2)); + if (BITS_BIG_ENDIAN) + start = GET_MODE_PRECISION (inner_mode) - size - start; + + if (start != 0) + x = gen_rtx_LSHIFTRT (inner_mode, XEXP (x, 0), + gen_int_shift_amount (inner_mode, start)); + else + x = XEXP (x, 0); + + if (mode != inner_mode) + { + if (REG_P (x) && HARD_REGISTER_P (x) + && !can_change_dest_mode (x, 0, mode)) + continue; + + x = gen_lowpart_SUBREG (mode, x); + } + } + else if (GET_CODE (x) == ZERO_EXTEND + && GET_CODE (XEXP (x, 0)) == SUBREG + && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (XEXP (x, 0)))) + && !paradoxical_subreg_p (XEXP (x, 0)) + && subreg_lowpart_p (XEXP (x, 0))) + { + inner_mode = as_a <scalar_int_mode> (GET_MODE (XEXP (x, 0))); + size = GET_MODE_PRECISION (inner_mode); + x = SUBREG_REG (XEXP (x, 0)); + if (GET_MODE (x) != mode) + { + if (REG_P (x) && HARD_REGISTER_P (x) + && !can_change_dest_mode (x, 0, mode)) + continue; + + x = gen_lowpart_SUBREG (mode, x); + } + } + else if (GET_CODE (x) == ZERO_EXTEND + && REG_P (XEXP (x, 0)) + && HARD_REGISTER_P (XEXP (x, 0)) + && can_change_dest_mode (XEXP (x, 0), 0, mode)) + { + inner_mode = as_a <scalar_int_mode> (GET_MODE (XEXP (x, 0))); + size = GET_MODE_PRECISION (inner_mode); + x = gen_rtx_REG (mode, REGNO (XEXP (x, 0))); + } + else + continue; + + if (!(GET_CODE (x) == LSHIFTRT + && CONST_INT_P (XEXP (x, 1)) + && size + INTVAL (XEXP (x, 1)) == GET_MODE_PRECISION (mode))) + { + wide_int mask = wi::mask (size, false, GET_MODE_PRECISION (mode)); + x = gen_rtx_AND (mode, x, immed_wide_int_const (mask, mode)); + } + + SUBST (**iter, x); + changed = true; + } + + if (changed) + FOR_EACH_SUBRTX_PTR (iter, array, src, NONCONST) + maybe_swap_commutative_operands (**iter); + + rtx *dst = &SET_DEST (pat); + scalar_int_mode mode; + if (GET_CODE (*dst) == ZERO_EXTRACT + && REG_P (XEXP (*dst, 0)) + && is_a <scalar_int_mode> (GET_MODE (XEXP (*dst, 0)), &mode) + && CONST_INT_P (XEXP (*dst, 1)) + && CONST_INT_P (XEXP (*dst, 2))) + { + rtx reg = XEXP (*dst, 0); + int width = INTVAL (XEXP (*dst, 1)); + int offset = INTVAL (XEXP (*dst, 2)); + int reg_width = GET_MODE_PRECISION (mode); + if (BITS_BIG_ENDIAN) + offset = reg_width - width - offset; + + rtx x, y, z, w; + wide_int mask = wi::shifted_mask (offset, width, true, reg_width); + wide_int mask2 = wi::shifted_mask (offset, width, false, reg_width); + x = gen_rtx_AND (mode, reg, immed_wide_int_const (mask, mode)); + if (offset) + y = gen_rtx_ASHIFT (mode, SET_SRC (pat), GEN_INT (offset)); + else + y = SET_SRC (pat); + z = gen_rtx_AND (mode, y, immed_wide_int_const (mask2, mode)); + w = gen_rtx_IOR (mode, x, z); + SUBST (SET_DEST (pat), reg); + SUBST (SET_SRC (pat), w); + + changed = true; + } + + return changed; +} + +/* Like recog, but we receive the address of a pointer to a new pattern. + We try to match the rtx that the pointer points to. + If that fails, we may try to modify or replace the pattern, + storing the replacement into the same pointer object. + + Modifications include deletion or addition of CLOBBERs. If the + instruction will still not match, we change ZERO_EXTEND and ZERO_EXTRACT + to the equivalent AND and perhaps LSHIFTRT patterns, and try with that + (and undo if that fails). + + PNOTES is a pointer to a location where any REG_UNUSED notes added for + the CLOBBERs are placed. + + The value is the final insn code from the pattern ultimately matched, + or -1. */ + +static int +recog_for_combine (rtx *pnewpat, rtx_insn *insn, rtx *pnotes) +{ + rtx pat = *pnewpat; + int insn_code_number = recog_for_combine_1 (pnewpat, insn, pnotes); + if (insn_code_number >= 0 || check_asm_operands (pat)) + return insn_code_number; + + void *marker = get_undo_marker (); + bool changed = false; + + if (GET_CODE (pat) == SET) + { + /* For an unrecognized single set of a constant, try placing it in + the constant pool, if this function already uses one. */ + rtx src = SET_SRC (pat); + if (CONSTANT_P (src) + && !CONST_INT_P (src) + && crtl->uses_const_pool) + { + machine_mode mode = GET_MODE (src); + if (mode == VOIDmode) + mode = GET_MODE (SET_DEST (pat)); + src = force_const_mem (mode, src); + if (src) + { + SUBST (SET_SRC (pat), src); + changed = true; + } + } + else + changed = change_zero_ext (pat); + } + else if (GET_CODE (pat) == PARALLEL) + { + int i; + for (i = 0; i < XVECLEN (pat, 0); i++) + { + rtx set = XVECEXP (pat, 0, i); + if (GET_CODE (set) == SET) + changed |= change_zero_ext (set); + } + } + + if (changed) + { + insn_code_number = recog_for_combine_1 (pnewpat, insn, pnotes); + + if (insn_code_number < 0) + undo_to_marker (marker); + } + + return insn_code_number; +} + +/* Like gen_lowpart_general but for use by combine. In combine it + is not possible to create any new pseudoregs. However, it is + safe to create invalid memory addresses, because combine will + try to recognize them and all they will do is make the combine + attempt fail. + + If for some reason this cannot do its job, an rtx + (clobber (const_int 0)) is returned. + An insn containing that will not be recognized. */ + +static rtx +gen_lowpart_for_combine (machine_mode omode, rtx x) +{ + machine_mode imode = GET_MODE (x); + rtx result; + + if (omode == imode) + return x; + + /* We can only support MODE being wider than a word if X is a + constant integer or has a mode the same size. */ + if (maybe_gt (GET_MODE_SIZE (omode), UNITS_PER_WORD) + && ! (CONST_SCALAR_INT_P (x) + || known_eq (GET_MODE_SIZE (imode), GET_MODE_SIZE (omode)))) + goto fail; + + /* X might be a paradoxical (subreg (mem)). In that case, gen_lowpart + won't know what to do. So we will strip off the SUBREG here and + process normally. */ + if (GET_CODE (x) == SUBREG && MEM_P (SUBREG_REG (x))) + { + x = SUBREG_REG (x); + + /* For use in case we fall down into the address adjustments + further below, we need to adjust the known mode and size of + x; imode and isize, since we just adjusted x. */ + imode = GET_MODE (x); + + if (imode == omode) + return x; + } + + result = gen_lowpart_common (omode, x); + + if (result) + return result; + + if (MEM_P (x)) + { + /* Refuse to work on a volatile memory ref or one with a mode-dependent + address. */ + if (MEM_VOLATILE_P (x) + || mode_dependent_address_p (XEXP (x, 0), MEM_ADDR_SPACE (x))) + goto fail; + + /* If we want to refer to something bigger than the original memref, + generate a paradoxical subreg instead. That will force a reload + of the original memref X. */ + if (paradoxical_subreg_p (omode, imode)) + return gen_rtx_SUBREG (omode, x, 0); + + poly_int64 offset = byte_lowpart_offset (omode, imode); + return adjust_address_nv (x, omode, offset); + } + + /* If X is a comparison operator, rewrite it in a new mode. This + probably won't match, but may allow further simplifications. */ + else if (COMPARISON_P (x) + && SCALAR_INT_MODE_P (imode) + && SCALAR_INT_MODE_P (omode)) + return gen_rtx_fmt_ee (GET_CODE (x), omode, XEXP (x, 0), XEXP (x, 1)); + + /* If we couldn't simplify X any other way, just enclose it in a + SUBREG. Normally, this SUBREG won't match, but some patterns may + include an explicit SUBREG or we may simplify it further in combine. */ + else + { + rtx res; + + if (imode == VOIDmode) + { + imode = int_mode_for_mode (omode).require (); + x = gen_lowpart_common (imode, x); + if (x == NULL) + goto fail; + } + res = lowpart_subreg (omode, x, imode); + if (res) + return res; + } + + fail: + return gen_rtx_CLOBBER (omode, const0_rtx); +} + +/* Try to simplify a comparison between OP0 and a constant OP1, + where CODE is the comparison code that will be tested, into a + (CODE OP0 const0_rtx) form. + + The result is a possibly different comparison code to use. + *POP1 may be updated. */ + +static enum rtx_code +simplify_compare_const (enum rtx_code code, machine_mode mode, + rtx op0, rtx *pop1) +{ + scalar_int_mode int_mode; + HOST_WIDE_INT const_op = INTVAL (*pop1); + + /* Get the constant we are comparing against and turn off all bits + not on in our mode. */ + if (mode != VOIDmode) + const_op = trunc_int_for_mode (const_op, mode); + + /* If we are comparing against a constant power of two and the value + being compared can only have that single bit nonzero (e.g., it was + `and'ed with that bit), we can replace this with a comparison + with zero. */ + if (const_op + && (code == EQ || code == NE || code == GE || code == GEU + || code == LT || code == LTU) + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && pow2p_hwi (const_op & GET_MODE_MASK (int_mode)) + && (nonzero_bits (op0, int_mode) + == (unsigned HOST_WIDE_INT) (const_op & GET_MODE_MASK (int_mode)))) + { + code = (code == EQ || code == GE || code == GEU ? NE : EQ); + const_op = 0; + } + + /* Similarly, if we are comparing a value known to be either -1 or + 0 with -1, change it to the opposite comparison against zero. */ + if (const_op == -1 + && (code == EQ || code == NE || code == GT || code == LE + || code == GEU || code == LTU) + && is_a <scalar_int_mode> (mode, &int_mode) + && num_sign_bit_copies (op0, int_mode) == GET_MODE_PRECISION (int_mode)) + { + code = (code == EQ || code == LE || code == GEU ? NE : EQ); + const_op = 0; + } + + /* Do some canonicalizations based on the comparison code. We prefer + comparisons against zero and then prefer equality comparisons. + If we can reduce the size of a constant, we will do that too. */ + switch (code) + { + case LT: + /* < C is equivalent to <= (C - 1) */ + if (const_op > 0) + { + const_op -= 1; + code = LE; + /* ... fall through to LE case below. */ + gcc_fallthrough (); + } + else + break; + + case LE: + /* <= C is equivalent to < (C + 1); we do this for C < 0 */ + if (const_op < 0) + { + const_op += 1; + code = LT; + } + + /* If we are doing a <= 0 comparison on a value known to have + a zero sign bit, we can replace this with == 0. */ + else if (const_op == 0 + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && (nonzero_bits (op0, int_mode) + & (HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1))) + == 0) + code = EQ; + break; + + case GE: + /* >= C is equivalent to > (C - 1). */ + if (const_op > 0) + { + const_op -= 1; + code = GT; + /* ... fall through to GT below. */ + gcc_fallthrough (); + } + else + break; + + case GT: + /* > C is equivalent to >= (C + 1); we do this for C < 0. */ + if (const_op < 0) + { + const_op += 1; + code = GE; + } + + /* If we are doing a > 0 comparison on a value known to have + a zero sign bit, we can replace this with != 0. */ + else if (const_op == 0 + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && (nonzero_bits (op0, int_mode) + & (HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1))) + == 0) + code = NE; + break; + + case LTU: + /* < C is equivalent to <= (C - 1). */ + if (const_op > 0) + { + const_op -= 1; + code = LEU; + /* ... fall through ... */ + gcc_fallthrough (); + } + /* (unsigned) < 0x80000000 is equivalent to >= 0. */ + else if (is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && ((unsigned HOST_WIDE_INT) const_op + == HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1))) + { + const_op = 0; + code = GE; + break; + } + else + break; + + case LEU: + /* unsigned <= 0 is equivalent to == 0 */ + if (const_op == 0) + code = EQ; + /* (unsigned) <= 0x7fffffff is equivalent to >= 0. */ + else if (is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && ((unsigned HOST_WIDE_INT) const_op + == ((HOST_WIDE_INT_1U + << (GET_MODE_PRECISION (int_mode) - 1)) - 1))) + { + const_op = 0; + code = GE; + } + break; + + case GEU: + /* >= C is equivalent to > (C - 1). */ + if (const_op > 1) + { + const_op -= 1; + code = GTU; + /* ... fall through ... */ + gcc_fallthrough (); + } + + /* (unsigned) >= 0x80000000 is equivalent to < 0. */ + else if (is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && ((unsigned HOST_WIDE_INT) const_op + == HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1))) + { + const_op = 0; + code = LT; + break; + } + else + break; + + case GTU: + /* unsigned > 0 is equivalent to != 0 */ + if (const_op == 0) + code = NE; + /* (unsigned) > 0x7fffffff is equivalent to < 0. */ + else if (is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT + && ((unsigned HOST_WIDE_INT) const_op + == (HOST_WIDE_INT_1U + << (GET_MODE_PRECISION (int_mode) - 1)) - 1)) + { + const_op = 0; + code = LT; + } + break; + + default: + break; + } + + *pop1 = GEN_INT (const_op); + return code; +} + +/* Simplify a comparison between *POP0 and *POP1 where CODE is the + comparison code that will be tested. + + The result is a possibly different comparison code to use. *POP0 and + *POP1 may be updated. + + It is possible that we might detect that a comparison is either always + true or always false. However, we do not perform general constant + folding in combine, so this knowledge isn't useful. Such tautologies + should have been detected earlier. Hence we ignore all such cases. */ + +static enum rtx_code +simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1) +{ + rtx op0 = *pop0; + rtx op1 = *pop1; + rtx tem, tem1; + int i; + scalar_int_mode mode, inner_mode, tmode; + opt_scalar_int_mode tmode_iter; + + /* Try a few ways of applying the same transformation to both operands. */ + while (1) + { + /* The test below this one won't handle SIGN_EXTENDs on these machines, + so check specially. */ + if (!WORD_REGISTER_OPERATIONS + && code != GTU && code != GEU && code != LTU && code != LEU + && GET_CODE (op0) == ASHIFTRT && GET_CODE (op1) == ASHIFTRT + && GET_CODE (XEXP (op0, 0)) == ASHIFT + && GET_CODE (XEXP (op1, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (op0, 0), 0)) == SUBREG + && GET_CODE (XEXP (XEXP (op1, 0), 0)) == SUBREG + && is_a <scalar_int_mode> (GET_MODE (op0), &mode) + && (is_a <scalar_int_mode> + (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0))), &inner_mode)) + && inner_mode == GET_MODE (SUBREG_REG (XEXP (XEXP (op1, 0), 0))) + && CONST_INT_P (XEXP (op0, 1)) + && XEXP (op0, 1) == XEXP (op1, 1) + && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1) + && XEXP (op0, 1) == XEXP (XEXP (op1, 0), 1) + && (INTVAL (XEXP (op0, 1)) + == (GET_MODE_PRECISION (mode) + - GET_MODE_PRECISION (inner_mode)))) + { + op0 = SUBREG_REG (XEXP (XEXP (op0, 0), 0)); + op1 = SUBREG_REG (XEXP (XEXP (op1, 0), 0)); + } + + /* If both operands are the same constant shift, see if we can ignore the + shift. We can if the shift is a rotate or if the bits shifted out of + this shift are known to be zero for both inputs and if the type of + comparison is compatible with the shift. */ + if (GET_CODE (op0) == GET_CODE (op1) + && HWI_COMPUTABLE_MODE_P (GET_MODE (op0)) + && ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ)) + || ((GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFT) + && (code != GT && code != LT && code != GE && code != LE)) + || (GET_CODE (op0) == ASHIFTRT + && (code != GTU && code != LTU + && code != GEU && code != LEU))) + && CONST_INT_P (XEXP (op0, 1)) + && INTVAL (XEXP (op0, 1)) >= 0 + && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT + && XEXP (op0, 1) == XEXP (op1, 1)) + { + machine_mode mode = GET_MODE (op0); + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + int shift_count = INTVAL (XEXP (op0, 1)); + + if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT) + mask &= (mask >> shift_count) << shift_count; + else if (GET_CODE (op0) == ASHIFT) + mask = (mask & (mask << shift_count)) >> shift_count; + + if ((nonzero_bits (XEXP (op0, 0), mode) & ~mask) == 0 + && (nonzero_bits (XEXP (op1, 0), mode) & ~mask) == 0) + op0 = XEXP (op0, 0), op1 = XEXP (op1, 0); + else + break; + } + + /* If both operands are AND's of a paradoxical SUBREG by constant, the + SUBREGs are of the same mode, and, in both cases, the AND would + be redundant if the comparison was done in the narrower mode, + do the comparison in the narrower mode (e.g., we are AND'ing with 1 + and the operand's possibly nonzero bits are 0xffffff01; in that case + if we only care about QImode, we don't need the AND). This case + occurs if the output mode of an scc insn is not SImode and + STORE_FLAG_VALUE == 1 (e.g., the 386). + + Similarly, check for a case where the AND's are ZERO_EXTEND + operations from some narrower mode even though a SUBREG is not + present. */ + + else if (GET_CODE (op0) == AND && GET_CODE (op1) == AND + && CONST_INT_P (XEXP (op0, 1)) + && CONST_INT_P (XEXP (op1, 1))) + { + rtx inner_op0 = XEXP (op0, 0); + rtx inner_op1 = XEXP (op1, 0); + HOST_WIDE_INT c0 = INTVAL (XEXP (op0, 1)); + HOST_WIDE_INT c1 = INTVAL (XEXP (op1, 1)); + int changed = 0; + + if (paradoxical_subreg_p (inner_op0) + && GET_CODE (inner_op1) == SUBREG + && HWI_COMPUTABLE_MODE_P (GET_MODE (SUBREG_REG (inner_op0))) + && (GET_MODE (SUBREG_REG (inner_op0)) + == GET_MODE (SUBREG_REG (inner_op1))) + && ((~c0) & nonzero_bits (SUBREG_REG (inner_op0), + GET_MODE (SUBREG_REG (inner_op0)))) == 0 + && ((~c1) & nonzero_bits (SUBREG_REG (inner_op1), + GET_MODE (SUBREG_REG (inner_op1)))) == 0) + { + op0 = SUBREG_REG (inner_op0); + op1 = SUBREG_REG (inner_op1); + + /* The resulting comparison is always unsigned since we masked + off the original sign bit. */ + code = unsigned_condition (code); + + changed = 1; + } + + else if (c0 == c1) + FOR_EACH_MODE_UNTIL (tmode, + as_a <scalar_int_mode> (GET_MODE (op0))) + if ((unsigned HOST_WIDE_INT) c0 == GET_MODE_MASK (tmode)) + { + op0 = gen_lowpart_or_truncate (tmode, inner_op0); + op1 = gen_lowpart_or_truncate (tmode, inner_op1); + code = unsigned_condition (code); + changed = 1; + break; + } + + if (! changed) + break; + } + + /* If both operands are NOT, we can strip off the outer operation + and adjust the comparison code for swapped operands; similarly for + NEG, except that this must be an equality comparison. */ + else if ((GET_CODE (op0) == NOT && GET_CODE (op1) == NOT) + || (GET_CODE (op0) == NEG && GET_CODE (op1) == NEG + && (code == EQ || code == NE))) + op0 = XEXP (op0, 0), op1 = XEXP (op1, 0), code = swap_condition (code); + + else + break; + } + + /* If the first operand is a constant, swap the operands and adjust the + comparison code appropriately, but don't do this if the second operand + is already a constant integer. */ + if (swap_commutative_operands_p (op0, op1)) + { + std::swap (op0, op1); + code = swap_condition (code); + } + + /* We now enter a loop during which we will try to simplify the comparison. + For the most part, we only are concerned with comparisons with zero, + but some things may really be comparisons with zero but not start + out looking that way. */ + + while (CONST_INT_P (op1)) + { + machine_mode raw_mode = GET_MODE (op0); + scalar_int_mode int_mode; + int equality_comparison_p; + int sign_bit_comparison_p; + int unsigned_comparison_p; + HOST_WIDE_INT const_op; + + /* We only want to handle integral modes. This catches VOIDmode, + CCmode, and the floating-point modes. An exception is that we + can handle VOIDmode if OP0 is a COMPARE or a comparison + operation. */ + + if (GET_MODE_CLASS (raw_mode) != MODE_INT + && ! (raw_mode == VOIDmode + && (GET_CODE (op0) == COMPARE || COMPARISON_P (op0)))) + break; + + /* Try to simplify the compare to constant, possibly changing the + comparison op, and/or changing op1 to zero. */ + code = simplify_compare_const (code, raw_mode, op0, &op1); + const_op = INTVAL (op1); + + /* Compute some predicates to simplify code below. */ + + equality_comparison_p = (code == EQ || code == NE); + sign_bit_comparison_p = ((code == LT || code == GE) && const_op == 0); + unsigned_comparison_p = (code == LTU || code == LEU || code == GTU + || code == GEU); + + /* If this is a sign bit comparison and we can do arithmetic in + MODE, say that we will only be needing the sign bit of OP0. */ + if (sign_bit_comparison_p + && is_a <scalar_int_mode> (raw_mode, &int_mode) + && HWI_COMPUTABLE_MODE_P (int_mode)) + op0 = force_to_mode (op0, int_mode, + HOST_WIDE_INT_1U + << (GET_MODE_PRECISION (int_mode) - 1), + 0); + + if (COMPARISON_P (op0)) + { + /* We can't do anything if OP0 is a condition code value, rather + than an actual data value. */ + if (const_op != 0 + || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC) + break; + + /* Get the two operands being compared. */ + if (GET_CODE (XEXP (op0, 0)) == COMPARE) + tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1); + else + tem = XEXP (op0, 0), tem1 = XEXP (op0, 1); + + /* Check for the cases where we simply want the result of the + earlier test or the opposite of that result. */ + if (code == NE || code == EQ + || (val_signbit_known_set_p (raw_mode, STORE_FLAG_VALUE) + && (code == LT || code == GE))) + { + enum rtx_code new_code; + if (code == LT || code == NE) + new_code = GET_CODE (op0); + else + new_code = reversed_comparison_code (op0, NULL); + + if (new_code != UNKNOWN) + { + code = new_code; + op0 = tem; + op1 = tem1; + continue; + } + } + break; + } + + if (raw_mode == VOIDmode) + break; + scalar_int_mode mode = as_a <scalar_int_mode> (raw_mode); + + /* Now try cases based on the opcode of OP0. If none of the cases + does a "continue", we exit this loop immediately after the + switch. */ + + unsigned int mode_width = GET_MODE_PRECISION (mode); + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + switch (GET_CODE (op0)) + { + case ZERO_EXTRACT: + /* If we are extracting a single bit from a variable position in + a constant that has only a single bit set and are comparing it + with zero, we can convert this into an equality comparison + between the position and the location of the single bit. */ + /* Except we can't if SHIFT_COUNT_TRUNCATED is set, since we might + have already reduced the shift count modulo the word size. */ + if (!SHIFT_COUNT_TRUNCATED + && CONST_INT_P (XEXP (op0, 0)) + && XEXP (op0, 1) == const1_rtx + && equality_comparison_p && const_op == 0 + && (i = exact_log2 (UINTVAL (XEXP (op0, 0)))) >= 0) + { + if (BITS_BIG_ENDIAN) + i = BITS_PER_WORD - 1 - i; + + op0 = XEXP (op0, 2); + op1 = GEN_INT (i); + const_op = i; + + /* Result is nonzero iff shift count is equal to I. */ + code = reverse_condition (code); + continue; + } + + /* fall through */ + + case SIGN_EXTRACT: + tem = expand_compound_operation (op0); + if (tem != op0) + { + op0 = tem; + continue; + } + break; + + case NOT: + /* If testing for equality, we can take the NOT of the constant. */ + if (equality_comparison_p + && (tem = simplify_unary_operation (NOT, mode, op1, mode)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* If just looking at the sign bit, reverse the sense of the + comparison. */ + if (sign_bit_comparison_p) + { + op0 = XEXP (op0, 0); + code = (code == GE ? LT : GE); + continue; + } + break; + + case NEG: + /* If testing for equality, we can take the NEG of the constant. */ + if (equality_comparison_p + && (tem = simplify_unary_operation (NEG, mode, op1, mode)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* The remaining cases only apply to comparisons with zero. */ + if (const_op != 0) + break; + + /* When X is ABS or is known positive, + (neg X) is < 0 if and only if X != 0. */ + + if (sign_bit_comparison_p + && (GET_CODE (XEXP (op0, 0)) == ABS + || (mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (op0, 0), mode) + & (HOST_WIDE_INT_1U << (mode_width - 1))) + == 0))) + { + op0 = XEXP (op0, 0); + code = (code == LT ? NE : EQ); + continue; + } + + /* If we have NEG of something whose two high-order bits are the + same, we know that "(-a) < 0" is equivalent to "a > 0". */ + if (num_sign_bit_copies (op0, mode) >= 2) + { + op0 = XEXP (op0, 0); + code = swap_condition (code); + continue; + } + break; + + case ROTATE: + /* If we are testing equality and our count is a constant, we + can perform the inverse operation on our RHS. */ + if (equality_comparison_p && CONST_INT_P (XEXP (op0, 1)) + && (tem = simplify_binary_operation (ROTATERT, mode, + op1, XEXP (op0, 1))) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* If we are doing a < 0 or >= 0 comparison, it means we are testing + a particular bit. Convert it to an AND of a constant of that + bit. This will be converted into a ZERO_EXTRACT. */ + if (const_op == 0 && sign_bit_comparison_p + && CONST_INT_P (XEXP (op0, 1)) + && mode_width <= HOST_BITS_PER_WIDE_INT + && UINTVAL (XEXP (op0, 1)) < mode_width) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), + (HOST_WIDE_INT_1U + << (mode_width - 1 + - INTVAL (XEXP (op0, 1))))); + code = (code == LT ? NE : EQ); + continue; + } + + /* Fall through. */ + + case ABS: + /* ABS is ignorable inside an equality comparison with zero. */ + if (const_op == 0 && equality_comparison_p) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + case SIGN_EXTEND: + /* Can simplify (compare (zero/sign_extend FOO) CONST) to + (compare FOO CONST) if CONST fits in FOO's mode and we + are either testing inequality or have an unsigned + comparison with ZERO_EXTEND or a signed comparison with + SIGN_EXTEND. But don't do it if we don't have a compare + insn of the given mode, since we'd have to revert it + later on, and then we wouldn't know whether to sign- or + zero-extend. */ + if (is_int_mode (GET_MODE (XEXP (op0, 0)), &mode) + && ! unsigned_comparison_p + && HWI_COMPUTABLE_MODE_P (mode) + && trunc_int_for_mode (const_op, mode) == const_op + && have_insn_for (COMPARE, mode)) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + case SUBREG: + /* Check for the case where we are comparing A - C1 with C2, that is + + (subreg:MODE (plus (A) (-C1))) op (C2) + + with C1 a constant, and try to lift the SUBREG, i.e. to do the + comparison in the wider mode. One of the following two conditions + must be true in order for this to be valid: + + 1. The mode extension results in the same bit pattern being added + on both sides and the comparison is equality or unsigned. As + C2 has been truncated to fit in MODE, the pattern can only be + all 0s or all 1s. + + 2. The mode extension results in the sign bit being copied on + each side. + + The difficulty here is that we have predicates for A but not for + (A - C1) so we need to check that C1 is within proper bounds so + as to perturbate A as little as possible. */ + + if (mode_width <= HOST_BITS_PER_WIDE_INT + && subreg_lowpart_p (op0) + && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op0)), + &inner_mode) + && GET_MODE_PRECISION (inner_mode) > mode_width + && GET_CODE (SUBREG_REG (op0)) == PLUS + && CONST_INT_P (XEXP (SUBREG_REG (op0), 1))) + { + rtx a = XEXP (SUBREG_REG (op0), 0); + HOST_WIDE_INT c1 = -INTVAL (XEXP (SUBREG_REG (op0), 1)); + + if ((c1 > 0 + && (unsigned HOST_WIDE_INT) c1 + < HOST_WIDE_INT_1U << (mode_width - 1) + && (equality_comparison_p || unsigned_comparison_p) + /* (A - C1) zero-extends if it is positive and sign-extends + if it is negative, C2 both zero- and sign-extends. */ + && (((nonzero_bits (a, inner_mode) + & ~GET_MODE_MASK (mode)) == 0 + && const_op >= 0) + /* (A - C1) sign-extends if it is positive and 1-extends + if it is negative, C2 both sign- and 1-extends. */ + || (num_sign_bit_copies (a, inner_mode) + > (unsigned int) (GET_MODE_PRECISION (inner_mode) + - mode_width) + && const_op < 0))) + || ((unsigned HOST_WIDE_INT) c1 + < HOST_WIDE_INT_1U << (mode_width - 2) + /* (A - C1) always sign-extends, like C2. */ + && num_sign_bit_copies (a, inner_mode) + > (unsigned int) (GET_MODE_PRECISION (inner_mode) + - (mode_width - 1)))) + { + op0 = SUBREG_REG (op0); + continue; + } + } + + /* If the inner mode is narrower and we are extracting the low part, + we can treat the SUBREG as if it were a ZERO_EXTEND. */ + if (paradoxical_subreg_p (op0)) + ; + else if (subreg_lowpart_p (op0) + && GET_MODE_CLASS (mode) == MODE_INT + && is_int_mode (GET_MODE (SUBREG_REG (op0)), &inner_mode) + && (code == NE || code == EQ) + && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT + && !paradoxical_subreg_p (op0) + && (nonzero_bits (SUBREG_REG (op0), inner_mode) + & ~GET_MODE_MASK (mode)) == 0) + { + /* Remove outer subregs that don't do anything. */ + tem = gen_lowpart (inner_mode, op1); + + if ((nonzero_bits (tem, inner_mode) + & ~GET_MODE_MASK (mode)) == 0) + { + op0 = SUBREG_REG (op0); + op1 = tem; + continue; + } + break; + } + else + break; + + /* FALLTHROUGH */ + + case ZERO_EXTEND: + if (is_int_mode (GET_MODE (XEXP (op0, 0)), &mode) + && (unsigned_comparison_p || equality_comparison_p) + && HWI_COMPUTABLE_MODE_P (mode) + && (unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (mode) + && const_op >= 0 + && have_insn_for (COMPARE, mode)) + { + op0 = XEXP (op0, 0); + continue; + } + break; + + case PLUS: + /* (eq (plus X A) B) -> (eq X (minus B A)). We can only do + this for equality comparisons due to pathological cases involving + overflows. */ + if (equality_comparison_p + && (tem = simplify_binary_operation (MINUS, mode, + op1, XEXP (op0, 1))) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + /* (plus (abs X) (const_int -1)) is < 0 if and only if X == 0. */ + if (const_op == 0 && XEXP (op0, 1) == constm1_rtx + && GET_CODE (XEXP (op0, 0)) == ABS && sign_bit_comparison_p) + { + op0 = XEXP (XEXP (op0, 0), 0); + code = (code == LT ? EQ : NE); + continue; + } + break; + + case MINUS: + /* We used to optimize signed comparisons against zero, but that + was incorrect. Unsigned comparisons against zero (GTU, LEU) + arrive here as equality comparisons, or (GEU, LTU) are + optimized away. No need to special-case them. */ + + /* (eq (minus A B) C) -> (eq A (plus B C)) or + (eq B (minus A C)), whichever simplifies. We can only do + this for equality comparisons due to pathological cases involving + overflows. */ + if (equality_comparison_p + && (tem = simplify_binary_operation (PLUS, mode, + XEXP (op0, 1), op1)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + + if (equality_comparison_p + && (tem = simplify_binary_operation (MINUS, mode, + XEXP (op0, 0), op1)) != 0) + { + op0 = XEXP (op0, 1); + op1 = tem; + continue; + } + + /* The sign bit of (minus (ashiftrt X C) X), where C is the number + of bits in X minus 1, is one iff X > 0. */ + if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT + && CONST_INT_P (XEXP (XEXP (op0, 0), 1)) + && UINTVAL (XEXP (XEXP (op0, 0), 1)) == mode_width - 1 + && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) + { + op0 = XEXP (op0, 1); + code = (code == GE ? LE : GT); + continue; + } + break; + + case XOR: + /* (eq (xor A B) C) -> (eq A (xor B C)). This is a simplification + if C is zero or B is a constant. */ + if (equality_comparison_p + && (tem = simplify_binary_operation (XOR, mode, + XEXP (op0, 1), op1)) != 0) + { + op0 = XEXP (op0, 0); + op1 = tem; + continue; + } + break; + + + case IOR: + /* The sign bit of (ior (plus X (const_int -1)) X) is nonzero + iff X <= 0. */ + if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == PLUS + && XEXP (XEXP (op0, 0), 1) == constm1_rtx + && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1))) + { + op0 = XEXP (op0, 1); + code = (code == GE ? GT : LE); + continue; + } + break; + + case AND: + /* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1). This + will be converted to a ZERO_EXTRACT later. */ + if (const_op == 0 && equality_comparison_p + && GET_CODE (XEXP (op0, 0)) == ASHIFT + && XEXP (XEXP (op0, 0), 0) == const1_rtx) + { + op0 = gen_rtx_LSHIFTRT (mode, XEXP (op0, 1), + XEXP (XEXP (op0, 0), 1)); + op0 = simplify_and_const_int (NULL_RTX, mode, op0, 1); + continue; + } + + /* If we are comparing (and (lshiftrt X C1) C2) for equality with + zero and X is a comparison and C1 and C2 describe only bits set + in STORE_FLAG_VALUE, we can compare with X. */ + if (const_op == 0 && equality_comparison_p + && mode_width <= HOST_BITS_PER_WIDE_INT + && CONST_INT_P (XEXP (op0, 1)) + && GET_CODE (XEXP (op0, 0)) == LSHIFTRT + && CONST_INT_P (XEXP (XEXP (op0, 0), 1)) + && INTVAL (XEXP (XEXP (op0, 0), 1)) >= 0 + && INTVAL (XEXP (XEXP (op0, 0), 1)) < HOST_BITS_PER_WIDE_INT) + { + mask = ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) + << INTVAL (XEXP (XEXP (op0, 0), 1))); + if ((~STORE_FLAG_VALUE & mask) == 0 + && (COMPARISON_P (XEXP (XEXP (op0, 0), 0)) + || ((tem = get_last_value (XEXP (XEXP (op0, 0), 0))) != 0 + && COMPARISON_P (tem)))) + { + op0 = XEXP (XEXP (op0, 0), 0); + continue; + } + } + + /* If we are doing an equality comparison of an AND of a bit equal + to the sign bit, replace this with a LT or GE comparison of + the underlying value. */ + if (equality_comparison_p + && const_op == 0 + && CONST_INT_P (XEXP (op0, 1)) + && mode_width <= HOST_BITS_PER_WIDE_INT + && ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode)) + == HOST_WIDE_INT_1U << (mode_width - 1))) + { + op0 = XEXP (op0, 0); + code = (code == EQ ? GE : LT); + continue; + } + + /* If this AND operation is really a ZERO_EXTEND from a narrower + mode, the constant fits within that mode, and this is either an + equality or unsigned comparison, try to do this comparison in + the narrower mode. + + Note that in: + + (ne:DI (and:DI (reg:DI 4) (const_int 0xffffffff)) (const_int 0)) + -> (ne:DI (reg:SI 4) (const_int 0)) + + unless TARGET_TRULY_NOOP_TRUNCATION allows it or the register is + known to hold a value of the required mode the + transformation is invalid. */ + if ((equality_comparison_p || unsigned_comparison_p) + && CONST_INT_P (XEXP (op0, 1)) + && (i = exact_log2 ((UINTVAL (XEXP (op0, 1)) + & GET_MODE_MASK (mode)) + + 1)) >= 0 + && const_op >> i == 0 + && int_mode_for_size (i, 1).exists (&tmode)) + { + op0 = gen_lowpart_or_truncate (tmode, XEXP (op0, 0)); + continue; + } + + /* If this is (and:M1 (subreg:M1 X:M2 0) (const_int C1)) where C1 + fits in both M1 and M2 and the SUBREG is either paradoxical + or represents the low part, permute the SUBREG and the AND + and try again. */ + if (GET_CODE (XEXP (op0, 0)) == SUBREG + && CONST_INT_P (XEXP (op0, 1))) + { + unsigned HOST_WIDE_INT c1 = INTVAL (XEXP (op0, 1)); + /* Require an integral mode, to avoid creating something like + (AND:SF ...). */ + if ((is_a <scalar_int_mode> + (GET_MODE (SUBREG_REG (XEXP (op0, 0))), &tmode)) + /* It is unsafe to commute the AND into the SUBREG if the + SUBREG is paradoxical and WORD_REGISTER_OPERATIONS is + not defined. As originally written the upper bits + have a defined value due to the AND operation. + However, if we commute the AND inside the SUBREG then + they no longer have defined values and the meaning of + the code has been changed. + Also C1 should not change value in the smaller mode, + see PR67028 (a positive C1 can become negative in the + smaller mode, so that the AND does no longer mask the + upper bits). */ + && ((WORD_REGISTER_OPERATIONS + && mode_width > GET_MODE_PRECISION (tmode) + && mode_width <= BITS_PER_WORD + && trunc_int_for_mode (c1, tmode) == (HOST_WIDE_INT) c1) + || (mode_width <= GET_MODE_PRECISION (tmode) + && subreg_lowpart_p (XEXP (op0, 0)))) + && mode_width <= HOST_BITS_PER_WIDE_INT + && HWI_COMPUTABLE_MODE_P (tmode) + && (c1 & ~mask) == 0 + && (c1 & ~GET_MODE_MASK (tmode)) == 0 + && c1 != mask + && c1 != GET_MODE_MASK (tmode)) + { + op0 = simplify_gen_binary (AND, tmode, + SUBREG_REG (XEXP (op0, 0)), + gen_int_mode (c1, tmode)); + op0 = gen_lowpart (mode, op0); + continue; + } + } + + /* Convert (ne (and (not X) 1) 0) to (eq (and X 1) 0). */ + if (const_op == 0 && equality_comparison_p + && XEXP (op0, 1) == const1_rtx + && GET_CODE (XEXP (op0, 0)) == NOT) + { + op0 = simplify_and_const_int (NULL_RTX, mode, + XEXP (XEXP (op0, 0), 0), 1); + code = (code == NE ? EQ : NE); + continue; + } + + /* Convert (ne (and (lshiftrt (not X)) 1) 0) to + (eq (and (lshiftrt X) 1) 0). + Also handle the case where (not X) is expressed using xor. */ + if (const_op == 0 && equality_comparison_p + && XEXP (op0, 1) == const1_rtx + && GET_CODE (XEXP (op0, 0)) == LSHIFTRT) + { + rtx shift_op = XEXP (XEXP (op0, 0), 0); + rtx shift_count = XEXP (XEXP (op0, 0), 1); + + if (GET_CODE (shift_op) == NOT + || (GET_CODE (shift_op) == XOR + && CONST_INT_P (XEXP (shift_op, 1)) + && CONST_INT_P (shift_count) + && HWI_COMPUTABLE_MODE_P (mode) + && (UINTVAL (XEXP (shift_op, 1)) + == HOST_WIDE_INT_1U + << INTVAL (shift_count)))) + { + op0 + = gen_rtx_LSHIFTRT (mode, XEXP (shift_op, 0), shift_count); + op0 = simplify_and_const_int (NULL_RTX, mode, op0, 1); + code = (code == NE ? EQ : NE); + continue; + } + } + break; + + case ASHIFT: + /* If we have (compare (ashift FOO N) (const_int C)) and + the high order N bits of FOO (N+1 if an inequality comparison) + are known to be zero, we can do this by comparing FOO with C + shifted right N bits so long as the low-order N bits of C are + zero. */ + if (CONST_INT_P (XEXP (op0, 1)) + && INTVAL (XEXP (op0, 1)) >= 0 + && ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p) + < HOST_BITS_PER_WIDE_INT) + && (((unsigned HOST_WIDE_INT) const_op + & ((HOST_WIDE_INT_1U << INTVAL (XEXP (op0, 1))) + - 1)) == 0) + && mode_width <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (XEXP (op0, 0), mode) + & ~(mask >> (INTVAL (XEXP (op0, 1)) + + ! equality_comparison_p))) == 0) + { + /* We must perform a logical shift, not an arithmetic one, + as we want the top N bits of C to be zero. */ + unsigned HOST_WIDE_INT temp = const_op & GET_MODE_MASK (mode); + + temp >>= INTVAL (XEXP (op0, 1)); + op1 = gen_int_mode (temp, mode); + op0 = XEXP (op0, 0); + continue; + } + + /* If we are doing a sign bit comparison, it means we are testing + a particular bit. Convert it to the appropriate AND. */ + if (sign_bit_comparison_p && CONST_INT_P (XEXP (op0, 1)) + && mode_width <= HOST_BITS_PER_WIDE_INT) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), + (HOST_WIDE_INT_1U + << (mode_width - 1 + - INTVAL (XEXP (op0, 1))))); + code = (code == LT ? NE : EQ); + continue; + } + + /* If this an equality comparison with zero and we are shifting + the low bit to the sign bit, we can convert this to an AND of the + low-order bit. */ + if (const_op == 0 && equality_comparison_p + && CONST_INT_P (XEXP (op0, 1)) + && UINTVAL (XEXP (op0, 1)) == mode_width - 1) + { + op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0), 1); + continue; + } + break; + + case ASHIFTRT: + /* If this is an equality comparison with zero, we can do this + as a logical shift, which might be much simpler. */ + if (equality_comparison_p && const_op == 0 + && CONST_INT_P (XEXP (op0, 1))) + { + op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, + XEXP (op0, 0), + INTVAL (XEXP (op0, 1))); + continue; + } + + /* If OP0 is a sign extension and CODE is not an unsigned comparison, + do the comparison in a narrower mode. */ + if (! unsigned_comparison_p + && CONST_INT_P (XEXP (op0, 1)) + && GET_CODE (XEXP (op0, 0)) == ASHIFT + && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1) + && (int_mode_for_size (mode_width - INTVAL (XEXP (op0, 1)), 1) + .exists (&tmode)) + && (((unsigned HOST_WIDE_INT) const_op + + (GET_MODE_MASK (tmode) >> 1) + 1) + <= GET_MODE_MASK (tmode))) + { + op0 = gen_lowpart (tmode, XEXP (XEXP (op0, 0), 0)); + continue; + } + + /* Likewise if OP0 is a PLUS of a sign extension with a + constant, which is usually represented with the PLUS + between the shifts. */ + if (! unsigned_comparison_p + && CONST_INT_P (XEXP (op0, 1)) + && GET_CODE (XEXP (op0, 0)) == PLUS + && CONST_INT_P (XEXP (XEXP (op0, 0), 1)) + && GET_CODE (XEXP (XEXP (op0, 0), 0)) == ASHIFT + && XEXP (op0, 1) == XEXP (XEXP (XEXP (op0, 0), 0), 1) + && (int_mode_for_size (mode_width - INTVAL (XEXP (op0, 1)), 1) + .exists (&tmode)) + && (((unsigned HOST_WIDE_INT) const_op + + (GET_MODE_MASK (tmode) >> 1) + 1) + <= GET_MODE_MASK (tmode))) + { + rtx inner = XEXP (XEXP (XEXP (op0, 0), 0), 0); + rtx add_const = XEXP (XEXP (op0, 0), 1); + rtx new_const = simplify_gen_binary (ASHIFTRT, mode, + add_const, XEXP (op0, 1)); + + op0 = simplify_gen_binary (PLUS, tmode, + gen_lowpart (tmode, inner), + new_const); + continue; + } + + /* FALLTHROUGH */ + case LSHIFTRT: + /* If we have (compare (xshiftrt FOO N) (const_int C)) and + the low order N bits of FOO are known to be zero, we can do this + by comparing FOO with C shifted left N bits so long as no + overflow occurs. Even if the low order N bits of FOO aren't known + to be zero, if the comparison is >= or < we can use the same + optimization and for > or <= by setting all the low + order N bits in the comparison constant. */ + if (CONST_INT_P (XEXP (op0, 1)) + && INTVAL (XEXP (op0, 1)) > 0 + && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT + && mode_width <= HOST_BITS_PER_WIDE_INT + && (((unsigned HOST_WIDE_INT) const_op + + (GET_CODE (op0) != LSHIFTRT + ? ((GET_MODE_MASK (mode) >> INTVAL (XEXP (op0, 1)) >> 1) + + 1) + : 0)) + <= GET_MODE_MASK (mode) >> INTVAL (XEXP (op0, 1)))) + { + unsigned HOST_WIDE_INT low_bits + = (nonzero_bits (XEXP (op0, 0), mode) + & ((HOST_WIDE_INT_1U + << INTVAL (XEXP (op0, 1))) - 1)); + if (low_bits == 0 || !equality_comparison_p) + { + /* If the shift was logical, then we must make the condition + unsigned. */ + if (GET_CODE (op0) == LSHIFTRT) + code = unsigned_condition (code); + + const_op = (unsigned HOST_WIDE_INT) const_op + << INTVAL (XEXP (op0, 1)); + if (low_bits != 0 + && (code == GT || code == GTU + || code == LE || code == LEU)) + const_op + |= ((HOST_WIDE_INT_1 << INTVAL (XEXP (op0, 1))) - 1); + op1 = GEN_INT (const_op); + op0 = XEXP (op0, 0); + continue; + } + } + + /* If we are using this shift to extract just the sign bit, we + can replace this with an LT or GE comparison. */ + if (const_op == 0 + && (equality_comparison_p || sign_bit_comparison_p) + && CONST_INT_P (XEXP (op0, 1)) + && UINTVAL (XEXP (op0, 1)) == mode_width - 1) + { + op0 = XEXP (op0, 0); + code = (code == NE || code == GT ? LT : GE); + continue; + } + break; + + default: + break; + } + + break; + } + + /* Now make any compound operations involved in this comparison. Then, + check for an outmost SUBREG on OP0 that is not doing anything or is + paradoxical. The latter transformation must only be performed when + it is known that the "extra" bits will be the same in op0 and op1 or + that they don't matter. There are three cases to consider: + + 1. SUBREG_REG (op0) is a register. In this case the bits are don't + care bits and we can assume they have any convenient value. So + making the transformation is safe. + + 2. SUBREG_REG (op0) is a memory and LOAD_EXTEND_OP is UNKNOWN. + In this case the upper bits of op0 are undefined. We should not make + the simplification in that case as we do not know the contents of + those bits. + + 3. SUBREG_REG (op0) is a memory and LOAD_EXTEND_OP is not UNKNOWN. + In that case we know those bits are zeros or ones. We must also be + sure that they are the same as the upper bits of op1. + + We can never remove a SUBREG for a non-equality comparison because + the sign bit is in a different place in the underlying object. */ + + rtx_code op0_mco_code = SET; + if (op1 == const0_rtx) + op0_mco_code = code == NE || code == EQ ? EQ : COMPARE; + + op0 = make_compound_operation (op0, op0_mco_code); + op1 = make_compound_operation (op1, SET); + + if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0) + && is_int_mode (GET_MODE (op0), &mode) + && is_int_mode (GET_MODE (SUBREG_REG (op0)), &inner_mode) + && (code == NE || code == EQ)) + { + if (paradoxical_subreg_p (op0)) + { + /* For paradoxical subregs, allow case 1 as above. Case 3 isn't + implemented. */ + if (REG_P (SUBREG_REG (op0))) + { + op0 = SUBREG_REG (op0); + op1 = gen_lowpart (inner_mode, op1); + } + } + else if (GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (SUBREG_REG (op0), inner_mode) + & ~GET_MODE_MASK (mode)) == 0) + { + tem = gen_lowpart (inner_mode, op1); + + if ((nonzero_bits (tem, inner_mode) & ~GET_MODE_MASK (mode)) == 0) + op0 = SUBREG_REG (op0), op1 = tem; + } + } + + /* We now do the opposite procedure: Some machines don't have compare + insns in all modes. If OP0's mode is an integer mode smaller than a + word and we can't do a compare in that mode, see if there is a larger + mode for which we can do the compare. There are a number of cases in + which we can use the wider mode. */ + + if (is_int_mode (GET_MODE (op0), &mode) + && GET_MODE_SIZE (mode) < UNITS_PER_WORD + && ! have_insn_for (COMPARE, mode)) + FOR_EACH_WIDER_MODE (tmode_iter, mode) + { + tmode = tmode_iter.require (); + if (!HWI_COMPUTABLE_MODE_P (tmode)) + break; + if (have_insn_for (COMPARE, tmode)) + { + int zero_extended; + + /* If this is a test for negative, we can make an explicit + test of the sign bit. Test this first so we can use + a paradoxical subreg to extend OP0. */ + + if (op1 == const0_rtx && (code == LT || code == GE) + && HWI_COMPUTABLE_MODE_P (mode)) + { + unsigned HOST_WIDE_INT sign + = HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (mode) - 1); + op0 = simplify_gen_binary (AND, tmode, + gen_lowpart (tmode, op0), + gen_int_mode (sign, tmode)); + code = (code == LT) ? NE : EQ; + break; + } + + /* If the only nonzero bits in OP0 and OP1 are those in the + narrower mode and this is an equality or unsigned comparison, + we can use the wider mode. Similarly for sign-extended + values, in which case it is true for all comparisons. */ + zero_extended = ((code == EQ || code == NE + || code == GEU || code == GTU + || code == LEU || code == LTU) + && (nonzero_bits (op0, tmode) + & ~GET_MODE_MASK (mode)) == 0 + && ((CONST_INT_P (op1) + || (nonzero_bits (op1, tmode) + & ~GET_MODE_MASK (mode)) == 0))); + + if (zero_extended + || ((num_sign_bit_copies (op0, tmode) + > (unsigned int) (GET_MODE_PRECISION (tmode) + - GET_MODE_PRECISION (mode))) + && (num_sign_bit_copies (op1, tmode) + > (unsigned int) (GET_MODE_PRECISION (tmode) + - GET_MODE_PRECISION (mode))))) + { + /* If OP0 is an AND and we don't have an AND in MODE either, + make a new AND in the proper mode. */ + if (GET_CODE (op0) == AND + && !have_insn_for (AND, mode)) + op0 = simplify_gen_binary (AND, tmode, + gen_lowpart (tmode, + XEXP (op0, 0)), + gen_lowpart (tmode, + XEXP (op0, 1))); + else + { + if (zero_extended) + { + op0 = simplify_gen_unary (ZERO_EXTEND, tmode, + op0, mode); + op1 = simplify_gen_unary (ZERO_EXTEND, tmode, + op1, mode); + } + else + { + op0 = simplify_gen_unary (SIGN_EXTEND, tmode, + op0, mode); + op1 = simplify_gen_unary (SIGN_EXTEND, tmode, + op1, mode); + } + break; + } + } + } + } + + /* We may have changed the comparison operands. Re-canonicalize. */ + if (swap_commutative_operands_p (op0, op1)) + { + std::swap (op0, op1); + code = swap_condition (code); + } + + /* If this machine only supports a subset of valid comparisons, see if we + can convert an unsupported one into a supported one. */ + target_canonicalize_comparison (&code, &op0, &op1, 0); + + *pop0 = op0; + *pop1 = op1; + + return code; +} + +/* Utility function for record_value_for_reg. Count number of + rtxs in X. */ +static int +count_rtxs (rtx x) +{ + enum rtx_code code = GET_CODE (x); + const char *fmt; + int i, j, ret = 1; + + if (GET_RTX_CLASS (code) == RTX_BIN_ARITH + || GET_RTX_CLASS (code) == RTX_COMM_ARITH) + { + rtx x0 = XEXP (x, 0); + rtx x1 = XEXP (x, 1); + + if (x0 == x1) + return 1 + 2 * count_rtxs (x0); + + if ((GET_RTX_CLASS (GET_CODE (x1)) == RTX_BIN_ARITH + || GET_RTX_CLASS (GET_CODE (x1)) == RTX_COMM_ARITH) + && (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1))) + return 2 + 2 * count_rtxs (x0) + + count_rtxs (x == XEXP (x1, 0) + ? XEXP (x1, 1) : XEXP (x1, 0)); + + if ((GET_RTX_CLASS (GET_CODE (x0)) == RTX_BIN_ARITH + || GET_RTX_CLASS (GET_CODE (x0)) == RTX_COMM_ARITH) + && (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1))) + return 2 + 2 * count_rtxs (x1) + + count_rtxs (x == XEXP (x0, 0) + ? XEXP (x0, 1) : XEXP (x0, 0)); + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + ret += count_rtxs (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + ret += count_rtxs (XVECEXP (x, i, j)); + + return ret; +} + +/* Utility function for following routine. Called when X is part of a value + being stored into last_set_value. Sets last_set_table_tick + for each register mentioned. Similar to mention_regs in cse.c */ + +static void +update_table_tick (rtx x) +{ + enum rtx_code code = GET_CODE (x); + const char *fmt = GET_RTX_FORMAT (code); + int i, j; + + if (code == REG) + { + unsigned int regno = REGNO (x); + unsigned int endregno = END_REGNO (x); + unsigned int r; + + for (r = regno; r < endregno; r++) + { + reg_stat_type *rsp = ®_stat[r]; + rsp->last_set_table_tick = label_tick; + } + + return; + } + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + /* Check for identical subexpressions. If x contains + identical subexpression we only have to traverse one of + them. */ + if (i == 0 && ARITHMETIC_P (x)) + { + /* Note that at this point x1 has already been + processed. */ + rtx x0 = XEXP (x, 0); + rtx x1 = XEXP (x, 1); + + /* If x0 and x1 are identical then there is no need to + process x0. */ + if (x0 == x1) + break; + + /* If x0 is identical to a subexpression of x1 then while + processing x1, x0 has already been processed. Thus we + are done with x. */ + if (ARITHMETIC_P (x1) + && (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1))) + break; + + /* If x1 is identical to a subexpression of x0 then we + still have to process the rest of x0. */ + if (ARITHMETIC_P (x0) + && (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1))) + { + update_table_tick (XEXP (x0, x1 == XEXP (x0, 0) ? 1 : 0)); + break; + } + } + + update_table_tick (XEXP (x, i)); + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + update_table_tick (XVECEXP (x, i, j)); +} + +/* Record that REG is set to VALUE in insn INSN. If VALUE is zero, we + are saying that the register is clobbered and we no longer know its + value. If INSN is zero, don't update reg_stat[].last_set; this is + only permitted with VALUE also zero and is used to invalidate the + register. */ + +static void +record_value_for_reg (rtx reg, rtx_insn *insn, rtx value) +{ + unsigned int regno = REGNO (reg); + unsigned int endregno = END_REGNO (reg); + unsigned int i; + reg_stat_type *rsp; + + /* If VALUE contains REG and we have a previous value for REG, substitute + the previous value. */ + if (value && insn && reg_overlap_mentioned_p (reg, value)) + { + rtx tem; + + /* Set things up so get_last_value is allowed to see anything set up to + our insn. */ + subst_low_luid = DF_INSN_LUID (insn); + tem = get_last_value (reg); + + /* If TEM is simply a binary operation with two CLOBBERs as operands, + it isn't going to be useful and will take a lot of time to process, + so just use the CLOBBER. */ + + if (tem) + { + if (ARITHMETIC_P (tem) + && GET_CODE (XEXP (tem, 0)) == CLOBBER + && GET_CODE (XEXP (tem, 1)) == CLOBBER) + tem = XEXP (tem, 0); + else if (count_occurrences (value, reg, 1) >= 2) + { + /* If there are two or more occurrences of REG in VALUE, + prevent the value from growing too much. */ + if (count_rtxs (tem) > param_max_last_value_rtl) + tem = gen_rtx_CLOBBER (GET_MODE (tem), const0_rtx); + } + + value = replace_rtx (copy_rtx (value), reg, tem); + } + } + + /* For each register modified, show we don't know its value, that + we don't know about its bitwise content, that its value has been + updated, and that we don't know the location of the death of the + register. */ + for (i = regno; i < endregno; i++) + { + rsp = ®_stat[i]; + + if (insn) + rsp->last_set = insn; + + rsp->last_set_value = 0; + rsp->last_set_mode = VOIDmode; + rsp->last_set_nonzero_bits = 0; + rsp->last_set_sign_bit_copies = 0; + rsp->last_death = 0; + rsp->truncated_to_mode = VOIDmode; + } + + /* Mark registers that are being referenced in this value. */ + if (value) + update_table_tick (value); + + /* Now update the status of each register being set. + If someone is using this register in this block, set this register + to invalid since we will get confused between the two lives in this + basic block. This makes using this register always invalid. In cse, we + scan the table to invalidate all entries using this register, but this + is too much work for us. */ + + for (i = regno; i < endregno; i++) + { + rsp = ®_stat[i]; + rsp->last_set_label = label_tick; + if (!insn + || (value && rsp->last_set_table_tick >= label_tick_ebb_start)) + rsp->last_set_invalid = 1; + else + rsp->last_set_invalid = 0; + } + + /* The value being assigned might refer to X (like in "x++;"). In that + case, we must replace it with (clobber (const_int 0)) to prevent + infinite loops. */ + rsp = ®_stat[regno]; + if (value && !get_last_value_validate (&value, insn, label_tick, 0)) + { + value = copy_rtx (value); + if (!get_last_value_validate (&value, insn, label_tick, 1)) + value = 0; + } + + /* For the main register being modified, update the value, the mode, the + nonzero bits, and the number of sign bit copies. */ + + rsp->last_set_value = value; + + if (value) + { + machine_mode mode = GET_MODE (reg); + subst_low_luid = DF_INSN_LUID (insn); + rsp->last_set_mode = mode; + if (GET_MODE_CLASS (mode) == MODE_INT + && HWI_COMPUTABLE_MODE_P (mode)) + mode = nonzero_bits_mode; + rsp->last_set_nonzero_bits = nonzero_bits (value, mode); + rsp->last_set_sign_bit_copies + = num_sign_bit_copies (value, GET_MODE (reg)); + } +} + +/* Called via note_stores from record_dead_and_set_regs to handle one + SET or CLOBBER in an insn. DATA is the instruction in which the + set is occurring. */ + +static void +record_dead_and_set_regs_1 (rtx dest, const_rtx setter, void *data) +{ + rtx_insn *record_dead_insn = (rtx_insn *) data; + + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + + if (!record_dead_insn) + { + if (REG_P (dest)) + record_value_for_reg (dest, NULL, NULL_RTX); + return; + } + + if (REG_P (dest)) + { + /* If we are setting the whole register, we know its value. Otherwise + show that we don't know the value. We can handle a SUBREG if it's + the low part, but we must be careful with paradoxical SUBREGs on + RISC architectures because we cannot strip e.g. an extension around + a load and record the naked load since the RTL middle-end considers + that the upper bits are defined according to LOAD_EXTEND_OP. */ + if (GET_CODE (setter) == SET && dest == SET_DEST (setter)) + record_value_for_reg (dest, record_dead_insn, SET_SRC (setter)); + else if (GET_CODE (setter) == SET + && GET_CODE (SET_DEST (setter)) == SUBREG + && SUBREG_REG (SET_DEST (setter)) == dest + && known_le (GET_MODE_PRECISION (GET_MODE (dest)), + BITS_PER_WORD) + && subreg_lowpart_p (SET_DEST (setter))) + record_value_for_reg (dest, record_dead_insn, + WORD_REGISTER_OPERATIONS + && word_register_operation_p (SET_SRC (setter)) + && paradoxical_subreg_p (SET_DEST (setter)) + ? SET_SRC (setter) + : gen_lowpart (GET_MODE (dest), + SET_SRC (setter))); + else + record_value_for_reg (dest, record_dead_insn, NULL_RTX); + } + else if (MEM_P (dest) + /* Ignore pushes, they clobber nothing. */ + && ! push_operand (dest, GET_MODE (dest))) + mem_last_set = DF_INSN_LUID (record_dead_insn); +} + +/* Update the records of when each REG was most recently set or killed + for the things done by INSN. This is the last thing done in processing + INSN in the combiner loop. + + We update reg_stat[], in particular fields last_set, last_set_value, + last_set_mode, last_set_nonzero_bits, last_set_sign_bit_copies, + last_death, and also the similar information mem_last_set (which insn + most recently modified memory) and last_call_luid (which insn was the + most recent subroutine call). */ + +static void +record_dead_and_set_regs (rtx_insn *insn) +{ + rtx link; + unsigned int i; + + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + { + if (REG_NOTE_KIND (link) == REG_DEAD + && REG_P (XEXP (link, 0))) + { + unsigned int regno = REGNO (XEXP (link, 0)); + unsigned int endregno = END_REGNO (XEXP (link, 0)); + + for (i = regno; i < endregno; i++) + { + reg_stat_type *rsp; + + rsp = ®_stat[i]; + rsp->last_death = insn; + } + } + else if (REG_NOTE_KIND (link) == REG_INC) + record_value_for_reg (XEXP (link, 0), insn, NULL_RTX); + } + + if (CALL_P (insn)) + { + HARD_REG_SET callee_clobbers + = insn_callee_abi (insn).full_and_partial_reg_clobbers (); + hard_reg_set_iterator hrsi; + EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, i, hrsi) + { + reg_stat_type *rsp; + + /* ??? We could try to preserve some information from the last + set of register I if the call doesn't actually clobber + (reg:last_set_mode I), which might be true for ABIs with + partial clobbers. However, it would be difficult to + update last_set_nonzero_bits and last_sign_bit_copies + to account for the part of I that actually was clobbered. + It wouldn't help much anyway, since we rarely see this + situation before RA. */ + rsp = ®_stat[i]; + rsp->last_set_invalid = 1; + rsp->last_set = insn; + rsp->last_set_value = 0; + rsp->last_set_mode = VOIDmode; + rsp->last_set_nonzero_bits = 0; + rsp->last_set_sign_bit_copies = 0; + rsp->last_death = 0; + rsp->truncated_to_mode = VOIDmode; + } + + last_call_luid = mem_last_set = DF_INSN_LUID (insn); + + /* We can't combine into a call pattern. Remember, though, that + the return value register is set at this LUID. We could + still replace a register with the return value from the + wrong subroutine call! */ + note_stores (insn, record_dead_and_set_regs_1, NULL_RTX); + } + else + note_stores (insn, record_dead_and_set_regs_1, insn); +} + +/* If a SUBREG has the promoted bit set, it is in fact a property of the + register present in the SUBREG, so for each such SUBREG go back and + adjust nonzero and sign bit information of the registers that are + known to have some zero/sign bits set. + + This is needed because when combine blows the SUBREGs away, the + information on zero/sign bits is lost and further combines can be + missed because of that. */ + +static void +record_promoted_value (rtx_insn *insn, rtx subreg) +{ + struct insn_link *links; + rtx set; + unsigned int regno = REGNO (SUBREG_REG (subreg)); + machine_mode mode = GET_MODE (subreg); + + if (!HWI_COMPUTABLE_MODE_P (mode)) + return; + + for (links = LOG_LINKS (insn); links;) + { + reg_stat_type *rsp; + + insn = links->insn; + set = single_set (insn); + + if (! set || !REG_P (SET_DEST (set)) + || REGNO (SET_DEST (set)) != regno + || GET_MODE (SET_DEST (set)) != GET_MODE (SUBREG_REG (subreg))) + { + links = links->next; + continue; + } + + rsp = ®_stat[regno]; + if (rsp->last_set == insn) + { + if (SUBREG_PROMOTED_UNSIGNED_P (subreg)) + rsp->last_set_nonzero_bits &= GET_MODE_MASK (mode); + } + + if (REG_P (SET_SRC (set))) + { + regno = REGNO (SET_SRC (set)); + links = LOG_LINKS (insn); + } + else + break; + } +} + +/* Check if X, a register, is known to contain a value already + truncated to MODE. In this case we can use a subreg to refer to + the truncated value even though in the generic case we would need + an explicit truncation. */ + +static bool +reg_truncated_to_mode (machine_mode mode, const_rtx x) +{ + reg_stat_type *rsp = ®_stat[REGNO (x)]; + machine_mode truncated = rsp->truncated_to_mode; + + if (truncated == 0 + || rsp->truncation_label < label_tick_ebb_start) + return false; + if (!partial_subreg_p (mode, truncated)) + return true; + if (TRULY_NOOP_TRUNCATION_MODES_P (mode, truncated)) + return true; + return false; +} + +/* If X is a hard reg or a subreg record the mode that the register is + accessed in. For non-TARGET_TRULY_NOOP_TRUNCATION targets we might be + able to turn a truncate into a subreg using this information. Return true + if traversing X is complete. */ + +static bool +record_truncated_value (rtx x) +{ + machine_mode truncated_mode; + reg_stat_type *rsp; + + if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x))) + { + machine_mode original_mode = GET_MODE (SUBREG_REG (x)); + truncated_mode = GET_MODE (x); + + if (!partial_subreg_p (truncated_mode, original_mode)) + return true; + + truncated_mode = GET_MODE (x); + if (TRULY_NOOP_TRUNCATION_MODES_P (truncated_mode, original_mode)) + return true; + + x = SUBREG_REG (x); + } + /* ??? For hard-regs we now record everything. We might be able to + optimize this using last_set_mode. */ + else if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) + truncated_mode = GET_MODE (x); + else + return false; + + rsp = ®_stat[REGNO (x)]; + if (rsp->truncated_to_mode == 0 + || rsp->truncation_label < label_tick_ebb_start + || partial_subreg_p (truncated_mode, rsp->truncated_to_mode)) + { + rsp->truncated_to_mode = truncated_mode; + rsp->truncation_label = label_tick; + } + + return true; +} + +/* Callback for note_uses. Find hardregs and subregs of pseudos and + the modes they are used in. This can help truning TRUNCATEs into + SUBREGs. */ + +static void +record_truncated_values (rtx *loc, void *data ATTRIBUTE_UNUSED) +{ + subrtx_var_iterator::array_type array; + FOR_EACH_SUBRTX_VAR (iter, array, *loc, NONCONST) + if (record_truncated_value (*iter)) + iter.skip_subrtxes (); +} + +/* Scan X for promoted SUBREGs. For each one found, + note what it implies to the registers used in it. */ + +static void +check_promoted_subreg (rtx_insn *insn, rtx x) +{ + if (GET_CODE (x) == SUBREG + && SUBREG_PROMOTED_VAR_P (x) + && REG_P (SUBREG_REG (x))) + record_promoted_value (insn, x); + else + { + const char *format = GET_RTX_FORMAT (GET_CODE (x)); + int i, j; + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++) + switch (format[i]) + { + case 'e': + check_promoted_subreg (insn, XEXP (x, i)); + break; + case 'V': + case 'E': + if (XVEC (x, i) != 0) + for (j = 0; j < XVECLEN (x, i); j++) + check_promoted_subreg (insn, XVECEXP (x, i, j)); + break; + } + } +} + +/* Verify that all the registers and memory references mentioned in *LOC are + still valid. *LOC was part of a value set in INSN when label_tick was + equal to TICK. Return 0 if some are not. If REPLACE is nonzero, replace + the invalid references with (clobber (const_int 0)) and return 1. This + replacement is useful because we often can get useful information about + the form of a value (e.g., if it was produced by a shift that always + produces -1 or 0) even though we don't know exactly what registers it + was produced from. */ + +static int +get_last_value_validate (rtx *loc, rtx_insn *insn, int tick, int replace) +{ + rtx x = *loc; + const char *fmt = GET_RTX_FORMAT (GET_CODE (x)); + int len = GET_RTX_LENGTH (GET_CODE (x)); + int i, j; + + if (REG_P (x)) + { + unsigned int regno = REGNO (x); + unsigned int endregno = END_REGNO (x); + unsigned int j; + + for (j = regno; j < endregno; j++) + { + reg_stat_type *rsp = ®_stat[j]; + if (rsp->last_set_invalid + /* If this is a pseudo-register that was only set once and not + live at the beginning of the function, it is always valid. */ + || (! (regno >= FIRST_PSEUDO_REGISTER + && regno < reg_n_sets_max + && REG_N_SETS (regno) == 1 + && (!REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), + regno))) + && rsp->last_set_label > tick)) + { + if (replace) + *loc = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); + return replace; + } + } + + return 1; + } + /* If this is a memory reference, make sure that there were no stores after + it that might have clobbered the value. We don't have alias info, so we + assume any store invalidates it. Moreover, we only have local UIDs, so + we also assume that there were stores in the intervening basic blocks. */ + else if (MEM_P (x) && !MEM_READONLY_P (x) + && (tick != label_tick || DF_INSN_LUID (insn) <= mem_last_set)) + { + if (replace) + *loc = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); + return replace; + } + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'e') + { + /* Check for identical subexpressions. If x contains + identical subexpression we only have to traverse one of + them. */ + if (i == 1 && ARITHMETIC_P (x)) + { + /* Note that at this point x0 has already been checked + and found valid. */ + rtx x0 = XEXP (x, 0); + rtx x1 = XEXP (x, 1); + + /* If x0 and x1 are identical then x is also valid. */ + if (x0 == x1) + return 1; + + /* If x1 is identical to a subexpression of x0 then + while checking x0, x1 has already been checked. Thus + it is valid and so as x. */ + if (ARITHMETIC_P (x0) + && (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1))) + return 1; + + /* If x0 is identical to a subexpression of x1 then x is + valid iff the rest of x1 is valid. */ + if (ARITHMETIC_P (x1) + && (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1))) + return + get_last_value_validate (&XEXP (x1, + x0 == XEXP (x1, 0) ? 1 : 0), + insn, tick, replace); + } + + if (get_last_value_validate (&XEXP (x, i), insn, tick, + replace) == 0) + return 0; + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + if (get_last_value_validate (&XVECEXP (x, i, j), + insn, tick, replace) == 0) + return 0; + } + + /* If we haven't found a reason for it to be invalid, it is valid. */ + return 1; +} + +/* Get the last value assigned to X, if known. Some registers + in the value may be replaced with (clobber (const_int 0)) if their value + is known longer known reliably. */ + +static rtx +get_last_value (const_rtx x) +{ + unsigned int regno; + rtx value; + reg_stat_type *rsp; + + /* If this is a non-paradoxical SUBREG, get the value of its operand and + then convert it to the desired mode. If this is a paradoxical SUBREG, + we cannot predict what values the "extra" bits might have. */ + if (GET_CODE (x) == SUBREG + && subreg_lowpart_p (x) + && !paradoxical_subreg_p (x) + && (value = get_last_value (SUBREG_REG (x))) != 0) + return gen_lowpart (GET_MODE (x), value); + + if (!REG_P (x)) + return 0; + + regno = REGNO (x); + rsp = ®_stat[regno]; + value = rsp->last_set_value; + + /* If we don't have a value, or if it isn't for this basic block and + it's either a hard register, set more than once, or it's a live + at the beginning of the function, return 0. + + Because if it's not live at the beginning of the function then the reg + is always set before being used (is never used without being set). + And, if it's set only once, and it's always set before use, then all + uses must have the same last value, even if it's not from this basic + block. */ + + if (value == 0 + || (rsp->last_set_label < label_tick_ebb_start + && (regno < FIRST_PSEUDO_REGISTER + || regno >= reg_n_sets_max + || REG_N_SETS (regno) != 1 + || REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), regno)))) + return 0; + + /* If the value was set in a later insn than the ones we are processing, + we can't use it even if the register was only set once. */ + if (rsp->last_set_label == label_tick + && DF_INSN_LUID (rsp->last_set) >= subst_low_luid) + return 0; + + /* If fewer bits were set than what we are asked for now, we cannot use + the value. */ + if (maybe_lt (GET_MODE_PRECISION (rsp->last_set_mode), + GET_MODE_PRECISION (GET_MODE (x)))) + return 0; + + /* If the value has all its registers valid, return it. */ + if (get_last_value_validate (&value, rsp->last_set, rsp->last_set_label, 0)) + return value; + + /* Otherwise, make a copy and replace any invalid register with + (clobber (const_int 0)). If that fails for some reason, return 0. */ + + value = copy_rtx (value); + if (get_last_value_validate (&value, rsp->last_set, rsp->last_set_label, 1)) + return value; + + return 0; +} + +/* Define three variables used for communication between the following + routines. */ + +static unsigned int reg_dead_regno, reg_dead_endregno; +static int reg_dead_flag; +rtx reg_dead_reg; + +/* Function called via note_stores from reg_dead_at_p. + + If DEST is within [reg_dead_regno, reg_dead_endregno), set + reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET. */ + +static void +reg_dead_at_p_1 (rtx dest, const_rtx x, void *data ATTRIBUTE_UNUSED) +{ + unsigned int regno, endregno; + + if (!REG_P (dest)) + return; + + regno = REGNO (dest); + endregno = END_REGNO (dest); + if (reg_dead_endregno > regno && reg_dead_regno < endregno) + reg_dead_flag = (GET_CODE (x) == CLOBBER) ? 1 : -1; +} + +/* Return nonzero if REG is known to be dead at INSN. + + We scan backwards from INSN. If we hit a REG_DEAD note or a CLOBBER + referencing REG, it is dead. If we hit a SET referencing REG, it is + live. Otherwise, see if it is live or dead at the start of the basic + block we are in. Hard regs marked as being live in NEWPAT_USED_REGS + must be assumed to be always live. */ + +static int +reg_dead_at_p (rtx reg, rtx_insn *insn) +{ + basic_block block; + unsigned int i; + + /* Set variables for reg_dead_at_p_1. */ + reg_dead_regno = REGNO (reg); + reg_dead_endregno = END_REGNO (reg); + reg_dead_reg = reg; + + reg_dead_flag = 0; + + /* Check that reg isn't mentioned in NEWPAT_USED_REGS. For fixed registers + we allow the machine description to decide whether use-and-clobber + patterns are OK. */ + if (reg_dead_regno < FIRST_PSEUDO_REGISTER) + { + for (i = reg_dead_regno; i < reg_dead_endregno; i++) + if (!fixed_regs[i] && TEST_HARD_REG_BIT (newpat_used_regs, i)) + return 0; + } + + /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, or + beginning of basic block. */ + block = BLOCK_FOR_INSN (insn); + for (;;) + { + if (INSN_P (insn)) + { + if (find_regno_note (insn, REG_UNUSED, reg_dead_regno)) + return 1; + + note_stores (insn, reg_dead_at_p_1, NULL); + if (reg_dead_flag) + return reg_dead_flag == 1 ? 1 : 0; + + if (find_regno_note (insn, REG_DEAD, reg_dead_regno)) + return 1; + } + + if (insn == BB_HEAD (block)) + break; + + insn = PREV_INSN (insn); + } + + /* Look at live-in sets for the basic block that we were in. */ + for (i = reg_dead_regno; i < reg_dead_endregno; i++) + if (REGNO_REG_SET_P (df_get_live_in (block), i)) + return 0; + + return 1; +} + +/* Note hard registers in X that are used. */ + +static void +mark_used_regs_combine (rtx x) +{ + RTX_CODE code = GET_CODE (x); + unsigned int regno; + int i; + + switch (code) + { + case LABEL_REF: + case SYMBOL_REF: + case CONST: + CASE_CONST_ANY: + case PC: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case ASM_INPUT: + return; + + case CLOBBER: + /* If we are clobbering a MEM, mark any hard registers inside the + address as used. */ + if (MEM_P (XEXP (x, 0))) + mark_used_regs_combine (XEXP (XEXP (x, 0), 0)); + return; + + case REG: + regno = REGNO (x); + /* A hard reg in a wide mode may really be multiple registers. + If so, mark all of them just like the first. */ + if (regno < FIRST_PSEUDO_REGISTER) + { + /* None of this applies to the stack, frame or arg pointers. */ + if (regno == STACK_POINTER_REGNUM + || (!HARD_FRAME_POINTER_IS_FRAME_POINTER + && regno == HARD_FRAME_POINTER_REGNUM) + || (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && regno == ARG_POINTER_REGNUM && fixed_regs[regno]) + || regno == FRAME_POINTER_REGNUM) + return; + + add_to_hard_reg_set (&newpat_used_regs, GET_MODE (x), regno); + } + return; + + case SET: + { + /* If setting a MEM, or a SUBREG of a MEM, then note any hard regs in + the address. */ + rtx testreg = SET_DEST (x); + + while (GET_CODE (testreg) == SUBREG + || GET_CODE (testreg) == ZERO_EXTRACT + || GET_CODE (testreg) == STRICT_LOW_PART) + testreg = XEXP (testreg, 0); + + if (MEM_P (testreg)) + mark_used_regs_combine (XEXP (testreg, 0)); + + mark_used_regs_combine (SET_SRC (x)); + } + return; + + default: + break; + } + + /* Recursively scan the operands of this expression. */ + + { + const char *fmt = GET_RTX_FORMAT (code); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + mark_used_regs_combine (XEXP (x, i)); + else if (fmt[i] == 'E') + { + int j; + + for (j = 0; j < XVECLEN (x, i); j++) + mark_used_regs_combine (XVECEXP (x, i, j)); + } + } + } +} + +/* Remove register number REGNO from the dead registers list of INSN. + + Return the note used to record the death, if there was one. */ + +rtx +remove_death (unsigned int regno, rtx_insn *insn) +{ + rtx note = find_regno_note (insn, REG_DEAD, regno); + + if (note) + remove_note (insn, note); + + return note; +} + +/* For each register (hardware or pseudo) used within expression X, if its + death is in an instruction with luid between FROM_LUID (inclusive) and + TO_INSN (exclusive), put a REG_DEAD note for that register in the + list headed by PNOTES. + + That said, don't move registers killed by maybe_kill_insn. + + This is done when X is being merged by combination into TO_INSN. These + notes will then be distributed as needed. */ + +static void +move_deaths (rtx x, rtx maybe_kill_insn, int from_luid, rtx_insn *to_insn, + rtx *pnotes) +{ + const char *fmt; + int len, i; + enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + unsigned int regno = REGNO (x); + rtx_insn *where_dead = reg_stat[regno].last_death; + + /* If we do not know where the register died, it may still die between + FROM_LUID and TO_INSN. If so, find it. This is PR83304. */ + if (!where_dead || DF_INSN_LUID (where_dead) >= DF_INSN_LUID (to_insn)) + { + rtx_insn *insn = prev_real_nondebug_insn (to_insn); + while (insn + && BLOCK_FOR_INSN (insn) == BLOCK_FOR_INSN (to_insn) + && DF_INSN_LUID (insn) >= from_luid) + { + if (dead_or_set_regno_p (insn, regno)) + { + if (find_regno_note (insn, REG_DEAD, regno)) + where_dead = insn; + break; + } + + insn = prev_real_nondebug_insn (insn); + } + } + + /* Don't move the register if it gets killed in between from and to. */ + if (maybe_kill_insn && reg_set_p (x, maybe_kill_insn) + && ! reg_referenced_p (x, maybe_kill_insn)) + return; + + if (where_dead + && BLOCK_FOR_INSN (where_dead) == BLOCK_FOR_INSN (to_insn) + && DF_INSN_LUID (where_dead) >= from_luid + && DF_INSN_LUID (where_dead) < DF_INSN_LUID (to_insn)) + { + rtx note = remove_death (regno, where_dead); + + /* It is possible for the call above to return 0. This can occur + when last_death points to I2 or I1 that we combined with. + In that case make a new note. + + We must also check for the case where X is a hard register + and NOTE is a death note for a range of hard registers + including X. In that case, we must put REG_DEAD notes for + the remaining registers in place of NOTE. */ + + if (note != 0 && regno < FIRST_PSEUDO_REGISTER + && partial_subreg_p (GET_MODE (x), GET_MODE (XEXP (note, 0)))) + { + unsigned int deadregno = REGNO (XEXP (note, 0)); + unsigned int deadend = END_REGNO (XEXP (note, 0)); + unsigned int ourend = END_REGNO (x); + unsigned int i; + + for (i = deadregno; i < deadend; i++) + if (i < regno || i >= ourend) + add_reg_note (where_dead, REG_DEAD, regno_reg_rtx[i]); + } + + /* If we didn't find any note, or if we found a REG_DEAD note that + covers only part of the given reg, and we have a multi-reg hard + register, then to be safe we must check for REG_DEAD notes + for each register other than the first. They could have + their own REG_DEAD notes lying around. */ + else if ((note == 0 + || (note != 0 + && partial_subreg_p (GET_MODE (XEXP (note, 0)), + GET_MODE (x)))) + && regno < FIRST_PSEUDO_REGISTER + && REG_NREGS (x) > 1) + { + unsigned int ourend = END_REGNO (x); + unsigned int i, offset; + rtx oldnotes = 0; + + if (note) + offset = hard_regno_nregs (regno, GET_MODE (XEXP (note, 0))); + else + offset = 1; + + for (i = regno + offset; i < ourend; i++) + move_deaths (regno_reg_rtx[i], + maybe_kill_insn, from_luid, to_insn, &oldnotes); + } + + if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x)) + { + XEXP (note, 1) = *pnotes; + *pnotes = note; + } + else + *pnotes = alloc_reg_note (REG_DEAD, x, *pnotes); + } + + return; + } + + else if (GET_CODE (x) == SET) + { + rtx dest = SET_DEST (x); + + move_deaths (SET_SRC (x), maybe_kill_insn, from_luid, to_insn, pnotes); + + /* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG + that accesses one word of a multi-word item, some + piece of everything register in the expression is used by + this insn, so remove any old death. */ + /* ??? So why do we test for equality of the sizes? */ + + if (GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART + || (GET_CODE (dest) == SUBREG + && !read_modify_subreg_p (dest))) + { + move_deaths (dest, maybe_kill_insn, from_luid, to_insn, pnotes); + return; + } + + /* If this is some other SUBREG, we know it replaces the entire + value, so use that as the destination. */ + if (GET_CODE (dest) == SUBREG) + dest = SUBREG_REG (dest); + + /* If this is a MEM, adjust deaths of anything used in the address. + For a REG (the only other possibility), the entire value is + being replaced so the old value is not used in this insn. */ + + if (MEM_P (dest)) + move_deaths (XEXP (dest, 0), maybe_kill_insn, from_luid, + to_insn, pnotes); + return; + } + + else if (GET_CODE (x) == CLOBBER) + return; + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + move_deaths (XVECEXP (x, i, j), maybe_kill_insn, from_luid, + to_insn, pnotes); + } + else if (fmt[i] == 'e') + move_deaths (XEXP (x, i), maybe_kill_insn, from_luid, to_insn, pnotes); + } +} + +/* Return 1 if X is the target of a bit-field assignment in BODY, the + pattern of an insn. X must be a REG. */ + +static int +reg_bitfield_target_p (rtx x, rtx body) +{ + int i; + + if (GET_CODE (body) == SET) + { + rtx dest = SET_DEST (body); + rtx target; + unsigned int regno, tregno, endregno, endtregno; + + if (GET_CODE (dest) == ZERO_EXTRACT) + target = XEXP (dest, 0); + else if (GET_CODE (dest) == STRICT_LOW_PART) + target = SUBREG_REG (XEXP (dest, 0)); + else + return 0; + + if (GET_CODE (target) == SUBREG) + target = SUBREG_REG (target); + + if (!REG_P (target)) + return 0; + + tregno = REGNO (target), regno = REGNO (x); + if (tregno >= FIRST_PSEUDO_REGISTER || regno >= FIRST_PSEUDO_REGISTER) + return target == x; + + endtregno = end_hard_regno (GET_MODE (target), tregno); + endregno = end_hard_regno (GET_MODE (x), regno); + + return endregno > tregno && regno < endtregno; + } + + else if (GET_CODE (body) == PARALLEL) + for (i = XVECLEN (body, 0) - 1; i >= 0; i--) + if (reg_bitfield_target_p (x, XVECEXP (body, 0, i))) + return 1; + + return 0; +} + +/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them + as appropriate. I3 and I2 are the insns resulting from the combination + insns including FROM (I2 may be zero). + + ELIM_I2 and ELIM_I1 are either zero or registers that we know will + not need REG_DEAD notes because they are being substituted for. This + saves searching in the most common cases. + + Each note in the list is either ignored or placed on some insns, depending + on the type of note. */ + +static void +distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, + rtx elim_i2, rtx elim_i1, rtx elim_i0) +{ + rtx note, next_note; + rtx tem_note; + rtx_insn *tem_insn; + + for (note = notes; note; note = next_note) + { + rtx_insn *place = 0, *place2 = 0; + + next_note = XEXP (note, 1); + switch (REG_NOTE_KIND (note)) + { + case REG_BR_PROB: + case REG_BR_PRED: + /* Doesn't matter much where we put this, as long as it's somewhere. + It is preferable to keep these notes on branches, which is most + likely to be i3. */ + place = i3; + break; + + case REG_NON_LOCAL_GOTO: + if (JUMP_P (i3)) + place = i3; + else + { + gcc_assert (i2 && JUMP_P (i2)); + place = i2; + } + break; + + case REG_EH_REGION: + /* These notes must remain with the call or trapping instruction. */ + if (CALL_P (i3)) + place = i3; + else if (i2 && CALL_P (i2)) + place = i2; + else + { + gcc_assert (cfun->can_throw_non_call_exceptions); + if (may_trap_p (i3)) + place = i3; + else if (i2 && may_trap_p (i2)) + place = i2; + /* ??? Otherwise assume we've combined things such that we + can now prove that the instructions can't trap. Drop the + note in this case. */ + } + break; + + case REG_ARGS_SIZE: + /* ??? How to distribute between i3-i1. Assume i3 contains the + entire adjustment. Assert i3 contains at least some adjust. */ + if (!noop_move_p (i3)) + { + poly_int64 old_size, args_size = get_args_size (note); + /* fixup_args_size_notes looks at REG_NORETURN note, + so ensure the note is placed there first. */ + if (CALL_P (i3)) + { + rtx *np; + for (np = &next_note; *np; np = &XEXP (*np, 1)) + if (REG_NOTE_KIND (*np) == REG_NORETURN) + { + rtx n = *np; + *np = XEXP (n, 1); + XEXP (n, 1) = REG_NOTES (i3); + REG_NOTES (i3) = n; + break; + } + } + old_size = fixup_args_size_notes (PREV_INSN (i3), i3, args_size); + /* emit_call_1 adds for !ACCUMULATE_OUTGOING_ARGS + REG_ARGS_SIZE note to all noreturn calls, allow that here. */ + gcc_assert (maybe_ne (old_size, args_size) + || (CALL_P (i3) + && !ACCUMULATE_OUTGOING_ARGS + && find_reg_note (i3, REG_NORETURN, NULL_RTX))); + } + break; + + case REG_NORETURN: + case REG_SETJMP: + case REG_TM: + case REG_CALL_DECL: + case REG_UNTYPED_CALL: + case REG_CALL_NOCF_CHECK: + /* These notes must remain with the call. It should not be + possible for both I2 and I3 to be a call. */ + if (CALL_P (i3)) + place = i3; + else + { + gcc_assert (i2 && CALL_P (i2)); + place = i2; + } + break; + + case REG_UNUSED: + /* Any clobbers for i3 may still exist, and so we must process + REG_UNUSED notes from that insn. + + Any clobbers from i2 or i1 can only exist if they were added by + recog_for_combine. In that case, recog_for_combine created the + necessary REG_UNUSED notes. Trying to keep any original + REG_UNUSED notes from these insns can cause incorrect output + if it is for the same register as the original i3 dest. + In that case, we will notice that the register is set in i3, + and then add a REG_UNUSED note for the destination of i3, which + is wrong. However, it is possible to have REG_UNUSED notes from + i2 or i1 for register which were both used and clobbered, so + we keep notes from i2 or i1 if they will turn into REG_DEAD + notes. */ + + /* If this register is set or clobbered between FROM_INSN and I3, + we should not create a note for it. */ + if (reg_set_between_p (XEXP (note, 0), from_insn, i3)) + break; + + /* If this register is set or clobbered in I3, put the note there + unless there is one already. */ + if (reg_set_p (XEXP (note, 0), PATTERN (i3))) + { + if (from_insn != i3) + break; + + if (! (REG_P (XEXP (note, 0)) + ? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0))) + : find_reg_note (i3, REG_UNUSED, XEXP (note, 0)))) + place = i3; + } + /* Otherwise, if this register is used by I3, then this register + now dies here, so we must put a REG_DEAD note here unless there + is one already. */ + else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)) + && ! (REG_P (XEXP (note, 0)) + ? find_regno_note (i3, REG_DEAD, + REGNO (XEXP (note, 0))) + : find_reg_note (i3, REG_DEAD, XEXP (note, 0)))) + { + PUT_REG_NOTE_KIND (note, REG_DEAD); + place = i3; + } + + /* A SET or CLOBBER of the REG_UNUSED reg has been removed, + but we can't tell which at this point. We must reset any + expectations we had about the value that was previously + stored in the reg. ??? Ideally, we'd adjust REG_N_SETS + and, if appropriate, restore its previous value, but we + don't have enough information for that at this point. */ + else + { + record_value_for_reg (XEXP (note, 0), NULL, NULL_RTX); + + /* Otherwise, if this register is now referenced in i2 + then the register used to be modified in one of the + original insns. If it was i3 (say, in an unused + parallel), it's now completely gone, so the note can + be discarded. But if it was modified in i2, i1 or i0 + and we still reference it in i2, then we're + referencing the previous value, and since the + register was modified and REG_UNUSED, we know that + the previous value is now dead. So, if we only + reference the register in i2, we change the note to + REG_DEAD, to reflect the previous value. However, if + we're also setting or clobbering the register as + scratch, we know (because the register was not + referenced in i3) that it's unused, just as it was + unused before, and we place the note in i2. */ + if (from_insn != i3 && i2 && INSN_P (i2) + && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) + { + if (!reg_set_p (XEXP (note, 0), PATTERN (i2))) + PUT_REG_NOTE_KIND (note, REG_DEAD); + if (! (REG_P (XEXP (note, 0)) + ? find_regno_note (i2, REG_NOTE_KIND (note), + REGNO (XEXP (note, 0))) + : find_reg_note (i2, REG_NOTE_KIND (note), + XEXP (note, 0)))) + place = i2; + } + } + + break; + + case REG_EQUAL: + case REG_EQUIV: + case REG_NOALIAS: + /* These notes say something about results of an insn. We can + only support them if they used to be on I3 in which case they + remain on I3. Otherwise they are ignored. + + If the note refers to an expression that is not a constant, we + must also ignore the note since we cannot tell whether the + equivalence is still true. It might be possible to do + slightly better than this (we only have a problem if I2DEST + or I1DEST is present in the expression), but it doesn't + seem worth the trouble. */ + + if (from_insn == i3 + && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0)))) + place = i3; + break; + + case REG_INC: + /* These notes say something about how a register is used. They must + be present on any use of the register in I2 or I3. */ + if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3))) + place = i3; + + if (i2 && reg_mentioned_p (XEXP (note, 0), PATTERN (i2))) + { + if (place) + place2 = i2; + else + place = i2; + } + break; + + case REG_LABEL_TARGET: + case REG_LABEL_OPERAND: + /* This can show up in several ways -- either directly in the + pattern, or hidden off in the constant pool with (or without?) + a REG_EQUAL note. */ + /* ??? Ignore the without-reg_equal-note problem for now. */ + if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3)) + || ((tem_note = find_reg_note (i3, REG_EQUAL, NULL_RTX)) + && GET_CODE (XEXP (tem_note, 0)) == LABEL_REF + && label_ref_label (XEXP (tem_note, 0)) == XEXP (note, 0))) + place = i3; + + if (i2 + && (reg_mentioned_p (XEXP (note, 0), PATTERN (i2)) + || ((tem_note = find_reg_note (i2, REG_EQUAL, NULL_RTX)) + && GET_CODE (XEXP (tem_note, 0)) == LABEL_REF + && label_ref_label (XEXP (tem_note, 0)) == XEXP (note, 0)))) + { + if (place) + place2 = i2; + else + place = i2; + } + + /* For REG_LABEL_TARGET on a JUMP_P, we prefer to put the note + as a JUMP_LABEL or decrement LABEL_NUSES if it's already + there. */ + if (place && JUMP_P (place) + && REG_NOTE_KIND (note) == REG_LABEL_TARGET + && (JUMP_LABEL (place) == NULL + || JUMP_LABEL (place) == XEXP (note, 0))) + { + rtx label = JUMP_LABEL (place); + + if (!label) + JUMP_LABEL (place) = XEXP (note, 0); + else if (LABEL_P (label)) + LABEL_NUSES (label)--; + } + + if (place2 && JUMP_P (place2) + && REG_NOTE_KIND (note) == REG_LABEL_TARGET + && (JUMP_LABEL (place2) == NULL + || JUMP_LABEL (place2) == XEXP (note, 0))) + { + rtx label = JUMP_LABEL (place2); + + if (!label) + JUMP_LABEL (place2) = XEXP (note, 0); + else if (LABEL_P (label)) + LABEL_NUSES (label)--; + place2 = 0; + } + break; + + case REG_NONNEG: + /* This note says something about the value of a register prior + to the execution of an insn. It is too much trouble to see + if the note is still correct in all situations. It is better + to simply delete it. */ + break; + + case REG_DEAD: + /* If we replaced the right hand side of FROM_INSN with a + REG_EQUAL note, the original use of the dying register + will not have been combined into I3 and I2. In such cases, + FROM_INSN is guaranteed to be the first of the combined + instructions, so we simply need to search back before + FROM_INSN for the previous use or set of this register, + then alter the notes there appropriately. + + If the register is used as an input in I3, it dies there. + Similarly for I2, if it is nonzero and adjacent to I3. + + If the register is not used as an input in either I3 or I2 + and it is not one of the registers we were supposed to eliminate, + there are two possibilities. We might have a non-adjacent I2 + or we might have somehow eliminated an additional register + from a computation. For example, we might have had A & B where + we discover that B will always be zero. In this case we will + eliminate the reference to A. + + In both cases, we must search to see if we can find a previous + use of A and put the death note there. */ + + if (from_insn + && from_insn == i2mod + && !reg_overlap_mentioned_p (XEXP (note, 0), i2mod_new_rhs)) + tem_insn = from_insn; + else + { + if (from_insn + && CALL_P (from_insn) + && find_reg_fusage (from_insn, USE, XEXP (note, 0))) + place = from_insn; + else if (i2 && reg_set_p (XEXP (note, 0), PATTERN (i2))) + { + /* If the new I2 sets the same register that is marked + dead in the note, we do not in general know where to + put the note. One important case we _can_ handle is + when the note comes from I3. */ + if (from_insn == i3) + place = i3; + else + break; + } + else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))) + place = i3; + else if (i2 != 0 && next_nonnote_nondebug_insn (i2) == i3 + && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) + place = i2; + else if ((rtx_equal_p (XEXP (note, 0), elim_i2) + && !(i2mod + && reg_overlap_mentioned_p (XEXP (note, 0), + i2mod_old_rhs))) + || rtx_equal_p (XEXP (note, 0), elim_i1) + || rtx_equal_p (XEXP (note, 0), elim_i0)) + break; + tem_insn = i3; + } + + if (place == 0) + { + basic_block bb = this_basic_block; + + for (tem_insn = PREV_INSN (tem_insn); place == 0; tem_insn = PREV_INSN (tem_insn)) + { + if (!NONDEBUG_INSN_P (tem_insn)) + { + if (tem_insn == BB_HEAD (bb)) + break; + continue; + } + + /* If the register is being set at TEM_INSN, see if that is all + TEM_INSN is doing. If so, delete TEM_INSN. Otherwise, make this + into a REG_UNUSED note instead. Don't delete sets to + global register vars. */ + if ((REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER + || !global_regs[REGNO (XEXP (note, 0))]) + && reg_set_p (XEXP (note, 0), PATTERN (tem_insn))) + { + rtx set = single_set (tem_insn); + rtx inner_dest = 0; + + if (set != 0) + for (inner_dest = SET_DEST (set); + (GET_CODE (inner_dest) == STRICT_LOW_PART + || GET_CODE (inner_dest) == SUBREG + || GET_CODE (inner_dest) == ZERO_EXTRACT); + inner_dest = XEXP (inner_dest, 0)) + ; + + /* Verify that it was the set, and not a clobber that + modified the register. + + If we cannot delete the setter due to side + effects, mark the user with an UNUSED note instead + of deleting it. */ + + if (set != 0 && ! side_effects_p (SET_SRC (set)) + && rtx_equal_p (XEXP (note, 0), inner_dest)) + { + /* Move the notes and links of TEM_INSN elsewhere. + This might delete other dead insns recursively. + First set the pattern to something that won't use + any register. */ + rtx old_notes = REG_NOTES (tem_insn); + + PATTERN (tem_insn) = pc_rtx; + REG_NOTES (tem_insn) = NULL; + + distribute_notes (old_notes, tem_insn, tem_insn, NULL, + NULL_RTX, NULL_RTX, NULL_RTX); + distribute_links (LOG_LINKS (tem_insn)); + + unsigned int regno = REGNO (XEXP (note, 0)); + reg_stat_type *rsp = ®_stat[regno]; + if (rsp->last_set == tem_insn) + record_value_for_reg (XEXP (note, 0), NULL, NULL_RTX); + + SET_INSN_DELETED (tem_insn); + if (tem_insn == i2) + i2 = NULL; + } + else + { + PUT_REG_NOTE_KIND (note, REG_UNUSED); + + /* If there isn't already a REG_UNUSED note, put one + here. Do not place a REG_DEAD note, even if + the register is also used here; that would not + match the algorithm used in lifetime analysis + and can cause the consistency check in the + scheduler to fail. */ + if (! find_regno_note (tem_insn, REG_UNUSED, + REGNO (XEXP (note, 0)))) + place = tem_insn; + break; + } + } + else if (reg_referenced_p (XEXP (note, 0), PATTERN (tem_insn)) + || (CALL_P (tem_insn) + && find_reg_fusage (tem_insn, USE, XEXP (note, 0)))) + { + place = tem_insn; + + /* If we are doing a 3->2 combination, and we have a + register which formerly died in i3 and was not used + by i2, which now no longer dies in i3 and is used in + i2 but does not die in i2, and place is between i2 + and i3, then we may need to move a link from place to + i2. */ + if (i2 && DF_INSN_LUID (place) > DF_INSN_LUID (i2) + && from_insn + && DF_INSN_LUID (from_insn) > DF_INSN_LUID (i2) + && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) + { + struct insn_link *links = LOG_LINKS (place); + LOG_LINKS (place) = NULL; + distribute_links (links); + } + break; + } + + if (tem_insn == BB_HEAD (bb)) + break; + } + + } + + /* If the register is set or already dead at PLACE, we needn't do + anything with this note if it is still a REG_DEAD note. + We check here if it is set at all, not if is it totally replaced, + which is what `dead_or_set_p' checks, so also check for it being + set partially. */ + + if (place && REG_NOTE_KIND (note) == REG_DEAD) + { + unsigned int regno = REGNO (XEXP (note, 0)); + reg_stat_type *rsp = ®_stat[regno]; + + if (dead_or_set_p (place, XEXP (note, 0)) + || reg_bitfield_target_p (XEXP (note, 0), PATTERN (place))) + { + /* Unless the register previously died in PLACE, clear + last_death. [I no longer understand why this is + being done.] */ + if (rsp->last_death != place) + rsp->last_death = 0; + place = 0; + } + else + rsp->last_death = place; + + /* If this is a death note for a hard reg that is occupying + multiple registers, ensure that we are still using all + parts of the object. If we find a piece of the object + that is unused, we must arrange for an appropriate REG_DEAD + note to be added for it. However, we can't just emit a USE + and tag the note to it, since the register might actually + be dead; so we recourse, and the recursive call then finds + the previous insn that used this register. */ + + if (place && REG_NREGS (XEXP (note, 0)) > 1) + { + unsigned int endregno = END_REGNO (XEXP (note, 0)); + bool all_used = true; + unsigned int i; + + for (i = regno; i < endregno; i++) + if ((! refers_to_regno_p (i, PATTERN (place)) + && ! find_regno_fusage (place, USE, i)) + || dead_or_set_regno_p (place, i)) + { + all_used = false; + break; + } + + if (! all_used) + { + /* Put only REG_DEAD notes for pieces that are + not already dead or set. */ + + for (i = regno; i < endregno; + i += hard_regno_nregs (i, reg_raw_mode[i])) + { + rtx piece = regno_reg_rtx[i]; + basic_block bb = this_basic_block; + + if (! dead_or_set_p (place, piece) + && ! reg_bitfield_target_p (piece, + PATTERN (place))) + { + rtx new_note = alloc_reg_note (REG_DEAD, piece, + NULL_RTX); + + distribute_notes (new_note, place, place, + NULL, NULL_RTX, NULL_RTX, + NULL_RTX); + } + else if (! refers_to_regno_p (i, PATTERN (place)) + && ! find_regno_fusage (place, USE, i)) + for (tem_insn = PREV_INSN (place); ; + tem_insn = PREV_INSN (tem_insn)) + { + if (!NONDEBUG_INSN_P (tem_insn)) + { + if (tem_insn == BB_HEAD (bb)) + break; + continue; + } + if (dead_or_set_p (tem_insn, piece) + || reg_bitfield_target_p (piece, + PATTERN (tem_insn))) + { + add_reg_note (tem_insn, REG_UNUSED, piece); + break; + } + } + } + + place = 0; + } + } + } + break; + + default: + /* Any other notes should not be present at this point in the + compilation. */ + gcc_unreachable (); + } + + if (place) + { + XEXP (note, 1) = REG_NOTES (place); + REG_NOTES (place) = note; + + /* Set added_notes_insn to the earliest insn we added a note to. */ + if (added_notes_insn == 0 + || DF_INSN_LUID (added_notes_insn) > DF_INSN_LUID (place)) + added_notes_insn = place; + } + + if (place2) + { + add_shallow_copy_of_reg_note (place2, note); + + /* Set added_notes_insn to the earliest insn we added a note to. */ + if (added_notes_insn == 0 + || DF_INSN_LUID (added_notes_insn) > DF_INSN_LUID (place2)) + added_notes_insn = place2; + } + } +} + +/* Similarly to above, distribute the LOG_LINKS that used to be present on + I3, I2, and I1 to new locations. This is also called to add a link + pointing at I3 when I3's destination is changed. */ + +static void +distribute_links (struct insn_link *links) +{ + struct insn_link *link, *next_link; + + for (link = links; link; link = next_link) + { + rtx_insn *place = 0; + rtx_insn *insn; + rtx set, reg; + + next_link = link->next; + + /* If the insn that this link points to is a NOTE, ignore it. */ + if (NOTE_P (link->insn)) + continue; + + set = 0; + rtx pat = PATTERN (link->insn); + if (GET_CODE (pat) == SET) + set = pat; + else if (GET_CODE (pat) == PARALLEL) + { + int i; + for (i = 0; i < XVECLEN (pat, 0); i++) + { + set = XVECEXP (pat, 0, i); + if (GET_CODE (set) != SET) + continue; + + reg = SET_DEST (set); + while (GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART + || GET_CODE (reg) == SUBREG) + reg = XEXP (reg, 0); + + if (!REG_P (reg)) + continue; + + if (REGNO (reg) == link->regno) + break; + } + if (i == XVECLEN (pat, 0)) + continue; + } + else + continue; + + reg = SET_DEST (set); + + while (GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART + || GET_CODE (reg) == SUBREG) + reg = XEXP (reg, 0); + + if (reg == pc_rtx) + continue; + + /* A LOG_LINK is defined as being placed on the first insn that uses + a register and points to the insn that sets the register. Start + searching at the next insn after the target of the link and stop + when we reach a set of the register or the end of the basic block. + + Note that this correctly handles the link that used to point from + I3 to I2. Also note that not much searching is typically done here + since most links don't point very far away. */ + + for (insn = NEXT_INSN (link->insn); + (insn && (this_basic_block->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) + || BB_HEAD (this_basic_block->next_bb) != insn)); + insn = NEXT_INSN (insn)) + if (DEBUG_INSN_P (insn)) + continue; + else if (INSN_P (insn) && reg_overlap_mentioned_p (reg, PATTERN (insn))) + { + if (reg_referenced_p (reg, PATTERN (insn))) + place = insn; + break; + } + else if (CALL_P (insn) + && find_reg_fusage (insn, USE, reg)) + { + place = insn; + break; + } + else if (INSN_P (insn) && reg_set_p (reg, insn)) + break; + + /* If we found a place to put the link, place it there unless there + is already a link to the same insn as LINK at that point. */ + + if (place) + { + struct insn_link *link2; + + FOR_EACH_LOG_LINK (link2, place) + if (link2->insn == link->insn && link2->regno == link->regno) + break; + + if (link2 == NULL) + { + link->next = LOG_LINKS (place); + LOG_LINKS (place) = link; + + /* Set added_links_insn to the earliest insn we added a + link to. */ + if (added_links_insn == 0 + || DF_INSN_LUID (added_links_insn) > DF_INSN_LUID (place)) + added_links_insn = place; + } + } + } +} + +/* Check for any register or memory mentioned in EQUIV that is not + mentioned in EXPR. This is used to restrict EQUIV to "specializations" + of EXPR where some registers may have been replaced by constants. */ + +static bool +unmentioned_reg_p (rtx equiv, rtx expr) +{ + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, equiv, NONCONST) + { + const_rtx x = *iter; + if ((REG_P (x) || MEM_P (x)) + && !reg_mentioned_p (x, expr)) + return true; + } + return false; +} + +DEBUG_FUNCTION void +dump_combine_stats (FILE *file) +{ + fprintf + (file, + ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n", + combine_attempts, combine_merges, combine_extras, combine_successes); +} + +void +dump_combine_total_stats (FILE *file) +{ + fprintf + (file, + "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n", + total_attempts, total_merges, total_extras, total_successes); +} + +/* Make pseudo-to-pseudo copies after every hard-reg-to-pseudo-copy, because + the reg-to-reg copy can usefully combine with later instructions, but we + do not want to combine the hard reg into later instructions, for that + restricts register allocation. */ +static void +make_more_copies (void) +{ + basic_block bb; + + FOR_EACH_BB_FN (bb, cfun) + { + rtx_insn *insn; + + FOR_BB_INSNS (bb, insn) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + rtx set = single_set (insn); + if (!set) + continue; + + rtx dest = SET_DEST (set); + if (!(REG_P (dest) && !HARD_REGISTER_P (dest))) + continue; + + rtx src = SET_SRC (set); + if (!(REG_P (src) && HARD_REGISTER_P (src))) + continue; + if (TEST_HARD_REG_BIT (fixed_reg_set, REGNO (src))) + continue; + + rtx new_reg = gen_reg_rtx (GET_MODE (dest)); + rtx_insn *new_insn = gen_move_insn (new_reg, src); + SET_SRC (set) = new_reg; + emit_insn_before (new_insn, insn); + df_insn_rescan (insn); + } + } +} + +/* Try combining insns through substitution. */ +static unsigned int +rest_of_handle_combine (void) +{ + make_more_copies (); + + df_set_flags (DF_LR_RUN_DCE + DF_DEFER_INSN_RESCAN); + df_note_add_problem (); + df_analyze (); + + regstat_init_n_sets_and_refs (); + reg_n_sets_max = max_reg_num (); + + int rebuild_jump_labels_after_combine + = combine_instructions (get_insns (), max_reg_num ()); + + /* Combining insns may have turned an indirect jump into a + direct jump. Rebuild the JUMP_LABEL fields of jumping + instructions. */ + if (rebuild_jump_labels_after_combine) + { + if (dom_info_available_p (CDI_DOMINATORS)) + free_dominance_info (CDI_DOMINATORS); + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cleanup_cfg (0); + timevar_pop (TV_JUMP); + } + + regstat_free_n_sets_and_refs (); + return 0; +} + +namespace { + +const pass_data pass_data_combine = +{ + RTL_PASS, /* type */ + "combine", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_COMBINE, /* tv_id */ + PROP_cfglayout, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_combine : public rtl_opt_pass +{ +public: + pass_combine (gcc::context *ctxt) + : rtl_opt_pass (pass_data_combine, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return (optimize > 0); } + virtual unsigned int execute (function *) + { + return rest_of_handle_combine (); + } + +}; // class pass_combine + +} // anon namespace + +rtl_opt_pass * +make_pass_combine (gcc::context *ctxt) +{ + return new pass_combine (ctxt); +} |