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/cse.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/cse.cc')
-rw-r--r-- | gcc/cse.cc | 7736 |
1 files changed, 7736 insertions, 0 deletions
diff --git a/gcc/cse.cc b/gcc/cse.cc new file mode 100644 index 0000000..94acdb9 --- /dev/null +++ b/gcc/cse.cc @@ -0,0 +1,7736 @@ +/* Common subexpression elimination 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/>. */ + +#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 "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "insn-config.h" +#include "regs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "cfgrtl.h" +#include "cfganal.h" +#include "cfgcleanup.h" +#include "alias.h" +#include "toplev.h" +#include "rtlhooks-def.h" +#include "tree-pass.h" +#include "dbgcnt.h" +#include "rtl-iter.h" +#include "regs.h" +#include "function-abi.h" +#include "rtlanal.h" +#include "expr.h" + +/* The basic idea of common subexpression elimination is to go + through the code, keeping a record of expressions that would + have the same value at the current scan point, and replacing + expressions encountered with the cheapest equivalent expression. + + It is too complicated to keep track of the different possibilities + when control paths merge in this code; so, at each label, we forget all + that is known and start fresh. This can be described as processing each + extended basic block separately. We have a separate pass to perform + global CSE. + + Note CSE can turn a conditional or computed jump into a nop or + an unconditional jump. When this occurs we arrange to run the jump + optimizer after CSE to delete the unreachable code. + + We use two data structures to record the equivalent expressions: + a hash table for most expressions, and a vector of "quantity + numbers" to record equivalent (pseudo) registers. + + The use of the special data structure for registers is desirable + because it is faster. It is possible because registers references + contain a fairly small number, the register number, taken from + a contiguously allocated series, and two register references are + identical if they have the same number. General expressions + do not have any such thing, so the only way to retrieve the + information recorded on an expression other than a register + is to keep it in a hash table. + +Registers and "quantity numbers": + + At the start of each basic block, all of the (hardware and pseudo) + registers used in the function are given distinct quantity + numbers to indicate their contents. During scan, when the code + copies one register into another, we copy the quantity number. + When a register is loaded in any other way, we allocate a new + quantity number to describe the value generated by this operation. + `REG_QTY (N)' records what quantity register N is currently thought + of as containing. + + All real quantity numbers are greater than or equal to zero. + If register N has not been assigned a quantity, `REG_QTY (N)' will + equal -N - 1, which is always negative. + + Quantity numbers below zero do not exist and none of the `qty_table' + entries should be referenced with a negative index. + + We also maintain a bidirectional chain of registers for each + quantity number. The `qty_table` members `first_reg' and `last_reg', + and `reg_eqv_table' members `next' and `prev' hold these chains. + + The first register in a chain is the one whose lifespan is least local. + Among equals, it is the one that was seen first. + We replace any equivalent register with that one. + + If two registers have the same quantity number, it must be true that + REG expressions with qty_table `mode' must be in the hash table for both + registers and must be in the same class. + + The converse is not true. Since hard registers may be referenced in + any mode, two REG expressions might be equivalent in the hash table + but not have the same quantity number if the quantity number of one + of the registers is not the same mode as those expressions. + +Constants and quantity numbers + + When a quantity has a known constant value, that value is stored + in the appropriate qty_table `const_rtx'. This is in addition to + putting the constant in the hash table as is usual for non-regs. + + Whether a reg or a constant is preferred is determined by the configuration + macro CONST_COSTS and will often depend on the constant value. In any + event, expressions containing constants can be simplified, by fold_rtx. + + When a quantity has a known nearly constant value (such as an address + of a stack slot), that value is stored in the appropriate qty_table + `const_rtx'. + + Integer constants don't have a machine mode. However, cse + determines the intended machine mode from the destination + of the instruction that moves the constant. The machine mode + is recorded in the hash table along with the actual RTL + constant expression so that different modes are kept separate. + +Other expressions: + + To record known equivalences among expressions in general + we use a hash table called `table'. It has a fixed number of buckets + that contain chains of `struct table_elt' elements for expressions. + These chains connect the elements whose expressions have the same + hash codes. + + Other chains through the same elements connect the elements which + currently have equivalent values. + + Register references in an expression are canonicalized before hashing + the expression. This is done using `reg_qty' and qty_table `first_reg'. + The hash code of a register reference is computed using the quantity + number, not the register number. + + When the value of an expression changes, it is necessary to remove from the + hash table not just that expression but all expressions whose values + could be different as a result. + + 1. If the value changing is in memory, except in special cases + ANYTHING referring to memory could be changed. That is because + nobody knows where a pointer does not point. + The function `invalidate_memory' removes what is necessary. + + The special cases are when the address is constant or is + a constant plus a fixed register such as the frame pointer + or a static chain pointer. When such addresses are stored in, + we can tell exactly which other such addresses must be invalidated + due to overlap. `invalidate' does this. + All expressions that refer to non-constant + memory addresses are also invalidated. `invalidate_memory' does this. + + 2. If the value changing is a register, all expressions + containing references to that register, and only those, + must be removed. + + Because searching the entire hash table for expressions that contain + a register is very slow, we try to figure out when it isn't necessary. + Precisely, this is necessary only when expressions have been + entered in the hash table using this register, and then the value has + changed, and then another expression wants to be added to refer to + the register's new value. This sequence of circumstances is rare + within any one basic block. + + `REG_TICK' and `REG_IN_TABLE', accessors for members of + cse_reg_info, are used to detect this case. REG_TICK (i) is + incremented whenever a value is stored in register i. + REG_IN_TABLE (i) holds -1 if no references to register i have been + entered in the table; otherwise, it contains the value REG_TICK (i) + had when the references were entered. If we want to enter a + reference and REG_IN_TABLE (i) != REG_TICK (i), we must scan and + remove old references. Until we want to enter a new entry, the + mere fact that the two vectors don't match makes the entries be + ignored if anyone tries to match them. + + Registers themselves are entered in the hash table as well as in + the equivalent-register chains. However, `REG_TICK' and + `REG_IN_TABLE' do not apply to expressions which are simple + register references. These expressions are removed from the table + immediately when they become invalid, and this can be done even if + we do not immediately search for all the expressions that refer to + the register. + + A CLOBBER rtx in an instruction invalidates its operand for further + reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK + invalidates everything that resides in memory. + +Related expressions: + + Constant expressions that differ only by an additive integer + are called related. When a constant expression is put in + the table, the related expression with no constant term + is also entered. These are made to point at each other + so that it is possible to find out if there exists any + register equivalent to an expression related to a given expression. */ + +/* Length of qty_table vector. We know in advance we will not need + a quantity number this big. */ + +static int max_qty; + +/* Next quantity number to be allocated. + This is 1 + the largest number needed so far. */ + +static int next_qty; + +/* Per-qty information tracking. + + `first_reg' and `last_reg' track the head and tail of the + chain of registers which currently contain this quantity. + + `mode' contains the machine mode of this quantity. + + `const_rtx' holds the rtx of the constant value of this + quantity, if known. A summations of the frame/arg pointer + and a constant can also be entered here. When this holds + a known value, `const_insn' is the insn which stored the + constant value. + + `comparison_{code,const,qty}' are used to track when a + comparison between a quantity and some constant or register has + been passed. In such a case, we know the results of the comparison + in case we see it again. These members record a comparison that + is known to be true. `comparison_code' holds the rtx code of such + a comparison, else it is set to UNKNOWN and the other two + comparison members are undefined. `comparison_const' holds + the constant being compared against, or zero if the comparison + is not against a constant. `comparison_qty' holds the quantity + being compared against when the result is known. If the comparison + is not with a register, `comparison_qty' is -1. */ + +struct qty_table_elem +{ + rtx const_rtx; + rtx_insn *const_insn; + rtx comparison_const; + int comparison_qty; + unsigned int first_reg, last_reg; + /* The sizes of these fields should match the sizes of the + code and mode fields of struct rtx_def (see rtl.h). */ + ENUM_BITFIELD(rtx_code) comparison_code : 16; + ENUM_BITFIELD(machine_mode) mode : 8; +}; + +/* The table of all qtys, indexed by qty number. */ +static struct qty_table_elem *qty_table; + +/* Insn being scanned. */ + +static rtx_insn *this_insn; +static bool optimize_this_for_speed_p; + +/* Index by register number, gives the number of the next (or + previous) register in the chain of registers sharing the same + value. + + Or -1 if this register is at the end of the chain. + + If REG_QTY (N) == -N - 1, reg_eqv_table[N].next is undefined. */ + +/* Per-register equivalence chain. */ +struct reg_eqv_elem +{ + int next, prev; +}; + +/* The table of all register equivalence chains. */ +static struct reg_eqv_elem *reg_eqv_table; + +struct cse_reg_info +{ + /* The timestamp at which this register is initialized. */ + unsigned int timestamp; + + /* The quantity number of the register's current contents. */ + int reg_qty; + + /* The number of times the register has been altered in the current + basic block. */ + int reg_tick; + + /* The REG_TICK value at which rtx's containing this register are + valid in the hash table. If this does not equal the current + reg_tick value, such expressions existing in the hash table are + invalid. */ + int reg_in_table; + + /* The SUBREG that was set when REG_TICK was last incremented. Set + to -1 if the last store was to the whole register, not a subreg. */ + unsigned int subreg_ticked; +}; + +/* A table of cse_reg_info indexed by register numbers. */ +static struct cse_reg_info *cse_reg_info_table; + +/* The size of the above table. */ +static unsigned int cse_reg_info_table_size; + +/* The index of the first entry that has not been initialized. */ +static unsigned int cse_reg_info_table_first_uninitialized; + +/* The timestamp at the beginning of the current run of + cse_extended_basic_block. We increment this variable at the beginning of + the current run of cse_extended_basic_block. The timestamp field of a + cse_reg_info entry matches the value of this variable if and only + if the entry has been initialized during the current run of + cse_extended_basic_block. */ +static unsigned int cse_reg_info_timestamp; + +/* A HARD_REG_SET containing all the hard registers for which there is + currently a REG expression in the hash table. Note the difference + from the above variables, which indicate if the REG is mentioned in some + expression in the table. */ + +static HARD_REG_SET hard_regs_in_table; + +/* True if CSE has altered the CFG. */ +static bool cse_cfg_altered; + +/* True if CSE has altered conditional jump insns in such a way + that jump optimization should be redone. */ +static bool cse_jumps_altered; + +/* True if we put a LABEL_REF into the hash table for an INSN + without a REG_LABEL_OPERAND, we have to rerun jump after CSE + to put in the note. */ +static bool recorded_label_ref; + +/* canon_hash stores 1 in do_not_record if it notices a reference to PC or + some other volatile subexpression. */ + +static int do_not_record; + +/* canon_hash stores 1 in hash_arg_in_memory + if it notices a reference to memory within the expression being hashed. */ + +static int hash_arg_in_memory; + +/* The hash table contains buckets which are chains of `struct table_elt's, + each recording one expression's information. + That expression is in the `exp' field. + + The canon_exp field contains a canonical (from the point of view of + alias analysis) version of the `exp' field. + + Those elements with the same hash code are chained in both directions + through the `next_same_hash' and `prev_same_hash' fields. + + Each set of expressions with equivalent values + are on a two-way chain through the `next_same_value' + and `prev_same_value' fields, and all point with + the `first_same_value' field at the first element in + that chain. The chain is in order of increasing cost. + Each element's cost value is in its `cost' field. + + The `in_memory' field is nonzero for elements that + involve any reference to memory. These elements are removed + whenever a write is done to an unidentified location in memory. + To be safe, we assume that a memory address is unidentified unless + the address is either a symbol constant or a constant plus + the frame pointer or argument pointer. + + The `related_value' field is used to connect related expressions + (that differ by adding an integer). + The related expressions are chained in a circular fashion. + `related_value' is zero for expressions for which this + chain is not useful. + + The `cost' field stores the cost of this element's expression. + The `regcost' field stores the value returned by approx_reg_cost for + this element's expression. + + The `is_const' flag is set if the element is a constant (including + a fixed address). + + The `flag' field is used as a temporary during some search routines. + + The `mode' field is usually the same as GET_MODE (`exp'), but + if `exp' is a CONST_INT and has no machine mode then the `mode' + field is the mode it was being used as. Each constant is + recorded separately for each mode it is used with. */ + +struct table_elt +{ + rtx exp; + rtx canon_exp; + struct table_elt *next_same_hash; + struct table_elt *prev_same_hash; + struct table_elt *next_same_value; + struct table_elt *prev_same_value; + struct table_elt *first_same_value; + struct table_elt *related_value; + int cost; + int regcost; + /* The size of this field should match the size + of the mode field of struct rtx_def (see rtl.h). */ + ENUM_BITFIELD(machine_mode) mode : 8; + char in_memory; + char is_const; + char flag; +}; + +/* We don't want a lot of buckets, because we rarely have very many + things stored in the hash table, and a lot of buckets slows + down a lot of loops that happen frequently. */ +#define HASH_SHIFT 5 +#define HASH_SIZE (1 << HASH_SHIFT) +#define HASH_MASK (HASH_SIZE - 1) + +/* Compute hash code of X in mode M. Special-case case where X is a pseudo + register (hard registers may require `do_not_record' to be set). */ + +#define HASH(X, M) \ + ((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER \ + ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \ + : canon_hash (X, M)) & HASH_MASK) + +/* Like HASH, but without side-effects. */ +#define SAFE_HASH(X, M) \ + ((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER \ + ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \ + : safe_hash (X, M)) & HASH_MASK) + +/* Determine whether register number N is considered a fixed register for the + purpose of approximating register costs. + It is desirable to replace other regs with fixed regs, to reduce need for + non-fixed hard regs. + A reg wins if it is either the frame pointer or designated as fixed. */ +#define FIXED_REGNO_P(N) \ + ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \ + || fixed_regs[N] || global_regs[N]) + +/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed + hard registers and pointers into the frame are the cheapest with a cost + of 0. Next come pseudos with a cost of one and other hard registers with + a cost of 2. Aside from these special cases, call `rtx_cost'. */ + +#define CHEAP_REGNO(N) \ + (REGNO_PTR_FRAME_P (N) \ + || (HARD_REGISTER_NUM_P (N) \ + && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS)) + +#define COST(X, MODE) \ + (REG_P (X) ? 0 : notreg_cost (X, MODE, SET, 1)) +#define COST_IN(X, MODE, OUTER, OPNO) \ + (REG_P (X) ? 0 : notreg_cost (X, MODE, OUTER, OPNO)) + +/* Get the number of times this register has been updated in this + basic block. */ + +#define REG_TICK(N) (get_cse_reg_info (N)->reg_tick) + +/* Get the point at which REG was recorded in the table. */ + +#define REG_IN_TABLE(N) (get_cse_reg_info (N)->reg_in_table) + +/* Get the SUBREG set at the last increment to REG_TICK (-1 if not a + SUBREG). */ + +#define SUBREG_TICKED(N) (get_cse_reg_info (N)->subreg_ticked) + +/* Get the quantity number for REG. */ + +#define REG_QTY(N) (get_cse_reg_info (N)->reg_qty) + +/* Determine if the quantity number for register X represents a valid index + into the qty_table. */ + +#define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0) + +/* Compare table_elt X and Y and return true iff X is cheaper than Y. */ + +#define CHEAPER(X, Y) \ + (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) + +static struct table_elt *table[HASH_SIZE]; + +/* Chain of `struct table_elt's made so far for this function + but currently removed from the table. */ + +static struct table_elt *free_element_chain; + +/* Trace a patch through the CFG. */ + +struct branch_path +{ + /* The basic block for this path entry. */ + basic_block bb; +}; + +/* This data describes a block that will be processed by + cse_extended_basic_block. */ + +struct cse_basic_block_data +{ + /* Total number of SETs in block. */ + int nsets; + /* Size of current branch path, if any. */ + int path_size; + /* Current path, indicating which basic_blocks will be processed. */ + struct branch_path *path; +}; + + +/* Pointers to the live in/live out bitmaps for the boundaries of the + current EBB. */ +static bitmap cse_ebb_live_in, cse_ebb_live_out; + +/* A simple bitmap to track which basic blocks have been visited + already as part of an already processed extended basic block. */ +static sbitmap cse_visited_basic_blocks; + +static bool fixed_base_plus_p (rtx x); +static int notreg_cost (rtx, machine_mode, enum rtx_code, int); +static int preferable (int, int, int, int); +static void new_basic_block (void); +static void make_new_qty (unsigned int, machine_mode); +static void make_regs_eqv (unsigned int, unsigned int); +static void delete_reg_equiv (unsigned int); +static int mention_regs (rtx); +static int insert_regs (rtx, struct table_elt *, int); +static void remove_from_table (struct table_elt *, unsigned); +static void remove_pseudo_from_table (rtx, unsigned); +static struct table_elt *lookup (rtx, unsigned, machine_mode); +static struct table_elt *lookup_for_remove (rtx, unsigned, machine_mode); +static rtx lookup_as_function (rtx, enum rtx_code); +static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned, + machine_mode, int, int); +static struct table_elt *insert (rtx, struct table_elt *, unsigned, + machine_mode); +static void merge_equiv_classes (struct table_elt *, struct table_elt *); +static void invalidate (rtx, machine_mode); +static void remove_invalid_refs (unsigned int); +static void remove_invalid_subreg_refs (unsigned int, poly_uint64, + machine_mode); +static void rehash_using_reg (rtx); +static void invalidate_memory (void); +static rtx use_related_value (rtx, struct table_elt *); + +static inline unsigned canon_hash (rtx, machine_mode); +static inline unsigned safe_hash (rtx, machine_mode); +static inline unsigned hash_rtx_string (const char *); + +static rtx canon_reg (rtx, rtx_insn *); +static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *, + machine_mode *, + machine_mode *); +static rtx fold_rtx (rtx, rtx_insn *); +static rtx equiv_constant (rtx); +static void record_jump_equiv (rtx_insn *, bool); +static void record_jump_cond (enum rtx_code, machine_mode, rtx, rtx, + int); +static void cse_insn (rtx_insn *); +static void cse_prescan_path (struct cse_basic_block_data *); +static void invalidate_from_clobbers (rtx_insn *); +static void invalidate_from_sets_and_clobbers (rtx_insn *); +static void cse_extended_basic_block (struct cse_basic_block_data *); +extern void dump_class (struct table_elt*); +static void get_cse_reg_info_1 (unsigned int regno); +static struct cse_reg_info * get_cse_reg_info (unsigned int regno); + +static void flush_hash_table (void); +static bool insn_live_p (rtx_insn *, int *); +static bool set_live_p (rtx, int *); +static void cse_change_cc_mode_insn (rtx_insn *, rtx); +static void cse_change_cc_mode_insns (rtx_insn *, rtx_insn *, rtx); +static machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx, + bool); + + +#undef RTL_HOOKS_GEN_LOWPART +#define RTL_HOOKS_GEN_LOWPART gen_lowpart_if_possible + +static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER; + +/* Nonzero if X has the form (PLUS frame-pointer integer). */ + +static bool +fixed_base_plus_p (rtx x) +{ + switch (GET_CODE (x)) + { + case REG: + if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx) + return true; + if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM]) + return true; + return false; + + case PLUS: + if (!CONST_INT_P (XEXP (x, 1))) + return false; + return fixed_base_plus_p (XEXP (x, 0)); + + default: + return false; + } +} + +/* Dump the expressions in the equivalence class indicated by CLASSP. + This function is used only for debugging. */ +DEBUG_FUNCTION void +dump_class (struct table_elt *classp) +{ + struct table_elt *elt; + + fprintf (stderr, "Equivalence chain for "); + print_rtl (stderr, classp->exp); + fprintf (stderr, ": \n"); + + for (elt = classp->first_same_value; elt; elt = elt->next_same_value) + { + print_rtl (stderr, elt->exp); + fprintf (stderr, "\n"); + } +} + +/* Return an estimate of the cost of the registers used in an rtx. + This is mostly the number of different REG expressions in the rtx; + however for some exceptions like fixed registers we use a cost of + 0. If any other hard register reference occurs, return MAX_COST. */ + +static int +approx_reg_cost (const_rtx x) +{ + int cost = 0; + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, x, NONCONST) + { + const_rtx x = *iter; + if (REG_P (x)) + { + unsigned int regno = REGNO (x); + if (!CHEAP_REGNO (regno)) + { + if (regno < FIRST_PSEUDO_REGISTER) + { + if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) + return MAX_COST; + cost += 2; + } + else + cost += 1; + } + } + } + return cost; +} + +/* Return a negative value if an rtx A, whose costs are given by COST_A + and REGCOST_A, is more desirable than an rtx B. + Return a positive value if A is less desirable, or 0 if the two are + equally good. */ +static int +preferable (int cost_a, int regcost_a, int cost_b, int regcost_b) +{ + /* First, get rid of cases involving expressions that are entirely + unwanted. */ + if (cost_a != cost_b) + { + if (cost_a == MAX_COST) + return 1; + if (cost_b == MAX_COST) + return -1; + } + + /* Avoid extending lifetimes of hardregs. */ + if (regcost_a != regcost_b) + { + if (regcost_a == MAX_COST) + return 1; + if (regcost_b == MAX_COST) + return -1; + } + + /* Normal operation costs take precedence. */ + if (cost_a != cost_b) + return cost_a - cost_b; + /* Only if these are identical consider effects on register pressure. */ + if (regcost_a != regcost_b) + return regcost_a - regcost_b; + return 0; +} + +/* Internal function, to compute cost when X is not a register; called + from COST macro to keep it simple. */ + +static int +notreg_cost (rtx x, machine_mode mode, enum rtx_code outer, int opno) +{ + scalar_int_mode int_mode, inner_mode; + return ((GET_CODE (x) == SUBREG + && REG_P (SUBREG_REG (x)) + && is_int_mode (mode, &int_mode) + && is_int_mode (GET_MODE (SUBREG_REG (x)), &inner_mode) + && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode) + && subreg_lowpart_p (x) + && TRULY_NOOP_TRUNCATION_MODES_P (int_mode, inner_mode)) + ? 0 + : rtx_cost (x, mode, outer, opno, optimize_this_for_speed_p) * 2); +} + + +/* Initialize CSE_REG_INFO_TABLE. */ + +static void +init_cse_reg_info (unsigned int nregs) +{ + /* Do we need to grow the table? */ + if (nregs > cse_reg_info_table_size) + { + unsigned int new_size; + + if (cse_reg_info_table_size < 2048) + { + /* Compute a new size that is a power of 2 and no smaller + than the large of NREGS and 64. */ + new_size = (cse_reg_info_table_size + ? cse_reg_info_table_size : 64); + + while (new_size < nregs) + new_size *= 2; + } + else + { + /* If we need a big table, allocate just enough to hold + NREGS registers. */ + new_size = nregs; + } + + /* Reallocate the table with NEW_SIZE entries. */ + free (cse_reg_info_table); + cse_reg_info_table = XNEWVEC (struct cse_reg_info, new_size); + cse_reg_info_table_size = new_size; + cse_reg_info_table_first_uninitialized = 0; + } + + /* Do we have all of the first NREGS entries initialized? */ + if (cse_reg_info_table_first_uninitialized < nregs) + { + unsigned int old_timestamp = cse_reg_info_timestamp - 1; + unsigned int i; + + /* Put the old timestamp on newly allocated entries so that they + will all be considered out of date. We do not touch those + entries beyond the first NREGS entries to be nice to the + virtual memory. */ + for (i = cse_reg_info_table_first_uninitialized; i < nregs; i++) + cse_reg_info_table[i].timestamp = old_timestamp; + + cse_reg_info_table_first_uninitialized = nregs; + } +} + +/* Given REGNO, initialize the cse_reg_info entry for REGNO. */ + +static void +get_cse_reg_info_1 (unsigned int regno) +{ + /* Set TIMESTAMP field to CSE_REG_INFO_TIMESTAMP so that this + entry will be considered to have been initialized. */ + cse_reg_info_table[regno].timestamp = cse_reg_info_timestamp; + + /* Initialize the rest of the entry. */ + cse_reg_info_table[regno].reg_tick = 1; + cse_reg_info_table[regno].reg_in_table = -1; + cse_reg_info_table[regno].subreg_ticked = -1; + cse_reg_info_table[regno].reg_qty = -regno - 1; +} + +/* Find a cse_reg_info entry for REGNO. */ + +static inline struct cse_reg_info * +get_cse_reg_info (unsigned int regno) +{ + struct cse_reg_info *p = &cse_reg_info_table[regno]; + + /* If this entry has not been initialized, go ahead and initialize + it. */ + if (p->timestamp != cse_reg_info_timestamp) + get_cse_reg_info_1 (regno); + + return p; +} + +/* Clear the hash table and initialize each register with its own quantity, + for a new basic block. */ + +static void +new_basic_block (void) +{ + int i; + + next_qty = 0; + + /* Invalidate cse_reg_info_table. */ + cse_reg_info_timestamp++; + + /* Clear out hash table state for this pass. */ + CLEAR_HARD_REG_SET (hard_regs_in_table); + + /* The per-quantity values used to be initialized here, but it is + much faster to initialize each as it is made in `make_new_qty'. */ + + for (i = 0; i < HASH_SIZE; i++) + { + struct table_elt *first; + + first = table[i]; + if (first != NULL) + { + struct table_elt *last = first; + + table[i] = NULL; + + while (last->next_same_hash != NULL) + last = last->next_same_hash; + + /* Now relink this hash entire chain into + the free element list. */ + + last->next_same_hash = free_element_chain; + free_element_chain = first; + } + } +} + +/* Say that register REG contains a quantity in mode MODE not in any + register before and initialize that quantity. */ + +static void +make_new_qty (unsigned int reg, machine_mode mode) +{ + int q; + struct qty_table_elem *ent; + struct reg_eqv_elem *eqv; + + gcc_assert (next_qty < max_qty); + + q = REG_QTY (reg) = next_qty++; + ent = &qty_table[q]; + ent->first_reg = reg; + ent->last_reg = reg; + ent->mode = mode; + ent->const_rtx = ent->const_insn = NULL; + ent->comparison_code = UNKNOWN; + + eqv = ®_eqv_table[reg]; + eqv->next = eqv->prev = -1; +} + +/* Make reg NEW equivalent to reg OLD. + OLD is not changing; NEW is. */ + +static void +make_regs_eqv (unsigned int new_reg, unsigned int old_reg) +{ + unsigned int lastr, firstr; + int q = REG_QTY (old_reg); + struct qty_table_elem *ent; + + ent = &qty_table[q]; + + /* Nothing should become eqv until it has a "non-invalid" qty number. */ + gcc_assert (REGNO_QTY_VALID_P (old_reg)); + + REG_QTY (new_reg) = q; + firstr = ent->first_reg; + lastr = ent->last_reg; + + /* Prefer fixed hard registers to anything. Prefer pseudo regs to other + hard regs. Among pseudos, if NEW will live longer than any other reg + of the same qty, and that is beyond the current basic block, + make it the new canonical replacement for this qty. */ + if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr)) + /* Certain fixed registers might be of the class NO_REGS. This means + that not only can they not be allocated by the compiler, but + they cannot be used in substitutions or canonicalizations + either. */ + && (new_reg >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new_reg) != NO_REGS) + && ((new_reg < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new_reg)) + || (new_reg >= FIRST_PSEUDO_REGISTER + && (firstr < FIRST_PSEUDO_REGISTER + || (bitmap_bit_p (cse_ebb_live_out, new_reg) + && !bitmap_bit_p (cse_ebb_live_out, firstr)) + || (bitmap_bit_p (cse_ebb_live_in, new_reg) + && !bitmap_bit_p (cse_ebb_live_in, firstr)))))) + { + reg_eqv_table[firstr].prev = new_reg; + reg_eqv_table[new_reg].next = firstr; + reg_eqv_table[new_reg].prev = -1; + ent->first_reg = new_reg; + } + else + { + /* If NEW is a hard reg (known to be non-fixed), insert at end. + Otherwise, insert before any non-fixed hard regs that are at the + end. Registers of class NO_REGS cannot be used as an + equivalent for anything. */ + while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0 + && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr)) + && new_reg >= FIRST_PSEUDO_REGISTER) + lastr = reg_eqv_table[lastr].prev; + reg_eqv_table[new_reg].next = reg_eqv_table[lastr].next; + if (reg_eqv_table[lastr].next >= 0) + reg_eqv_table[reg_eqv_table[lastr].next].prev = new_reg; + else + qty_table[q].last_reg = new_reg; + reg_eqv_table[lastr].next = new_reg; + reg_eqv_table[new_reg].prev = lastr; + } +} + +/* Remove REG from its equivalence class. */ + +static void +delete_reg_equiv (unsigned int reg) +{ + struct qty_table_elem *ent; + int q = REG_QTY (reg); + int p, n; + + /* If invalid, do nothing. */ + if (! REGNO_QTY_VALID_P (reg)) + return; + + ent = &qty_table[q]; + + p = reg_eqv_table[reg].prev; + n = reg_eqv_table[reg].next; + + if (n != -1) + reg_eqv_table[n].prev = p; + else + ent->last_reg = p; + if (p != -1) + reg_eqv_table[p].next = n; + else + ent->first_reg = n; + + REG_QTY (reg) = -reg - 1; +} + +/* Remove any invalid expressions from the hash table + that refer to any of the registers contained in expression X. + + Make sure that newly inserted references to those registers + as subexpressions will be considered valid. + + mention_regs is not called when a register itself + is being stored in the table. + + Return 1 if we have done something that may have changed the hash code + of X. */ + +static int +mention_regs (rtx x) +{ + enum rtx_code code; + int i, j; + const char *fmt; + int changed = 0; + + if (x == 0) + return 0; + + code = GET_CODE (x); + if (code == REG) + { + unsigned int regno = REGNO (x); + unsigned int endregno = END_REGNO (x); + unsigned int i; + + for (i = regno; i < endregno; i++) + { + if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i)) + remove_invalid_refs (i); + + REG_IN_TABLE (i) = REG_TICK (i); + SUBREG_TICKED (i) = -1; + } + + return 0; + } + + /* If this is a SUBREG, we don't want to discard other SUBREGs of the same + pseudo if they don't use overlapping words. We handle only pseudos + here for simplicity. */ + if (code == SUBREG && REG_P (SUBREG_REG (x)) + && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER) + { + unsigned int i = REGNO (SUBREG_REG (x)); + + if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i)) + { + /* If REG_IN_TABLE (i) differs from REG_TICK (i) by one, and + the last store to this register really stored into this + subreg, then remove the memory of this subreg. + Otherwise, remove any memory of the entire register and + all its subregs from the table. */ + if (REG_TICK (i) - REG_IN_TABLE (i) > 1 + || SUBREG_TICKED (i) != REGNO (SUBREG_REG (x))) + remove_invalid_refs (i); + else + remove_invalid_subreg_refs (i, SUBREG_BYTE (x), GET_MODE (x)); + } + + REG_IN_TABLE (i) = REG_TICK (i); + SUBREG_TICKED (i) = REGNO (SUBREG_REG (x)); + return 0; + } + + /* If X is a comparison or a COMPARE and either operand is a register + that does not have a quantity, give it one. This is so that a later + call to record_jump_equiv won't cause X to be assigned a different + hash code and not found in the table after that call. + + It is not necessary to do this here, since rehash_using_reg can + fix up the table later, but doing this here eliminates the need to + call that expensive function in the most common case where the only + use of the register is in the comparison. */ + + if (code == COMPARE || COMPARISON_P (x)) + { + if (REG_P (XEXP (x, 0)) + && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))) + if (insert_regs (XEXP (x, 0), NULL, 0)) + { + rehash_using_reg (XEXP (x, 0)); + changed = 1; + } + + if (REG_P (XEXP (x, 1)) + && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))) + if (insert_regs (XEXP (x, 1), NULL, 0)) + { + rehash_using_reg (XEXP (x, 1)); + changed = 1; + } + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + changed |= mention_regs (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + changed |= mention_regs (XVECEXP (x, i, j)); + + return changed; +} + +/* Update the register quantities for inserting X into the hash table + with a value equivalent to CLASSP. + (If the class does not contain a REG, it is irrelevant.) + If MODIFIED is nonzero, X is a destination; it is being modified. + Note that delete_reg_equiv should be called on a register + before insert_regs is done on that register with MODIFIED != 0. + + Nonzero value means that elements of reg_qty have changed + so X's hash code may be different. */ + +static int +insert_regs (rtx x, struct table_elt *classp, int modified) +{ + if (REG_P (x)) + { + unsigned int regno = REGNO (x); + int qty_valid; + + /* If REGNO is in the equivalence table already but is of the + wrong mode for that equivalence, don't do anything here. */ + + qty_valid = REGNO_QTY_VALID_P (regno); + if (qty_valid) + { + struct qty_table_elem *ent = &qty_table[REG_QTY (regno)]; + + if (ent->mode != GET_MODE (x)) + return 0; + } + + if (modified || ! qty_valid) + { + if (classp) + for (classp = classp->first_same_value; + classp != 0; + classp = classp->next_same_value) + if (REG_P (classp->exp) + && GET_MODE (classp->exp) == GET_MODE (x)) + { + unsigned c_regno = REGNO (classp->exp); + + gcc_assert (REGNO_QTY_VALID_P (c_regno)); + + /* Suppose that 5 is hard reg and 100 and 101 are + pseudos. Consider + + (set (reg:si 100) (reg:si 5)) + (set (reg:si 5) (reg:si 100)) + (set (reg:di 101) (reg:di 5)) + + We would now set REG_QTY (101) = REG_QTY (5), but the + entry for 5 is in SImode. When we use this later in + copy propagation, we get the register in wrong mode. */ + if (qty_table[REG_QTY (c_regno)].mode != GET_MODE (x)) + continue; + + make_regs_eqv (regno, c_regno); + return 1; + } + + /* Mention_regs for a SUBREG checks if REG_TICK is exactly one larger + than REG_IN_TABLE to find out if there was only a single preceding + invalidation - for the SUBREG - or another one, which would be + for the full register. However, if we find here that REG_TICK + indicates that the register is invalid, it means that it has + been invalidated in a separate operation. The SUBREG might be used + now (then this is a recursive call), or we might use the full REG + now and a SUBREG of it later. So bump up REG_TICK so that + mention_regs will do the right thing. */ + if (! modified + && REG_IN_TABLE (regno) >= 0 + && REG_TICK (regno) == REG_IN_TABLE (regno) + 1) + REG_TICK (regno)++; + make_new_qty (regno, GET_MODE (x)); + return 1; + } + + return 0; + } + + /* If X is a SUBREG, we will likely be inserting the inner register in the + table. If that register doesn't have an assigned quantity number at + this point but does later, the insertion that we will be doing now will + not be accessible because its hash code will have changed. So assign + a quantity number now. */ + + else if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x)) + && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x)))) + { + insert_regs (SUBREG_REG (x), NULL, 0); + mention_regs (x); + return 1; + } + else + return mention_regs (x); +} + + +/* Compute upper and lower anchors for CST. Also compute the offset of CST + from these anchors/bases such that *_BASE + *_OFFS = CST. Return false iff + CST is equal to an anchor. */ + +static bool +compute_const_anchors (rtx cst, + HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs, + HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs) +{ + HOST_WIDE_INT n = INTVAL (cst); + + *lower_base = n & ~(targetm.const_anchor - 1); + if (*lower_base == n) + return false; + + *upper_base = + (n + (targetm.const_anchor - 1)) & ~(targetm.const_anchor - 1); + *upper_offs = n - *upper_base; + *lower_offs = n - *lower_base; + return true; +} + +/* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE. */ + +static void +insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs, + machine_mode mode) +{ + struct table_elt *elt; + unsigned hash; + rtx anchor_exp; + rtx exp; + + anchor_exp = GEN_INT (anchor); + hash = HASH (anchor_exp, mode); + elt = lookup (anchor_exp, hash, mode); + if (!elt) + elt = insert (anchor_exp, NULL, hash, mode); + + exp = plus_constant (mode, reg, offs); + /* REG has just been inserted and the hash codes recomputed. */ + mention_regs (exp); + hash = HASH (exp, mode); + + /* Use the cost of the register rather than the whole expression. When + looking up constant anchors we will further offset the corresponding + expression therefore it does not make sense to prefer REGs over + reg-immediate additions. Prefer instead the oldest expression. Also + don't prefer pseudos over hard regs so that we derive constants in + argument registers from other argument registers rather than from the + original pseudo that was used to synthesize the constant. */ + insert_with_costs (exp, elt, hash, mode, COST (reg, mode), 1); +} + +/* The constant CST is equivalent to the register REG. Create + equivalences between the two anchors of CST and the corresponding + register-offset expressions using REG. */ + +static void +insert_const_anchors (rtx reg, rtx cst, machine_mode mode) +{ + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + + if (!compute_const_anchors (cst, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return; + + /* Ignore anchors of value 0. Constants accessible from zero are + simple. */ + if (lower_base != 0) + insert_const_anchor (lower_base, reg, -lower_offs, mode); + + if (upper_base != 0) + insert_const_anchor (upper_base, reg, -upper_offs, mode); +} + +/* We need to express ANCHOR_ELT->exp + OFFS. Walk the equivalence list of + ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a + valid expression. Return the cheapest and oldest of such expressions. In + *OLD, return how old the resulting expression is compared to the other + equivalent expressions. */ + +static rtx +find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs, + unsigned *old) +{ + struct table_elt *elt; + unsigned idx; + struct table_elt *match_elt; + rtx match; + + /* Find the cheapest and *oldest* expression to maximize the chance of + reusing the same pseudo. */ + + match_elt = NULL; + match = NULL_RTX; + for (elt = anchor_elt->first_same_value, idx = 0; + elt; + elt = elt->next_same_value, idx++) + { + if (match_elt && CHEAPER (match_elt, elt)) + return match; + + if (REG_P (elt->exp) + || (GET_CODE (elt->exp) == PLUS + && REG_P (XEXP (elt->exp, 0)) + && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT)) + { + rtx x; + + /* Ignore expressions that are no longer valid. */ + if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false)) + continue; + + x = plus_constant (GET_MODE (elt->exp), elt->exp, offs); + if (REG_P (x) + || (GET_CODE (x) == PLUS + && IN_RANGE (INTVAL (XEXP (x, 1)), + -targetm.const_anchor, + targetm.const_anchor - 1))) + { + match = x; + match_elt = elt; + *old = idx; + } + } + } + + return match; +} + +/* Try to express the constant SRC_CONST using a register+offset expression + derived from a constant anchor. Return it if successful or NULL_RTX, + otherwise. */ + +static rtx +try_const_anchors (rtx src_const, machine_mode mode) +{ + struct table_elt *lower_elt, *upper_elt; + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + rtx lower_anchor_rtx, upper_anchor_rtx; + rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX; + unsigned lower_old, upper_old; + + /* CONST_INT is used for CC modes, but we should leave those alone. */ + if (GET_MODE_CLASS (mode) == MODE_CC) + return NULL_RTX; + + gcc_assert (SCALAR_INT_MODE_P (mode)); + if (!compute_const_anchors (src_const, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return NULL_RTX; + + lower_anchor_rtx = GEN_INT (lower_base); + upper_anchor_rtx = GEN_INT (upper_base); + lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode); + upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode); + + if (lower_elt) + lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old); + if (upper_elt) + upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old); + + if (!lower_exp) + return upper_exp; + if (!upper_exp) + return lower_exp; + + /* Return the older expression. */ + return (upper_old > lower_old ? upper_exp : lower_exp); +} + +/* Look in or update the hash table. */ + +/* Remove table element ELT from use in the table. + HASH is its hash code, made using the HASH macro. + It's an argument because often that is known in advance + and we save much time not recomputing it. */ + +static void +remove_from_table (struct table_elt *elt, unsigned int hash) +{ + if (elt == 0) + return; + + /* Mark this element as removed. See cse_insn. */ + elt->first_same_value = 0; + + /* Remove the table element from its equivalence class. */ + + { + struct table_elt *prev = elt->prev_same_value; + struct table_elt *next = elt->next_same_value; + + if (next) + next->prev_same_value = prev; + + if (prev) + prev->next_same_value = next; + else + { + struct table_elt *newfirst = next; + while (next) + { + next->first_same_value = newfirst; + next = next->next_same_value; + } + } + } + + /* Remove the table element from its hash bucket. */ + + { + struct table_elt *prev = elt->prev_same_hash; + struct table_elt *next = elt->next_same_hash; + + if (next) + next->prev_same_hash = prev; + + if (prev) + prev->next_same_hash = next; + else if (table[hash] == elt) + table[hash] = next; + else + { + /* This entry is not in the proper hash bucket. This can happen + when two classes were merged by `merge_equiv_classes'. Search + for the hash bucket that it heads. This happens only very + rarely, so the cost is acceptable. */ + for (hash = 0; hash < HASH_SIZE; hash++) + if (table[hash] == elt) + table[hash] = next; + } + } + + /* Remove the table element from its related-value circular chain. */ + + if (elt->related_value != 0 && elt->related_value != elt) + { + struct table_elt *p = elt->related_value; + + while (p->related_value != elt) + p = p->related_value; + p->related_value = elt->related_value; + if (p->related_value == p) + p->related_value = 0; + } + + /* Now add it to the free element chain. */ + elt->next_same_hash = free_element_chain; + free_element_chain = elt; +} + +/* Same as above, but X is a pseudo-register. */ + +static void +remove_pseudo_from_table (rtx x, unsigned int hash) +{ + struct table_elt *elt; + + /* Because a pseudo-register can be referenced in more than one + mode, we might have to remove more than one table entry. */ + while ((elt = lookup_for_remove (x, hash, VOIDmode))) + remove_from_table (elt, hash); +} + +/* Look up X in the hash table and return its table element, + or 0 if X is not in the table. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + Here we are satisfied to find an expression whose tree structure + looks like X. */ + +static struct table_elt * +lookup (rtx x, unsigned int hash, machine_mode mode) +{ + struct table_elt *p; + + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode && ((x == p->exp && REG_P (x)) + || exp_equiv_p (x, p->exp, !REG_P (x), false))) + return p; + + return 0; +} + +/* Like `lookup' but don't care whether the table element uses invalid regs. + Also ignore discrepancies in the machine mode of a register. */ + +static struct table_elt * +lookup_for_remove (rtx x, unsigned int hash, machine_mode mode) +{ + struct table_elt *p; + + if (REG_P (x)) + { + unsigned int regno = REGNO (x); + + /* Don't check the machine mode when comparing registers; + invalidating (REG:SI 0) also invalidates (REG:DF 0). */ + for (p = table[hash]; p; p = p->next_same_hash) + if (REG_P (p->exp) + && REGNO (p->exp) == regno) + return p; + } + else + { + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode + && (x == p->exp || exp_equiv_p (x, p->exp, 0, false))) + return p; + } + + return 0; +} + +/* Look for an expression equivalent to X and with code CODE. + If one is found, return that expression. */ + +static rtx +lookup_as_function (rtx x, enum rtx_code code) +{ + struct table_elt *p + = lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x)); + + if (p == 0) + return 0; + + for (p = p->first_same_value; p; p = p->next_same_value) + if (GET_CODE (p->exp) == code + /* Make sure this is a valid entry in the table. */ + && exp_equiv_p (p->exp, p->exp, 1, false)) + return p->exp; + + return 0; +} + +/* Insert X in the hash table, assuming HASH is its hash code and + CLASSP is an element of the class it should go in (or 0 if a new + class should be made). COST is the code of X and reg_cost is the + cost of registers in X. It is inserted at the proper position to + keep the class in the order cheapest first. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + For elements of equal cheapness, the most recent one + goes in front, except that the first element in the list + remains first unless a cheaper element is added. The order of + pseudo-registers does not matter, as canon_reg will be called to + find the cheapest when a register is retrieved from the table. + + The in_memory field in the hash table element is set to 0. + The caller must set it nonzero if appropriate. + + You should call insert_regs (X, CLASSP, MODIFY) before calling here, + and if insert_regs returns a nonzero value + you must then recompute its hash code before calling here. + + If necessary, update table showing constant values of quantities. */ + +static struct table_elt * +insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash, + machine_mode mode, int cost, int reg_cost) +{ + struct table_elt *elt; + + /* If X is a register and we haven't made a quantity for it, + something is wrong. */ + gcc_assert (!REG_P (x) || REGNO_QTY_VALID_P (REGNO (x))); + + /* If X is a hard register, show it is being put in the table. */ + if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) + add_to_hard_reg_set (&hard_regs_in_table, GET_MODE (x), REGNO (x)); + + /* Put an element for X into the right hash bucket. */ + + elt = free_element_chain; + if (elt) + free_element_chain = elt->next_same_hash; + else + elt = XNEW (struct table_elt); + + elt->exp = x; + elt->canon_exp = NULL_RTX; + elt->cost = cost; + elt->regcost = reg_cost; + elt->next_same_value = 0; + elt->prev_same_value = 0; + elt->next_same_hash = table[hash]; + elt->prev_same_hash = 0; + elt->related_value = 0; + elt->in_memory = 0; + elt->mode = mode; + elt->is_const = (CONSTANT_P (x) || fixed_base_plus_p (x)); + + if (table[hash]) + table[hash]->prev_same_hash = elt; + table[hash] = elt; + + /* Put it into the proper value-class. */ + if (classp) + { + classp = classp->first_same_value; + if (CHEAPER (elt, classp)) + /* Insert at the head of the class. */ + { + struct table_elt *p; + elt->next_same_value = classp; + classp->prev_same_value = elt; + elt->first_same_value = elt; + + for (p = classp; p; p = p->next_same_value) + p->first_same_value = elt; + } + else + { + /* Insert not at head of the class. */ + /* Put it after the last element cheaper than X. */ + struct table_elt *p, *next; + + for (p = classp; + (next = p->next_same_value) && CHEAPER (next, elt); + p = next) + ; + + /* Put it after P and before NEXT. */ + elt->next_same_value = next; + if (next) + next->prev_same_value = elt; + + elt->prev_same_value = p; + p->next_same_value = elt; + elt->first_same_value = classp; + } + } + else + elt->first_same_value = elt; + + /* If this is a constant being set equivalent to a register or a register + being set equivalent to a constant, note the constant equivalence. + + If this is a constant, it cannot be equivalent to a different constant, + and a constant is the only thing that can be cheaper than a register. So + we know the register is the head of the class (before the constant was + inserted). + + If this is a register that is not already known equivalent to a + constant, we must check the entire class. + + If this is a register that is already known equivalent to an insn, + update the qtys `const_insn' to show that `this_insn' is the latest + insn making that quantity equivalent to the constant. */ + + if (elt->is_const && classp && REG_P (classp->exp) + && !REG_P (x)) + { + int exp_q = REG_QTY (REGNO (classp->exp)); + struct qty_table_elem *exp_ent = &qty_table[exp_q]; + + exp_ent->const_rtx = gen_lowpart (exp_ent->mode, x); + exp_ent->const_insn = this_insn; + } + + else if (REG_P (x) + && classp + && ! qty_table[REG_QTY (REGNO (x))].const_rtx + && ! elt->is_const) + { + struct table_elt *p; + + for (p = classp; p != 0; p = p->next_same_value) + { + if (p->is_const && !REG_P (p->exp)) + { + int x_q = REG_QTY (REGNO (x)); + struct qty_table_elem *x_ent = &qty_table[x_q]; + + x_ent->const_rtx + = gen_lowpart (GET_MODE (x), p->exp); + x_ent->const_insn = this_insn; + break; + } + } + } + + else if (REG_P (x) + && qty_table[REG_QTY (REGNO (x))].const_rtx + && GET_MODE (x) == qty_table[REG_QTY (REGNO (x))].mode) + qty_table[REG_QTY (REGNO (x))].const_insn = this_insn; + + /* If this is a constant with symbolic value, + and it has a term with an explicit integer value, + link it up with related expressions. */ + if (GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + unsigned subhash; + struct table_elt *subelt, *subelt_prev; + + if (subexp != 0) + { + /* Get the integer-free subexpression in the hash table. */ + subhash = SAFE_HASH (subexp, mode); + subelt = lookup (subexp, subhash, mode); + if (subelt == 0) + subelt = insert (subexp, NULL, subhash, mode); + /* Initialize SUBELT's circular chain if it has none. */ + if (subelt->related_value == 0) + subelt->related_value = subelt; + /* Find the element in the circular chain that precedes SUBELT. */ + subelt_prev = subelt; + while (subelt_prev->related_value != subelt) + subelt_prev = subelt_prev->related_value; + /* Put new ELT into SUBELT's circular chain just before SUBELT. + This way the element that follows SUBELT is the oldest one. */ + elt->related_value = subelt_prev->related_value; + subelt_prev->related_value = elt; + } + } + + return elt; +} + +/* Wrap insert_with_costs by passing the default costs. */ + +static struct table_elt * +insert (rtx x, struct table_elt *classp, unsigned int hash, + machine_mode mode) +{ + return insert_with_costs (x, classp, hash, mode, + COST (x, mode), approx_reg_cost (x)); +} + + +/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from + CLASS2 into CLASS1. This is done when we have reached an insn which makes + the two classes equivalent. + + CLASS1 will be the surviving class; CLASS2 should not be used after this + call. + + Any invalid entries in CLASS2 will not be copied. */ + +static void +merge_equiv_classes (struct table_elt *class1, struct table_elt *class2) +{ + struct table_elt *elt, *next, *new_elt; + + /* Ensure we start with the head of the classes. */ + class1 = class1->first_same_value; + class2 = class2->first_same_value; + + /* If they were already equal, forget it. */ + if (class1 == class2) + return; + + for (elt = class2; elt; elt = next) + { + unsigned int hash; + rtx exp = elt->exp; + machine_mode mode = elt->mode; + + next = elt->next_same_value; + + /* Remove old entry, make a new one in CLASS1's class. + Don't do this for invalid entries as we cannot find their + hash code (it also isn't necessary). */ + if (REG_P (exp) || exp_equiv_p (exp, exp, 1, false)) + { + bool need_rehash = false; + + hash_arg_in_memory = 0; + hash = HASH (exp, mode); + + if (REG_P (exp)) + { + need_rehash = REGNO_QTY_VALID_P (REGNO (exp)); + delete_reg_equiv (REGNO (exp)); + } + + if (REG_P (exp) && REGNO (exp) >= FIRST_PSEUDO_REGISTER) + remove_pseudo_from_table (exp, hash); + else + remove_from_table (elt, hash); + + if (insert_regs (exp, class1, 0) || need_rehash) + { + rehash_using_reg (exp); + hash = HASH (exp, mode); + } + new_elt = insert (exp, class1, hash, mode); + new_elt->in_memory = hash_arg_in_memory; + if (GET_CODE (exp) == ASM_OPERANDS && elt->cost == MAX_COST) + new_elt->cost = MAX_COST; + } + } +} + +/* Flush the entire hash table. */ + +static void +flush_hash_table (void) +{ + int i; + struct table_elt *p; + + for (i = 0; i < HASH_SIZE; i++) + for (p = table[i]; p; p = table[i]) + { + /* Note that invalidate can remove elements + after P in the current hash chain. */ + if (REG_P (p->exp)) + invalidate (p->exp, VOIDmode); + else + remove_from_table (p, i); + } +} + +/* Check whether an anti dependence exists between X and EXP. MODE and + ADDR are as for canon_anti_dependence. */ + +static bool +check_dependence (const_rtx x, rtx exp, machine_mode mode, rtx addr) +{ + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, x, NONCONST) + { + const_rtx x = *iter; + if (MEM_P (x) && canon_anti_dependence (x, true, exp, mode, addr)) + return true; + } + return false; +} + +/* Remove from the hash table, or mark as invalid, all expressions whose + values could be altered by storing in register X. */ + +static void +invalidate_reg (rtx x) +{ + gcc_assert (GET_CODE (x) == REG); + + /* If X is a register, dependencies on its contents are recorded + through the qty number mechanism. Just change the qty number of + the register, mark it as invalid for expressions that refer to it, + and remove it itself. */ + unsigned int regno = REGNO (x); + unsigned int hash = HASH (x, GET_MODE (x)); + + /* Remove REGNO from any quantity list it might be on and indicate + that its value might have changed. If it is a pseudo, remove its + entry from the hash table. + + For a hard register, we do the first two actions above for any + additional hard registers corresponding to X. Then, if any of these + registers are in the table, we must remove any REG entries that + overlap these registers. */ + + delete_reg_equiv (regno); + REG_TICK (regno)++; + SUBREG_TICKED (regno) = -1; + + if (regno >= FIRST_PSEUDO_REGISTER) + remove_pseudo_from_table (x, hash); + else + { + HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno); + unsigned int endregno = END_REGNO (x); + unsigned int rn; + struct table_elt *p, *next; + + CLEAR_HARD_REG_BIT (hard_regs_in_table, regno); + + for (rn = regno + 1; rn < endregno; rn++) + { + in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn); + CLEAR_HARD_REG_BIT (hard_regs_in_table, rn); + delete_reg_equiv (rn); + REG_TICK (rn)++; + SUBREG_TICKED (rn) = -1; + } + + if (in_table) + for (hash = 0; hash < HASH_SIZE; hash++) + for (p = table[hash]; p; p = next) + { + next = p->next_same_hash; + + if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) + continue; + + unsigned int tregno = REGNO (p->exp); + unsigned int tendregno = END_REGNO (p->exp); + if (tendregno > regno && tregno < endregno) + remove_from_table (p, hash); + } + } +} + +/* Remove from the hash table, or mark as invalid, all expressions whose + values could be altered by storing in X. X is a register, a subreg, or + a memory reference with nonvarying address (because, when a memory + reference with a varying address is stored in, all memory references are + removed by invalidate_memory so specific invalidation is superfluous). + FULL_MODE, if not VOIDmode, indicates that this much should be + invalidated instead of just the amount indicated by the mode of X. This + is only used for bitfield stores into memory. + + A nonvarying address may be just a register or just a symbol reference, + or it may be either of those plus a numeric offset. */ + +static void +invalidate (rtx x, machine_mode full_mode) +{ + int i; + struct table_elt *p; + rtx addr; + + switch (GET_CODE (x)) + { + case REG: + invalidate_reg (x); + return; + + case SUBREG: + invalidate (SUBREG_REG (x), VOIDmode); + return; + + case PARALLEL: + for (i = XVECLEN (x, 0) - 1; i >= 0; --i) + invalidate (XVECEXP (x, 0, i), VOIDmode); + return; + + case EXPR_LIST: + /* This is part of a disjoint return value; extract the location in + question ignoring the offset. */ + invalidate (XEXP (x, 0), VOIDmode); + return; + + case MEM: + addr = canon_rtx (get_addr (XEXP (x, 0))); + /* Calculate the canonical version of X here so that + true_dependence doesn't generate new RTL for X on each call. */ + x = canon_rtx (x); + + /* Remove all hash table elements that refer to overlapping pieces of + memory. */ + if (full_mode == VOIDmode) + full_mode = GET_MODE (x); + + for (i = 0; i < HASH_SIZE; i++) + { + struct table_elt *next; + + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (p->in_memory) + { + /* Just canonicalize the expression once; + otherwise each time we call invalidate + true_dependence will canonicalize the + expression again. */ + if (!p->canon_exp) + p->canon_exp = canon_rtx (p->exp); + if (check_dependence (p->canon_exp, x, full_mode, addr)) + remove_from_table (p, i); + } + } + } + return; + + default: + gcc_unreachable (); + } +} + +/* Invalidate DEST. Used when DEST is not going to be added + into the hash table for some reason, e.g. do_not_record + flagged on it. */ + +static void +invalidate_dest (rtx dest) +{ + if (REG_P (dest) + || GET_CODE (dest) == SUBREG + || MEM_P (dest)) + invalidate (dest, VOIDmode); + else if (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == ZERO_EXTRACT) + invalidate (XEXP (dest, 0), GET_MODE (dest)); +} + +/* Remove all expressions that refer to register REGNO, + since they are already invalid, and we are about to + mark that register valid again and don't want the old + expressions to reappear as valid. */ + +static void +remove_invalid_refs (unsigned int regno) +{ + unsigned int i; + struct table_elt *p, *next; + + for (i = 0; i < HASH_SIZE; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (!REG_P (p->exp) && refers_to_regno_p (regno, p->exp)) + remove_from_table (p, i); + } +} + +/* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET, + and mode MODE. */ +static void +remove_invalid_subreg_refs (unsigned int regno, poly_uint64 offset, + machine_mode mode) +{ + unsigned int i; + struct table_elt *p, *next; + + for (i = 0; i < HASH_SIZE; i++) + for (p = table[i]; p; p = next) + { + rtx exp = p->exp; + next = p->next_same_hash; + + if (!REG_P (exp) + && (GET_CODE (exp) != SUBREG + || !REG_P (SUBREG_REG (exp)) + || REGNO (SUBREG_REG (exp)) != regno + || ranges_maybe_overlap_p (SUBREG_BYTE (exp), + GET_MODE_SIZE (GET_MODE (exp)), + offset, GET_MODE_SIZE (mode))) + && refers_to_regno_p (regno, p->exp)) + remove_from_table (p, i); + } +} + +/* Recompute the hash codes of any valid entries in the hash table that + reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG. + + This is called when we make a jump equivalence. */ + +static void +rehash_using_reg (rtx x) +{ + unsigned int i; + struct table_elt *p, *next; + unsigned hash; + + if (GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); + + /* If X is not a register or if the register is known not to be in any + valid entries in the table, we have no work to do. */ + + if (!REG_P (x) + || REG_IN_TABLE (REGNO (x)) < 0 + || REG_IN_TABLE (REGNO (x)) != REG_TICK (REGNO (x))) + return; + + /* Scan all hash chains looking for valid entries that mention X. + If we find one and it is in the wrong hash chain, move it. */ + + for (i = 0; i < HASH_SIZE; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (reg_mentioned_p (x, p->exp) + && exp_equiv_p (p->exp, p->exp, 1, false) + && i != (hash = SAFE_HASH (p->exp, p->mode))) + { + if (p->next_same_hash) + p->next_same_hash->prev_same_hash = p->prev_same_hash; + + if (p->prev_same_hash) + p->prev_same_hash->next_same_hash = p->next_same_hash; + else + table[i] = p->next_same_hash; + + p->next_same_hash = table[hash]; + p->prev_same_hash = 0; + if (table[hash]) + table[hash]->prev_same_hash = p; + table[hash] = p; + } + } +} + +/* Remove from the hash table any expression that is a call-clobbered + register in INSN. Also update their TICK values. */ + +static void +invalidate_for_call (rtx_insn *insn) +{ + unsigned int regno; + unsigned hash; + struct table_elt *p, *next; + int in_table = 0; + hard_reg_set_iterator hrsi; + + /* Go through all the hard registers. For each that might be clobbered + in call insn INSN, remove the register from quantity chains and update + reg_tick if defined. Also see if any of these registers is currently + in the table. + + ??? We could be more precise for partially-clobbered registers, + and only invalidate values that actually occupy the clobbered part + of the registers. It doesn't seem worth the effort though, since + we shouldn't see this situation much before RA. Whatever choice + we make here has to be consistent with the table walk below, + so any change to this test will require a change there too. */ + HARD_REG_SET callee_clobbers + = insn_callee_abi (insn).full_and_partial_reg_clobbers (); + EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi) + { + delete_reg_equiv (regno); + if (REG_TICK (regno) >= 0) + { + REG_TICK (regno)++; + SUBREG_TICKED (regno) = -1; + } + in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0); + } + + /* In the case where we have no call-clobbered hard registers in the + table, we are done. Otherwise, scan the table and remove any + entry that overlaps a call-clobbered register. */ + + if (in_table) + for (hash = 0; hash < HASH_SIZE; hash++) + for (p = table[hash]; p; p = next) + { + next = p->next_same_hash; + + if (!REG_P (p->exp) + || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) + continue; + + /* This must use the same test as above rather than the + more accurate clobbers_reg_p. */ + if (overlaps_hard_reg_set_p (callee_clobbers, GET_MODE (p->exp), + REGNO (p->exp))) + remove_from_table (p, hash); + } +} + +/* Given an expression X of type CONST, + and ELT which is its table entry (or 0 if it + is not in the hash table), + return an alternate expression for X as a register plus integer. + If none can be found, return 0. */ + +static rtx +use_related_value (rtx x, struct table_elt *elt) +{ + struct table_elt *relt = 0; + struct table_elt *p, *q; + HOST_WIDE_INT offset; + + /* First, is there anything related known? + If we have a table element, we can tell from that. + Otherwise, must look it up. */ + + if (elt != 0 && elt->related_value != 0) + relt = elt; + else if (elt == 0 && GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + if (subexp != 0) + relt = lookup (subexp, + SAFE_HASH (subexp, GET_MODE (subexp)), + GET_MODE (subexp)); + } + + if (relt == 0) + return 0; + + /* Search all related table entries for one that has an + equivalent register. */ + + p = relt; + while (1) + { + /* This loop is strange in that it is executed in two different cases. + The first is when X is already in the table. Then it is searching + the RELATED_VALUE list of X's class (RELT). The second case is when + X is not in the table. Then RELT points to a class for the related + value. + + Ensure that, whatever case we are in, that we ignore classes that have + the same value as X. */ + + if (rtx_equal_p (x, p->exp)) + q = 0; + else + for (q = p->first_same_value; q; q = q->next_same_value) + if (REG_P (q->exp)) + break; + + if (q) + break; + + p = p->related_value; + + /* We went all the way around, so there is nothing to be found. + Alternatively, perhaps RELT was in the table for some other reason + and it has no related values recorded. */ + if (p == relt || p == 0) + break; + } + + if (q == 0) + return 0; + + offset = (get_integer_term (x) - get_integer_term (p->exp)); + /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ + return plus_constant (q->mode, q->exp, offset); +} + + +/* Hash a string. Just add its bytes up. */ +static inline unsigned +hash_rtx_string (const char *ps) +{ + unsigned hash = 0; + const unsigned char *p = (const unsigned char *) ps; + + if (p) + while (*p) + hash += *p++; + + return hash; +} + +/* Same as hash_rtx, but call CB on each rtx if it is not NULL. + When the callback returns true, we continue with the new rtx. */ + +unsigned +hash_rtx_cb (const_rtx x, machine_mode mode, + int *do_not_record_p, int *hash_arg_in_memory_p, + bool have_reg_qty, hash_rtx_callback_function cb) +{ + int i, j; + unsigned hash = 0; + enum rtx_code code; + const char *fmt; + machine_mode newmode; + rtx newx; + + /* Used to turn recursion into iteration. We can't rely on GCC's + tail-recursion elimination since we need to keep accumulating values + in HASH. */ + repeat: + if (x == 0) + return hash; + + /* Invoke the callback first. */ + if (cb != NULL + && ((*cb) (x, mode, &newx, &newmode))) + { + hash += hash_rtx_cb (newx, newmode, do_not_record_p, + hash_arg_in_memory_p, have_reg_qty, cb); + return hash; + } + + code = GET_CODE (x); + switch (code) + { + case REG: + { + unsigned int regno = REGNO (x); + + if (do_not_record_p && !reload_completed) + { + /* On some machines, we can't record any non-fixed hard register, + because extending its life will cause reload problems. We + consider ap, fp, sp, gp to be fixed for this purpose. + + We also consider CCmode registers to be fixed for this purpose; + failure to do so leads to failure to simplify 0<100 type of + conditionals. + + On all machines, we can't record any global registers. + Nor should we record any register that is in a small + class, as defined by TARGET_CLASS_LIKELY_SPILLED_P. */ + bool record; + + if (regno >= FIRST_PSEUDO_REGISTER) + record = true; + else if (x == frame_pointer_rtx + || x == hard_frame_pointer_rtx + || x == arg_pointer_rtx + || x == stack_pointer_rtx + || x == pic_offset_table_rtx) + record = true; + else if (global_regs[regno]) + record = false; + else if (fixed_regs[regno]) + record = true; + else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC) + record = true; + else if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) + record = false; + else if (targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno))) + record = false; + else + record = true; + + if (!record) + { + *do_not_record_p = 1; + return 0; + } + } + + hash += ((unsigned int) REG << 7); + hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno); + return hash; + } + + /* We handle SUBREG of a REG specially because the underlying + reg changes its hash value with every value change; we don't + want to have to forget unrelated subregs when one subreg changes. */ + case SUBREG: + { + if (REG_P (SUBREG_REG (x))) + { + hash += (((unsigned int) SUBREG << 7) + + REGNO (SUBREG_REG (x)) + + (constant_lower_bound (SUBREG_BYTE (x)) + / UNITS_PER_WORD)); + return hash; + } + break; + } + + case CONST_INT: + hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode + + (unsigned int) INTVAL (x)); + return hash; + + case CONST_WIDE_INT: + for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++) + hash += CONST_WIDE_INT_ELT (x, i); + return hash; + + case CONST_POLY_INT: + { + inchash::hash h; + h.add_int (hash); + for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i) + h.add_wide_int (CONST_POLY_INT_COEFFS (x)[i]); + return h.end (); + } + + case CONST_DOUBLE: + /* This is like the general case, except that it only counts + the integers representing the constant. */ + hash += (unsigned int) code + (unsigned int) GET_MODE (x); + if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode) + hash += ((unsigned int) CONST_DOUBLE_LOW (x) + + (unsigned int) CONST_DOUBLE_HIGH (x)); + else + hash += real_hash (CONST_DOUBLE_REAL_VALUE (x)); + return hash; + + case CONST_FIXED: + hash += (unsigned int) code + (unsigned int) GET_MODE (x); + hash += fixed_hash (CONST_FIXED_VALUE (x)); + return hash; + + case CONST_VECTOR: + { + int units; + rtx elt; + + units = const_vector_encoded_nelts (x); + + for (i = 0; i < units; ++i) + { + elt = CONST_VECTOR_ENCODED_ELT (x, i); + hash += hash_rtx_cb (elt, GET_MODE (elt), + do_not_record_p, hash_arg_in_memory_p, + have_reg_qty, cb); + } + + return hash; + } + + /* Assume there is only one rtx object for any given label. */ + case LABEL_REF: + /* We don't hash on the address of the CODE_LABEL to avoid bootstrap + differences and differences between each stage's debugging dumps. */ + hash += (((unsigned int) LABEL_REF << 7) + + CODE_LABEL_NUMBER (label_ref_label (x))); + return hash; + + case SYMBOL_REF: + { + /* Don't hash on the symbol's address to avoid bootstrap differences. + Different hash values may cause expressions to be recorded in + different orders and thus different registers to be used in the + final assembler. This also avoids differences in the dump files + between various stages. */ + unsigned int h = 0; + const unsigned char *p = (const unsigned char *) XSTR (x, 0); + + while (*p) + h += (h << 7) + *p++; /* ??? revisit */ + + hash += ((unsigned int) SYMBOL_REF << 7) + h; + return hash; + } + + case MEM: + /* We don't record if marked volatile or if BLKmode since we don't + know the size of the move. */ + if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)) + { + *do_not_record_p = 1; + return 0; + } + if (hash_arg_in_memory_p && !MEM_READONLY_P (x)) + *hash_arg_in_memory_p = 1; + + /* Now that we have already found this special case, + might as well speed it up as much as possible. */ + hash += (unsigned) MEM; + x = XEXP (x, 0); + goto repeat; + + case USE: + /* A USE that mentions non-volatile memory needs special + handling since the MEM may be BLKmode which normally + prevents an entry from being made. Pure calls are + marked by a USE which mentions BLKmode memory. + See calls.c:emit_call_1. */ + if (MEM_P (XEXP (x, 0)) + && ! MEM_VOLATILE_P (XEXP (x, 0))) + { + hash += (unsigned) USE; + x = XEXP (x, 0); + + if (hash_arg_in_memory_p && !MEM_READONLY_P (x)) + *hash_arg_in_memory_p = 1; + + /* Now that we have already found this special case, + might as well speed it up as much as possible. */ + hash += (unsigned) MEM; + x = XEXP (x, 0); + goto repeat; + } + break; + + case PRE_DEC: + case PRE_INC: + case POST_DEC: + case POST_INC: + case PRE_MODIFY: + case POST_MODIFY: + case PC: + case CALL: + case UNSPEC_VOLATILE: + if (do_not_record_p) { + *do_not_record_p = 1; + return 0; + } + else + return hash; + break; + + case ASM_OPERANDS: + if (do_not_record_p && MEM_VOLATILE_P (x)) + { + *do_not_record_p = 1; + return 0; + } + else + { + /* We don't want to take the filename and line into account. */ + hash += (unsigned) code + (unsigned) GET_MODE (x) + + hash_rtx_string (ASM_OPERANDS_TEMPLATE (x)) + + hash_rtx_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x)) + + (unsigned) ASM_OPERANDS_OUTPUT_IDX (x); + + if (ASM_OPERANDS_INPUT_LENGTH (x)) + { + for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) + { + hash += (hash_rtx_cb (ASM_OPERANDS_INPUT (x, i), + GET_MODE (ASM_OPERANDS_INPUT (x, i)), + do_not_record_p, hash_arg_in_memory_p, + have_reg_qty, cb) + + hash_rtx_string + (ASM_OPERANDS_INPUT_CONSTRAINT (x, i))); + } + + hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0)); + x = ASM_OPERANDS_INPUT (x, 0); + mode = GET_MODE (x); + goto repeat; + } + + return hash; + } + break; + + default: + break; + } + + i = GET_RTX_LENGTH (code) - 1; + hash += (unsigned) code + (unsigned) GET_MODE (x); + fmt = GET_RTX_FORMAT (code); + for (; i >= 0; i--) + { + switch (fmt[i]) + { + case 'e': + /* If we are about to do the last recursive call + needed at this level, change it into iteration. + This function is called enough to be worth it. */ + if (i == 0) + { + x = XEXP (x, i); + goto repeat; + } + + hash += hash_rtx_cb (XEXP (x, i), VOIDmode, do_not_record_p, + hash_arg_in_memory_p, + have_reg_qty, cb); + break; + + case 'E': + for (j = 0; j < XVECLEN (x, i); j++) + hash += hash_rtx_cb (XVECEXP (x, i, j), VOIDmode, do_not_record_p, + hash_arg_in_memory_p, + have_reg_qty, cb); + break; + + case 's': + hash += hash_rtx_string (XSTR (x, i)); + break; + + case 'i': + hash += (unsigned int) XINT (x, i); + break; + + case 'p': + hash += constant_lower_bound (SUBREG_BYTE (x)); + break; + + case '0': case 't': + /* Unused. */ + break; + + default: + gcc_unreachable (); + } + } + + return hash; +} + +/* Hash an rtx. We are careful to make sure the value is never negative. + Equivalent registers hash identically. + MODE is used in hashing for CONST_INTs only; + otherwise the mode of X is used. + + Store 1 in DO_NOT_RECORD_P if any subexpression is volatile. + + If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains + a MEM rtx which does not have the MEM_READONLY_P flag set. + + Note that cse_insn knows that the hash code of a MEM expression + is just (int) MEM plus the hash code of the address. */ + +unsigned +hash_rtx (const_rtx x, machine_mode mode, int *do_not_record_p, + int *hash_arg_in_memory_p, bool have_reg_qty) +{ + return hash_rtx_cb (x, mode, do_not_record_p, + hash_arg_in_memory_p, have_reg_qty, NULL); +} + +/* Hash an rtx X for cse via hash_rtx. + Stores 1 in do_not_record if any subexpression is volatile. + Stores 1 in hash_arg_in_memory if X contains a mem rtx which + does not have the MEM_READONLY_P flag set. */ + +static inline unsigned +canon_hash (rtx x, machine_mode mode) +{ + return hash_rtx (x, mode, &do_not_record, &hash_arg_in_memory, true); +} + +/* Like canon_hash but with no side effects, i.e. do_not_record + and hash_arg_in_memory are not changed. */ + +static inline unsigned +safe_hash (rtx x, machine_mode mode) +{ + int dummy_do_not_record; + return hash_rtx (x, mode, &dummy_do_not_record, NULL, true); +} + +/* Return 1 iff X and Y would canonicalize into the same thing, + without actually constructing the canonicalization of either one. + If VALIDATE is nonzero, + we assume X is an expression being processed from the rtl + and Y was found in the hash table. We check register refs + in Y for being marked as valid. + + If FOR_GCSE is true, we compare X and Y for equivalence for GCSE. */ + +int +exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse) +{ + int i, j; + enum rtx_code code; + const char *fmt; + + /* Note: it is incorrect to assume an expression is equivalent to itself + if VALIDATE is nonzero. */ + if (x == y && !validate) + return 1; + + if (x == 0 || y == 0) + return x == y; + + code = GET_CODE (x); + if (code != GET_CODE (y)) + return 0; + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + /* MEMs referring to different address space are not equivalent. */ + if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y)) + return 0; + + switch (code) + { + case PC: + CASE_CONST_UNIQUE: + return x == y; + + case CONST_VECTOR: + if (!same_vector_encodings_p (x, y)) + return false; + break; + + case LABEL_REF: + return label_ref_label (x) == label_ref_label (y); + + case SYMBOL_REF: + return XSTR (x, 0) == XSTR (y, 0); + + case REG: + if (for_gcse) + return REGNO (x) == REGNO (y); + else + { + unsigned int regno = REGNO (y); + unsigned int i; + unsigned int endregno = END_REGNO (y); + + /* If the quantities are not the same, the expressions are not + equivalent. If there are and we are not to validate, they + are equivalent. Otherwise, ensure all regs are up-to-date. */ + + if (REG_QTY (REGNO (x)) != REG_QTY (regno)) + return 0; + + if (! validate) + return 1; + + for (i = regno; i < endregno; i++) + if (REG_IN_TABLE (i) != REG_TICK (i)) + return 0; + + return 1; + } + + case MEM: + if (for_gcse) + { + /* A volatile mem should not be considered equivalent to any + other. */ + if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y)) + return 0; + + /* Can't merge two expressions in different alias sets, since we + can decide that the expression is transparent in a block when + it isn't, due to it being set with the different alias set. + + Also, can't merge two expressions with different MEM_ATTRS. + They could e.g. be two different entities allocated into the + same space on the stack (see e.g. PR25130). In that case, the + MEM addresses can be the same, even though the two MEMs are + absolutely not equivalent. + + But because really all MEM attributes should be the same for + equivalent MEMs, we just use the invariant that MEMs that have + the same attributes share the same mem_attrs data structure. */ + if (!mem_attrs_eq_p (MEM_ATTRS (x), MEM_ATTRS (y))) + return 0; + + /* If we are handling exceptions, we cannot consider two expressions + with different trapping status as equivalent, because simple_mem + might accept one and reject the other. */ + if (cfun->can_throw_non_call_exceptions + && (MEM_NOTRAP_P (x) != MEM_NOTRAP_P (y))) + return 0; + } + break; + + /* For commutative operations, check both orders. */ + case PLUS: + case MULT: + case AND: + case IOR: + case XOR: + case NE: + case EQ: + return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), + validate, for_gcse) + && exp_equiv_p (XEXP (x, 1), XEXP (y, 1), + validate, for_gcse)) + || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1), + validate, for_gcse) + && exp_equiv_p (XEXP (x, 1), XEXP (y, 0), + validate, for_gcse))); + + case ASM_OPERANDS: + /* We don't use the generic code below because we want to + disregard filename and line numbers. */ + + /* A volatile asm isn't equivalent to any other. */ + if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y)) + return 0; + + if (GET_MODE (x) != GET_MODE (y) + || strcmp (ASM_OPERANDS_TEMPLATE (x), ASM_OPERANDS_TEMPLATE (y)) + || strcmp (ASM_OPERANDS_OUTPUT_CONSTRAINT (x), + ASM_OPERANDS_OUTPUT_CONSTRAINT (y)) + || ASM_OPERANDS_OUTPUT_IDX (x) != ASM_OPERANDS_OUTPUT_IDX (y) + || ASM_OPERANDS_INPUT_LENGTH (x) != ASM_OPERANDS_INPUT_LENGTH (y)) + return 0; + + if (ASM_OPERANDS_INPUT_LENGTH (x)) + { + for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) + if (! exp_equiv_p (ASM_OPERANDS_INPUT (x, i), + ASM_OPERANDS_INPUT (y, i), + validate, for_gcse) + || strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i), + ASM_OPERANDS_INPUT_CONSTRAINT (y, i))) + return 0; + } + + return 1; + + default: + break; + } + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole thing. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + switch (fmt[i]) + { + case 'e': + if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), + validate, for_gcse)) + return 0; + break; + + case 'E': + if (XVECLEN (x, i) != XVECLEN (y, i)) + return 0; + for (j = 0; j < XVECLEN (x, i); j++) + if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), + validate, for_gcse)) + return 0; + break; + + case 's': + if (strcmp (XSTR (x, i), XSTR (y, i))) + return 0; + break; + + case 'i': + if (XINT (x, i) != XINT (y, i)) + return 0; + break; + + case 'w': + if (XWINT (x, i) != XWINT (y, i)) + return 0; + break; + + case 'p': + if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y))) + return 0; + break; + + case '0': + case 't': + break; + + default: + gcc_unreachable (); + } + } + + return 1; +} + +/* Subroutine of canon_reg. Pass *XLOC through canon_reg, and validate + the result if necessary. INSN is as for canon_reg. */ + +static void +validate_canon_reg (rtx *xloc, rtx_insn *insn) +{ + if (*xloc) + { + rtx new_rtx = canon_reg (*xloc, insn); + + /* If replacing pseudo with hard reg or vice versa, ensure the + insn remains valid. Likewise if the insn has MATCH_DUPs. */ + gcc_assert (insn && new_rtx); + validate_change (insn, xloc, new_rtx, 1); + } +} + +/* Canonicalize an expression: + replace each register reference inside it + with the "oldest" equivalent register. + + If INSN is nonzero validate_change is used to ensure that INSN remains valid + after we make our substitution. The calls are made with IN_GROUP nonzero + so apply_change_group must be called upon the outermost return from this + function (unless INSN is zero). The result of apply_change_group can + generally be discarded since the changes we are making are optional. */ + +static rtx +canon_reg (rtx x, rtx_insn *insn) +{ + int i; + enum rtx_code code; + const char *fmt; + + if (x == 0) + return x; + + code = GET_CODE (x); + switch (code) + { + case PC: + case CONST: + CASE_CONST_ANY: + case SYMBOL_REF: + case LABEL_REF: + case ADDR_VEC: + case ADDR_DIFF_VEC: + return x; + + case REG: + { + int first; + int q; + struct qty_table_elem *ent; + + /* Never replace a hard reg, because hard regs can appear + in more than one machine mode, and we must preserve the mode + of each occurrence. Also, some hard regs appear in + MEMs that are shared and mustn't be altered. Don't try to + replace any reg that maps to a reg of class NO_REGS. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER + || ! REGNO_QTY_VALID_P (REGNO (x))) + return x; + + q = REG_QTY (REGNO (x)); + ent = &qty_table[q]; + first = ent->first_reg; + return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] + : REGNO_REG_CLASS (first) == NO_REGS ? x + : gen_rtx_REG (ent->mode, first)); + } + + default: + break; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + int j; + + if (fmt[i] == 'e') + validate_canon_reg (&XEXP (x, i), insn); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + validate_canon_reg (&XVECEXP (x, i, j), insn); + } + + return x; +} + +/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison + operation (EQ, NE, GT, etc.), follow it back through the hash table and + what values are being compared. + + *PARG1 and *PARG2 are updated to contain the rtx representing the values + actually being compared. For example, if *PARG1 was (reg:CC CC_REG) and + *PARG2 was (const_int 0), *PARG1 and *PARG2 will be set to the objects that + were compared to produce (reg:CC CC_REG). + + The return value is the comparison operator and is either the code of + A or the code corresponding to the inverse of the comparison. */ + +static enum rtx_code +find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2, + machine_mode *pmode1, machine_mode *pmode2) +{ + rtx arg1, arg2; + hash_set<rtx> *visited = NULL; + /* Set nonzero when we find something of interest. */ + rtx x = NULL; + + arg1 = *parg1, arg2 = *parg2; + + /* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */ + + while (arg2 == CONST0_RTX (GET_MODE (arg1))) + { + int reverse_code = 0; + struct table_elt *p = 0; + + /* Remember state from previous iteration. */ + if (x) + { + if (!visited) + visited = new hash_set<rtx>; + visited->add (x); + x = 0; + } + + /* If arg1 is a COMPARE, extract the comparison arguments from it. */ + + if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx) + x = arg1; + + /* If ARG1 is a comparison operator and CODE is testing for + STORE_FLAG_VALUE, get the inner arguments. */ + + else if (COMPARISON_P (arg1)) + { +#ifdef FLOAT_STORE_FLAG_VALUE + REAL_VALUE_TYPE fsfv; +#endif + + if (code == NE + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT + && code == LT && STORE_FLAG_VALUE == -1) +#ifdef FLOAT_STORE_FLAG_VALUE + || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1)) + && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), + REAL_VALUE_NEGATIVE (fsfv))) +#endif + ) + x = arg1; + else if (code == EQ + || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT + && code == GE && STORE_FLAG_VALUE == -1) +#ifdef FLOAT_STORE_FLAG_VALUE + || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1)) + && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), + REAL_VALUE_NEGATIVE (fsfv))) +#endif + ) + x = arg1, reverse_code = 1; + } + + /* ??? We could also check for + + (ne (and (eq (...) (const_int 1))) (const_int 0)) + + and related forms, but let's wait until we see them occurring. */ + + if (x == 0) + /* Look up ARG1 in the hash table and see if it has an equivalence + that lets us see what is being compared. */ + p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1)); + if (p) + { + p = p->first_same_value; + + /* If what we compare is already known to be constant, that is as + good as it gets. + We need to break the loop in this case, because otherwise we + can have an infinite loop when looking at a reg that is known + to be a constant which is the same as a comparison of a reg + against zero which appears later in the insn stream, which in + turn is constant and the same as the comparison of the first reg + against zero... */ + if (p->is_const) + break; + } + + for (; p; p = p->next_same_value) + { + machine_mode inner_mode = GET_MODE (p->exp); +#ifdef FLOAT_STORE_FLAG_VALUE + REAL_VALUE_TYPE fsfv; +#endif + + /* If the entry isn't valid, skip it. */ + if (! exp_equiv_p (p->exp, p->exp, 1, false)) + continue; + + /* If it's a comparison we've used before, skip it. */ + if (visited && visited->contains (p->exp)) + continue; + + if (GET_CODE (p->exp) == COMPARE + /* Another possibility is that this machine has a compare insn + that includes the comparison code. In that case, ARG1 would + be equivalent to a comparison operation that would set ARG1 to + either STORE_FLAG_VALUE or zero. If this is an NE operation, + ORIG_CODE is the actual comparison being done; if it is an EQ, + we must reverse ORIG_CODE. On machine with a negative value + for STORE_FLAG_VALUE, also look at LT and GE operations. */ + || ((code == NE + || (code == LT + && val_signbit_known_set_p (inner_mode, + STORE_FLAG_VALUE)) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == LT + && SCALAR_FLOAT_MODE_P (inner_mode) + && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), + REAL_VALUE_NEGATIVE (fsfv))) +#endif + ) + && COMPARISON_P (p->exp))) + { + x = p->exp; + break; + } + else if ((code == EQ + || (code == GE + && val_signbit_known_set_p (inner_mode, + STORE_FLAG_VALUE)) +#ifdef FLOAT_STORE_FLAG_VALUE + || (code == GE + && SCALAR_FLOAT_MODE_P (inner_mode) + && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), + REAL_VALUE_NEGATIVE (fsfv))) +#endif + ) + && COMPARISON_P (p->exp)) + { + reverse_code = 1; + x = p->exp; + break; + } + + /* If this non-trapping address, e.g. fp + constant, the + equivalent is a better operand since it may let us predict + the value of the comparison. */ + else if (!rtx_addr_can_trap_p (p->exp)) + { + arg1 = p->exp; + continue; + } + } + + /* If we didn't find a useful equivalence for ARG1, we are done. + Otherwise, set up for the next iteration. */ + if (x == 0) + break; + + /* If we need to reverse the comparison, make sure that is + possible -- we can't necessarily infer the value of GE from LT + with floating-point operands. */ + if (reverse_code) + { + enum rtx_code reversed = reversed_comparison_code (x, NULL); + if (reversed == UNKNOWN) + break; + else + code = reversed; + } + else if (COMPARISON_P (x)) + code = GET_CODE (x); + arg1 = XEXP (x, 0), arg2 = XEXP (x, 1); + } + + /* Return our results. Return the modes from before fold_rtx + because fold_rtx might produce const_int, and then it's too late. */ + *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2); + *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0); + + if (visited) + delete visited; + return code; +} + +/* If X is a nontrivial arithmetic operation on an argument for which + a constant value can be determined, return the result of operating + on that value, as a constant. Otherwise, return X, possibly with + one or more operands changed to a forward-propagated constant. + + If X is a register whose contents are known, we do NOT return + those contents here; equiv_constant is called to perform that task. + For SUBREGs and MEMs, we do that both here and in equiv_constant. + + INSN is the insn that we may be modifying. If it is 0, make a copy + of X before modifying it. */ + +static rtx +fold_rtx (rtx x, rtx_insn *insn) +{ + enum rtx_code code; + machine_mode mode; + const char *fmt; + int i; + rtx new_rtx = 0; + int changed = 0; + poly_int64 xval; + + /* Operands of X. */ + /* Workaround -Wmaybe-uninitialized false positive during + profiledbootstrap by initializing them. */ + rtx folded_arg0 = NULL_RTX; + rtx folded_arg1 = NULL_RTX; + + /* Constant equivalents of first three operands of X; + 0 when no such equivalent is known. */ + rtx const_arg0; + rtx const_arg1; + rtx const_arg2; + + /* The mode of the first operand of X. We need this for sign and zero + extends. */ + machine_mode mode_arg0; + + if (x == 0) + return x; + + /* Try to perform some initial simplifications on X. */ + code = GET_CODE (x); + switch (code) + { + case MEM: + case SUBREG: + /* The first operand of a SIGN/ZERO_EXTRACT has a different meaning + than it would in other contexts. Basically its mode does not + signify the size of the object read. That information is carried + by size operand. If we happen to have a MEM of the appropriate + mode in our tables with a constant value we could simplify the + extraction incorrectly if we allowed substitution of that value + for the MEM. */ + case ZERO_EXTRACT: + case SIGN_EXTRACT: + if ((new_rtx = equiv_constant (x)) != NULL_RTX) + return new_rtx; + return x; + + case CONST: + CASE_CONST_ANY: + case SYMBOL_REF: + case LABEL_REF: + case REG: + case PC: + /* No use simplifying an EXPR_LIST + since they are used only for lists of args + in a function call's REG_EQUAL note. */ + case EXPR_LIST: + return x; + + case ASM_OPERANDS: + if (insn) + { + for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) + validate_change (insn, &ASM_OPERANDS_INPUT (x, i), + fold_rtx (ASM_OPERANDS_INPUT (x, i), insn), 0); + } + return x; + + case CALL: + if (NO_FUNCTION_CSE && CONSTANT_P (XEXP (XEXP (x, 0), 0))) + return x; + 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; + } + } + + /* Anything else goes through the loop below. */ + default: + break; + } + + mode = GET_MODE (x); + const_arg0 = 0; + const_arg1 = 0; + const_arg2 = 0; + mode_arg0 = VOIDmode; + + /* Try folding our operands. + Then see which ones have constant values known. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + rtx folded_arg = XEXP (x, i), const_arg; + machine_mode mode_arg = GET_MODE (folded_arg); + + switch (GET_CODE (folded_arg)) + { + case MEM: + case REG: + case SUBREG: + const_arg = equiv_constant (folded_arg); + break; + + case CONST: + CASE_CONST_ANY: + case SYMBOL_REF: + case LABEL_REF: + const_arg = folded_arg; + break; + + default: + folded_arg = fold_rtx (folded_arg, insn); + const_arg = equiv_constant (folded_arg); + break; + } + + /* For the first three operands, see if the operand + is constant or equivalent to a constant. */ + switch (i) + { + case 0: + folded_arg0 = folded_arg; + const_arg0 = const_arg; + mode_arg0 = mode_arg; + break; + case 1: + folded_arg1 = folded_arg; + const_arg1 = const_arg; + break; + case 2: + const_arg2 = const_arg; + break; + } + + /* Pick the least expensive of the argument and an equivalent constant + argument. */ + if (const_arg != 0 + && const_arg != folded_arg + && (COST_IN (const_arg, mode_arg, code, i) + <= COST_IN (folded_arg, mode_arg, code, i)) + + /* It's not safe to substitute the operand of a conversion + operator with a constant, as the conversion's identity + depends upon the mode of its operand. This optimization + is handled by the call to simplify_unary_operation. */ + && (GET_RTX_CLASS (code) != RTX_UNARY + || GET_MODE (const_arg) == mode_arg0 + || (code != ZERO_EXTEND + && code != SIGN_EXTEND + && code != TRUNCATE + && code != FLOAT_TRUNCATE + && code != FLOAT_EXTEND + && code != FLOAT + && code != FIX + && code != UNSIGNED_FLOAT + && code != UNSIGNED_FIX))) + folded_arg = const_arg; + + if (folded_arg == XEXP (x, i)) + continue; + + if (insn == NULL_RTX && !changed) + x = copy_rtx (x); + changed = 1; + validate_unshare_change (insn, &XEXP (x, i), folded_arg, 1); + } + + if (changed) + { + /* Canonicalize X if necessary, and keep const_argN and folded_argN + consistent with the order in X. */ + if (canonicalize_change_group (insn, x)) + { + std::swap (const_arg0, const_arg1); + std::swap (folded_arg0, folded_arg1); + } + + apply_change_group (); + } + + /* If X is an arithmetic operation, see if we can simplify it. */ + + switch (GET_RTX_CLASS (code)) + { + case RTX_UNARY: + { + /* We can't simplify extension ops unless we know the + original mode. */ + if ((code == ZERO_EXTEND || code == SIGN_EXTEND) + && mode_arg0 == VOIDmode) + break; + + new_rtx = simplify_unary_operation (code, mode, + const_arg0 ? const_arg0 : folded_arg0, + mode_arg0); + } + break; + + case RTX_COMPARE: + case RTX_COMM_COMPARE: + /* See what items are actually being compared and set FOLDED_ARG[01] + to those values and CODE to the actual comparison code. If any are + constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't + do anything if both operands are already known to be constant. */ + + /* ??? Vector mode comparisons are not supported yet. */ + if (VECTOR_MODE_P (mode)) + break; + + if (const_arg0 == 0 || const_arg1 == 0) + { + struct table_elt *p0, *p1; + rtx true_rtx, false_rtx; + machine_mode mode_arg1; + + if (SCALAR_FLOAT_MODE_P (mode)) + { +#ifdef FLOAT_STORE_FLAG_VALUE + true_rtx = (const_double_from_real_value + (FLOAT_STORE_FLAG_VALUE (mode), mode)); +#else + true_rtx = NULL_RTX; +#endif + false_rtx = CONST0_RTX (mode); + } + else + { + true_rtx = const_true_rtx; + false_rtx = const0_rtx; + } + + code = find_comparison_args (code, &folded_arg0, &folded_arg1, + &mode_arg0, &mode_arg1); + + /* If the mode is VOIDmode or a MODE_CC mode, we don't know + what kinds of things are being compared, so we can't do + anything with this comparison. */ + + if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC) + break; + + const_arg0 = equiv_constant (folded_arg0); + const_arg1 = equiv_constant (folded_arg1); + + /* If we do not now have two constants being compared, see + if we can nevertheless deduce some things about the + comparison. */ + if (const_arg0 == 0 || const_arg1 == 0) + { + if (const_arg1 != NULL) + { + rtx cheapest_simplification; + int cheapest_cost; + rtx simp_result; + struct table_elt *p; + + /* See if we can find an equivalent of folded_arg0 + that gets us a cheaper expression, possibly a + constant through simplifications. */ + p = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0), + mode_arg0); + + if (p != NULL) + { + cheapest_simplification = x; + cheapest_cost = COST (x, mode); + + for (p = p->first_same_value; p != NULL; p = p->next_same_value) + { + int cost; + + /* If the entry isn't valid, skip it. */ + if (! exp_equiv_p (p->exp, p->exp, 1, false)) + continue; + + /* Try to simplify using this equivalence. */ + simp_result + = simplify_relational_operation (code, mode, + mode_arg0, + p->exp, + const_arg1); + + if (simp_result == NULL) + continue; + + cost = COST (simp_result, mode); + if (cost < cheapest_cost) + { + cheapest_cost = cost; + cheapest_simplification = simp_result; + } + } + + /* If we have a cheaper expression now, use that + and try folding it further, from the top. */ + if (cheapest_simplification != x) + return fold_rtx (copy_rtx (cheapest_simplification), + insn); + } + } + + /* See if the two operands are the same. */ + + if ((REG_P (folded_arg0) + && REG_P (folded_arg1) + && (REG_QTY (REGNO (folded_arg0)) + == REG_QTY (REGNO (folded_arg1)))) + || ((p0 = lookup (folded_arg0, + SAFE_HASH (folded_arg0, mode_arg0), + mode_arg0)) + && (p1 = lookup (folded_arg1, + SAFE_HASH (folded_arg1, mode_arg0), + mode_arg0)) + && p0->first_same_value == p1->first_same_value)) + folded_arg1 = folded_arg0; + + /* If FOLDED_ARG0 is a register, see if the comparison we are + doing now is either the same as we did before or the reverse + (we only check the reverse if not floating-point). */ + else if (REG_P (folded_arg0)) + { + int qty = REG_QTY (REGNO (folded_arg0)); + + if (REGNO_QTY_VALID_P (REGNO (folded_arg0))) + { + struct qty_table_elem *ent = &qty_table[qty]; + + if ((comparison_dominates_p (ent->comparison_code, code) + || (! FLOAT_MODE_P (mode_arg0) + && comparison_dominates_p (ent->comparison_code, + reverse_condition (code)))) + && (rtx_equal_p (ent->comparison_const, folded_arg1) + || (const_arg1 + && rtx_equal_p (ent->comparison_const, + const_arg1)) + || (REG_P (folded_arg1) + && (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty)))) + { + if (comparison_dominates_p (ent->comparison_code, code)) + { + if (true_rtx) + return true_rtx; + else + break; + } + else + return false_rtx; + } + } + } + } + } + + /* If we are comparing against zero, see if the first operand is + equivalent to an IOR with a constant. If so, we may be able to + determine the result of this comparison. */ + if (const_arg1 == const0_rtx && !const_arg0) + { + rtx y = lookup_as_function (folded_arg0, IOR); + rtx inner_const; + + if (y != 0 + && (inner_const = equiv_constant (XEXP (y, 1))) != 0 + && CONST_INT_P (inner_const) + && INTVAL (inner_const) != 0) + folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const); + } + + { + rtx op0 = const_arg0 ? const_arg0 : copy_rtx (folded_arg0); + rtx op1 = const_arg1 ? const_arg1 : copy_rtx (folded_arg1); + new_rtx = simplify_relational_operation (code, mode, mode_arg0, + op0, op1); + } + break; + + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: + switch (code) + { + case PLUS: + /* If the second operand is a LABEL_REF, see if the first is a MINUS + with that LABEL_REF as its second operand. If so, the result is + the first operand of that MINUS. This handles switches with an + ADDR_DIFF_VEC table. */ + if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF) + { + rtx y + = GET_CODE (folded_arg0) == MINUS ? folded_arg0 + : lookup_as_function (folded_arg0, MINUS); + + if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF + && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg1)) + return XEXP (y, 0); + + /* Now try for a CONST of a MINUS like the above. */ + if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0 + : lookup_as_function (folded_arg0, CONST))) != 0 + && GET_CODE (XEXP (y, 0)) == MINUS + && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF + && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg1)) + return XEXP (XEXP (y, 0), 0); + } + + /* Likewise if the operands are in the other order. */ + if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF) + { + rtx y + = GET_CODE (folded_arg1) == MINUS ? folded_arg1 + : lookup_as_function (folded_arg1, MINUS); + + if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF + && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg0)) + return XEXP (y, 0); + + /* Now try for a CONST of a MINUS like the above. */ + if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1 + : lookup_as_function (folded_arg1, CONST))) != 0 + && GET_CODE (XEXP (y, 0)) == MINUS + && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF + && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg0)) + return XEXP (XEXP (y, 0), 0); + } + + /* If second operand is a register equivalent to a negative + CONST_INT, see if we can find a register equivalent to the + positive constant. Make a MINUS if so. Don't do this for + a non-negative constant since we might then alternate between + choosing positive and negative constants. Having the positive + constant previously-used is the more common case. Be sure + the resulting constant is non-negative; if const_arg1 were + the smallest negative number this would overflow: depending + on the mode, this would either just be the same value (and + hence not save anything) or be incorrect. */ + if (const_arg1 != 0 && CONST_INT_P (const_arg1) + && INTVAL (const_arg1) < 0 + /* This used to test + + -INTVAL (const_arg1) >= 0 + + But The Sun V5.0 compilers mis-compiled that test. So + instead we test for the problematic value in a more direct + manner and hope the Sun compilers get it correct. */ + && INTVAL (const_arg1) != + (HOST_WIDE_INT_1 << (HOST_BITS_PER_WIDE_INT - 1)) + && REG_P (folded_arg1)) + { + rtx new_const = GEN_INT (-INTVAL (const_arg1)); + struct table_elt *p + = lookup (new_const, SAFE_HASH (new_const, mode), mode); + + if (p) + for (p = p->first_same_value; p; p = p->next_same_value) + if (REG_P (p->exp)) + return simplify_gen_binary (MINUS, mode, folded_arg0, + canon_reg (p->exp, NULL)); + } + goto from_plus; + + case MINUS: + /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2). + If so, produce (PLUS Z C2-C). */ + if (const_arg1 != 0 && poly_int_rtx_p (const_arg1, &xval)) + { + rtx y = lookup_as_function (XEXP (x, 0), PLUS); + if (y && poly_int_rtx_p (XEXP (y, 1))) + return fold_rtx (plus_constant (mode, copy_rtx (y), -xval), + NULL); + } + + /* Fall through. */ + + from_plus: + case SMIN: case SMAX: case UMIN: case UMAX: + case IOR: case AND: case XOR: + case MULT: + case ASHIFT: case LSHIFTRT: case ASHIFTRT: + /* If we have (<op> <reg> <const_int>) for an associative OP and REG + is known to be of similar form, we may be able to replace the + operation with a combined operation. This may eliminate the + intermediate operation if every use is simplified in this way. + Note that the similar optimization done by combine.c only works + if the intermediate operation's result has only one reference. */ + + if (REG_P (folded_arg0) + && const_arg1 && CONST_INT_P (const_arg1)) + { + int is_shift + = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT); + rtx y, inner_const, new_const; + rtx canon_const_arg1 = const_arg1; + enum rtx_code associate_code; + + if (is_shift + && (INTVAL (const_arg1) >= GET_MODE_UNIT_PRECISION (mode) + || INTVAL (const_arg1) < 0)) + { + if (SHIFT_COUNT_TRUNCATED) + canon_const_arg1 = gen_int_shift_amount + (mode, (INTVAL (const_arg1) + & (GET_MODE_UNIT_BITSIZE (mode) - 1))); + else + break; + } + + y = lookup_as_function (folded_arg0, code); + if (y == 0) + break; + + /* If we have compiled a statement like + "if (x == (x & mask1))", and now are looking at + "x & mask2", we will have a case where the first operand + of Y is the same as our first operand. Unless we detect + this case, an infinite loop will result. */ + if (XEXP (y, 0) == folded_arg0) + break; + + inner_const = equiv_constant (fold_rtx (XEXP (y, 1), 0)); + if (!inner_const || !CONST_INT_P (inner_const)) + break; + + /* Don't associate these operations if they are a PLUS with the + same constant and it is a power of two. These might be doable + with a pre- or post-increment. Similarly for two subtracts of + identical powers of two with post decrement. */ + + if (code == PLUS && const_arg1 == inner_const + && ((HAVE_PRE_INCREMENT + && pow2p_hwi (INTVAL (const_arg1))) + || (HAVE_POST_INCREMENT + && pow2p_hwi (INTVAL (const_arg1))) + || (HAVE_PRE_DECREMENT + && pow2p_hwi (- INTVAL (const_arg1))) + || (HAVE_POST_DECREMENT + && pow2p_hwi (- INTVAL (const_arg1))))) + break; + + /* ??? Vector mode shifts by scalar + shift operand are not supported yet. */ + if (is_shift && VECTOR_MODE_P (mode)) + break; + + if (is_shift + && (INTVAL (inner_const) >= GET_MODE_UNIT_PRECISION (mode) + || INTVAL (inner_const) < 0)) + { + if (SHIFT_COUNT_TRUNCATED) + inner_const = gen_int_shift_amount + (mode, (INTVAL (inner_const) + & (GET_MODE_UNIT_BITSIZE (mode) - 1))); + else + break; + } + + /* Compute the code used to compose the constants. For example, + A-C1-C2 is A-(C1 + C2), so if CODE == MINUS, we want PLUS. */ + + associate_code = (is_shift || code == MINUS ? PLUS : code); + + new_const = simplify_binary_operation (associate_code, mode, + canon_const_arg1, + inner_const); + + if (new_const == 0) + break; + + /* If we are associating shift operations, don't let this + produce a shift of the size of the object or larger. + This could occur when we follow a sign-extend by a right + shift on a machine that does a sign-extend as a pair + of shifts. */ + + if (is_shift + && CONST_INT_P (new_const) + && INTVAL (new_const) >= GET_MODE_UNIT_PRECISION (mode)) + { + /* As an exception, we can turn an ASHIFTRT of this + form into a shift of the number of bits - 1. */ + if (code == ASHIFTRT) + new_const = gen_int_shift_amount + (mode, GET_MODE_UNIT_BITSIZE (mode) - 1); + else if (!side_effects_p (XEXP (y, 0))) + return CONST0_RTX (mode); + else + break; + } + + y = copy_rtx (XEXP (y, 0)); + + /* If Y contains our first operand (the most common way this + can happen is if Y is a MEM), we would do into an infinite + loop if we tried to fold it. So don't in that case. */ + + if (! reg_mentioned_p (folded_arg0, y)) + y = fold_rtx (y, insn); + + return simplify_gen_binary (code, mode, y, new_const); + } + break; + + case DIV: case UDIV: + /* ??? The associative optimization performed immediately above is + also possible for DIV and UDIV using associate_code of MULT. + However, we would need extra code to verify that the + multiplication does not overflow, that is, there is no overflow + in the calculation of new_const. */ + break; + + default: + break; + } + + new_rtx = simplify_binary_operation (code, mode, + const_arg0 ? const_arg0 : folded_arg0, + const_arg1 ? const_arg1 : folded_arg1); + break; + + case RTX_OBJ: + /* (lo_sum (high X) X) is simply X. */ + if (code == LO_SUM && const_arg0 != 0 + && GET_CODE (const_arg0) == HIGH + && rtx_equal_p (XEXP (const_arg0, 0), const_arg1)) + return const_arg1; + break; + + case RTX_TERNARY: + case RTX_BITFIELD_OPS: + new_rtx = simplify_ternary_operation (code, mode, mode_arg0, + const_arg0 ? const_arg0 : folded_arg0, + const_arg1 ? const_arg1 : folded_arg1, + const_arg2 ? const_arg2 : XEXP (x, 2)); + break; + + default: + break; + } + + return new_rtx ? new_rtx : x; +} + +/* Return a constant value currently equivalent to X. + Return 0 if we don't know one. */ + +static rtx +equiv_constant (rtx x) +{ + if (REG_P (x) + && REGNO_QTY_VALID_P (REGNO (x))) + { + int x_q = REG_QTY (REGNO (x)); + struct qty_table_elem *x_ent = &qty_table[x_q]; + + if (x_ent->const_rtx) + x = gen_lowpart (GET_MODE (x), x_ent->const_rtx); + } + + if (x == 0 || CONSTANT_P (x)) + return x; + + if (GET_CODE (x) == SUBREG) + { + machine_mode mode = GET_MODE (x); + machine_mode imode = GET_MODE (SUBREG_REG (x)); + rtx new_rtx; + + /* See if we previously assigned a constant value to this SUBREG. */ + if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0 + || (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0 + || (NUM_POLY_INT_COEFFS > 1 + && (new_rtx = lookup_as_function (x, CONST_POLY_INT)) != 0) + || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0 + || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0) + return new_rtx; + + /* If we didn't and if doing so makes sense, see if we previously + assigned a constant value to the enclosing word mode SUBREG. */ + if (known_lt (GET_MODE_SIZE (mode), UNITS_PER_WORD) + && known_lt (UNITS_PER_WORD, GET_MODE_SIZE (imode))) + { + poly_int64 byte = (SUBREG_BYTE (x) + - subreg_lowpart_offset (mode, word_mode)); + if (known_ge (byte, 0) && multiple_p (byte, UNITS_PER_WORD)) + { + rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte); + new_rtx = lookup_as_function (y, CONST_INT); + if (new_rtx) + return gen_lowpart (mode, new_rtx); + } + } + + /* Otherwise see if we already have a constant for the inner REG, + and if that is enough to calculate an equivalent constant for + the subreg. Note that the upper bits of paradoxical subregs + are undefined, so they cannot be said to equal anything. */ + if (REG_P (SUBREG_REG (x)) + && !paradoxical_subreg_p (x) + && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0) + return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x)); + + return 0; + } + + /* If X is a MEM, see if it is a constant-pool reference, or look it up in + the hash table in case its value was seen before. */ + + if (MEM_P (x)) + { + struct table_elt *elt; + + x = avoid_constant_pool_reference (x); + if (CONSTANT_P (x)) + return x; + + elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x)); + if (elt == 0) + return 0; + + for (elt = elt->first_same_value; elt; elt = elt->next_same_value) + if (elt->is_const && CONSTANT_P (elt->exp)) + return elt->exp; + } + + return 0; +} + +/* Given INSN, a jump insn, TAKEN indicates if we are following the + "taken" branch. + + In certain cases, this can cause us to add an equivalence. For example, + if we are following the taken case of + if (i == 2) + we can add the fact that `i' and '2' are now equivalent. + + In any case, we can record that this comparison was passed. If the same + comparison is seen later, we will know its value. */ + +static void +record_jump_equiv (rtx_insn *insn, bool taken) +{ + int cond_known_true; + rtx op0, op1; + rtx set; + machine_mode mode, mode0, mode1; + int reversed_nonequality = 0; + enum rtx_code code; + + /* Ensure this is the right kind of insn. */ + gcc_assert (any_condjump_p (insn)); + + set = pc_set (insn); + + /* See if this jump condition is known true or false. */ + if (taken) + cond_known_true = (XEXP (SET_SRC (set), 2) == pc_rtx); + else + cond_known_true = (XEXP (SET_SRC (set), 1) == pc_rtx); + + /* Get the type of comparison being done and the operands being compared. + If we had to reverse a non-equality condition, record that fact so we + know that it isn't valid for floating-point. */ + code = GET_CODE (XEXP (SET_SRC (set), 0)); + op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn); + op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn); + + /* If fold_rtx returns NULL_RTX, there's nothing to record. */ + if (op0 == NULL_RTX || op1 == NULL_RTX) + return; + + code = find_comparison_args (code, &op0, &op1, &mode0, &mode1); + if (! cond_known_true) + { + code = reversed_comparison_code_parts (code, op0, op1, insn); + + /* Don't remember if we can't find the inverse. */ + if (code == UNKNOWN) + return; + } + + /* The mode is the mode of the non-constant. */ + mode = mode0; + if (mode1 != VOIDmode) + mode = mode1; + + record_jump_cond (code, mode, op0, op1, reversed_nonequality); +} + +/* Yet another form of subreg creation. In this case, we want something in + MODE, and we should assume OP has MODE iff it is naturally modeless. */ + +static rtx +record_jump_cond_subreg (machine_mode mode, rtx op) +{ + machine_mode op_mode = GET_MODE (op); + if (op_mode == mode || op_mode == VOIDmode) + return op; + return lowpart_subreg (mode, op, op_mode); +} + +/* We know that comparison CODE applied to OP0 and OP1 in MODE is true. + REVERSED_NONEQUALITY is nonzero if CODE had to be swapped. + Make any useful entries we can with that information. Called from + above function and called recursively. */ + +static void +record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0, + rtx op1, int reversed_nonequality) +{ + unsigned op0_hash, op1_hash; + int op0_in_memory, op1_in_memory; + struct table_elt *op0_elt, *op1_elt; + + /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG, + we know that they are also equal in the smaller mode (this is also + true for all smaller modes whether or not there is a SUBREG, but + is not worth testing for with no SUBREG). */ + + /* Note that GET_MODE (op0) may not equal MODE. */ + if (code == EQ && paradoxical_subreg_p (op0)) + { + machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); + rtx tem = record_jump_cond_subreg (inner_mode, op1); + if (tem) + record_jump_cond (code, mode, SUBREG_REG (op0), tem, + reversed_nonequality); + } + + if (code == EQ && paradoxical_subreg_p (op1)) + { + machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); + rtx tem = record_jump_cond_subreg (inner_mode, op0); + if (tem) + record_jump_cond (code, mode, SUBREG_REG (op1), tem, + reversed_nonequality); + } + + /* Similarly, if this is an NE comparison, and either is a SUBREG + making a smaller mode, we know the whole thing is also NE. */ + + /* Note that GET_MODE (op0) may not equal MODE; + if we test MODE instead, we can get an infinite recursion + alternating between two modes each wider than MODE. */ + + if (code == NE + && partial_subreg_p (op0) + && subreg_lowpart_p (op0)) + { + machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); + rtx tem = record_jump_cond_subreg (inner_mode, op1); + if (tem) + record_jump_cond (code, mode, SUBREG_REG (op0), tem, + reversed_nonequality); + } + + if (code == NE + && partial_subreg_p (op1) + && subreg_lowpart_p (op1)) + { + machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); + rtx tem = record_jump_cond_subreg (inner_mode, op0); + if (tem) + record_jump_cond (code, mode, SUBREG_REG (op1), tem, + reversed_nonequality); + } + + /* Hash both operands. */ + + do_not_record = 0; + hash_arg_in_memory = 0; + op0_hash = HASH (op0, mode); + op0_in_memory = hash_arg_in_memory; + + if (do_not_record) + return; + + do_not_record = 0; + hash_arg_in_memory = 0; + op1_hash = HASH (op1, mode); + op1_in_memory = hash_arg_in_memory; + + if (do_not_record) + return; + + /* Look up both operands. */ + op0_elt = lookup (op0, op0_hash, mode); + op1_elt = lookup (op1, op1_hash, mode); + + /* If both operands are already equivalent or if they are not in the + table but are identical, do nothing. */ + if ((op0_elt != 0 && op1_elt != 0 + && op0_elt->first_same_value == op1_elt->first_same_value) + || op0 == op1 || rtx_equal_p (op0, op1)) + return; + + /* If we aren't setting two things equal all we can do is save this + comparison. Similarly if this is floating-point. In the latter + case, OP1 might be zero and both -0.0 and 0.0 are equal to it. + If we record the equality, we might inadvertently delete code + whose intent was to change -0 to +0. */ + + if (code != EQ || FLOAT_MODE_P (GET_MODE (op0))) + { + struct qty_table_elem *ent; + int qty; + + /* If we reversed a floating-point comparison, if OP0 is not a + register, or if OP1 is neither a register or constant, we can't + do anything. */ + + if (!REG_P (op1)) + op1 = equiv_constant (op1); + + if ((reversed_nonequality && FLOAT_MODE_P (mode)) + || !REG_P (op0) || op1 == 0) + return; + + /* Put OP0 in the hash table if it isn't already. This gives it a + new quantity number. */ + if (op0_elt == 0) + { + if (insert_regs (op0, NULL, 0)) + { + rehash_using_reg (op0); + op0_hash = HASH (op0, mode); + + /* If OP0 is contained in OP1, this changes its hash code + as well. Faster to rehash than to check, except + for the simple case of a constant. */ + if (! CONSTANT_P (op1)) + op1_hash = HASH (op1,mode); + } + + op0_elt = insert (op0, NULL, op0_hash, mode); + op0_elt->in_memory = op0_in_memory; + } + + qty = REG_QTY (REGNO (op0)); + ent = &qty_table[qty]; + + ent->comparison_code = code; + if (REG_P (op1)) + { + /* Look it up again--in case op0 and op1 are the same. */ + op1_elt = lookup (op1, op1_hash, mode); + + /* Put OP1 in the hash table so it gets a new quantity number. */ + if (op1_elt == 0) + { + if (insert_regs (op1, NULL, 0)) + { + rehash_using_reg (op1); + op1_hash = HASH (op1, mode); + } + + op1_elt = insert (op1, NULL, op1_hash, mode); + op1_elt->in_memory = op1_in_memory; + } + + ent->comparison_const = NULL_RTX; + ent->comparison_qty = REG_QTY (REGNO (op1)); + } + else + { + ent->comparison_const = op1; + ent->comparison_qty = -1; + } + + return; + } + + /* If either side is still missing an equivalence, make it now, + then merge the equivalences. */ + + if (op0_elt == 0) + { + if (insert_regs (op0, NULL, 0)) + { + rehash_using_reg (op0); + op0_hash = HASH (op0, mode); + } + + op0_elt = insert (op0, NULL, op0_hash, mode); + op0_elt->in_memory = op0_in_memory; + } + + if (op1_elt == 0) + { + if (insert_regs (op1, NULL, 0)) + { + rehash_using_reg (op1); + op1_hash = HASH (op1, mode); + } + + op1_elt = insert (op1, NULL, op1_hash, mode); + op1_elt->in_memory = op1_in_memory; + } + + merge_equiv_classes (op0_elt, op1_elt); +} + +/* CSE processing for one instruction. + + Most "true" common subexpressions are mostly optimized away in GIMPLE, + but the few that "leak through" are cleaned up by cse_insn, and complex + addressing modes are often formed here. + + The main function is cse_insn, and between here and that function + a couple of helper functions is defined to keep the size of cse_insn + within reasonable proportions. + + Data is shared between the main and helper functions via STRUCT SET, + that contains all data related for every set in the instruction that + is being processed. + + Note that cse_main processes all sets in the instruction. Most + passes in GCC only process simple SET insns or single_set insns, but + CSE processes insns with multiple sets as well. */ + +/* Data on one SET contained in the instruction. */ + +struct set +{ + /* The SET rtx itself. */ + rtx rtl; + /* The SET_SRC of the rtx (the original value, if it is changing). */ + rtx src; + /* The hash-table element for the SET_SRC of the SET. */ + struct table_elt *src_elt; + /* Hash value for the SET_SRC. */ + unsigned src_hash; + /* Hash value for the SET_DEST. */ + unsigned dest_hash; + /* The SET_DEST, with SUBREG, etc., stripped. */ + rtx inner_dest; + /* Nonzero if the SET_SRC is in memory. */ + char src_in_memory; + /* Nonzero if the SET_SRC contains something + whose value cannot be predicted and understood. */ + char src_volatile; + /* Original machine mode, in case it becomes a CONST_INT. + The size of this field should match the size of the mode + field of struct rtx_def (see rtl.h). */ + ENUM_BITFIELD(machine_mode) mode : 8; + /* Hash value of constant equivalent for SET_SRC. */ + unsigned src_const_hash; + /* A constant equivalent for SET_SRC, if any. */ + rtx src_const; + /* Table entry for constant equivalent for SET_SRC, if any. */ + struct table_elt *src_const_elt; + /* Table entry for the destination address. */ + struct table_elt *dest_addr_elt; +}; + +/* Special handling for (set REG0 REG1) where REG0 is the + "cheapest", cheaper than REG1. After cse, REG1 will probably not + be used in the sequel, so (if easily done) change this insn to + (set REG1 REG0) and replace REG1 with REG0 in the previous insn + that computed their value. Then REG1 will become a dead store + and won't cloud the situation for later optimizations. + + Do not make this change if REG1 is a hard register, because it will + then be used in the sequel and we may be changing a two-operand insn + into a three-operand insn. + + This is the last transformation that cse_insn will try to do. */ + +static void +try_back_substitute_reg (rtx set, rtx_insn *insn) +{ + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + + if (REG_P (dest) + && REG_P (src) && ! HARD_REGISTER_P (src) + && REGNO_QTY_VALID_P (REGNO (src))) + { + int src_q = REG_QTY (REGNO (src)); + struct qty_table_elem *src_ent = &qty_table[src_q]; + + if (src_ent->first_reg == REGNO (dest)) + { + /* Scan for the previous nonnote insn, but stop at a basic + block boundary. */ + rtx_insn *prev = insn; + rtx_insn *bb_head = BB_HEAD (BLOCK_FOR_INSN (insn)); + do + { + prev = PREV_INSN (prev); + } + while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev))); + + /* Do not swap the registers around if the previous instruction + attaches a REG_EQUIV note to REG1. + + ??? It's not entirely clear whether we can transfer a REG_EQUIV + from the pseudo that originally shadowed an incoming argument + to another register. Some uses of REG_EQUIV might rely on it + being attached to REG1 rather than REG2. + + This section previously turned the REG_EQUIV into a REG_EQUAL + note. We cannot do that because REG_EQUIV may provide an + uninitialized stack slot when REG_PARM_STACK_SPACE is used. */ + if (NONJUMP_INSN_P (prev) + && GET_CODE (PATTERN (prev)) == SET + && SET_DEST (PATTERN (prev)) == src + && ! find_reg_note (prev, REG_EQUIV, NULL_RTX)) + { + rtx note; + + validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1); + validate_change (insn, &SET_DEST (set), src, 1); + validate_change (insn, &SET_SRC (set), dest, 1); + apply_change_group (); + + /* If INSN has a REG_EQUAL note, and this note mentions + REG0, then we must delete it, because the value in + REG0 has changed. If the note's value is REG1, we must + also delete it because that is now this insn's dest. */ + note = find_reg_note (insn, REG_EQUAL, NULL_RTX); + if (note != 0 + && (reg_mentioned_p (dest, XEXP (note, 0)) + || rtx_equal_p (src, XEXP (note, 0)))) + remove_note (insn, note); + + /* If INSN has a REG_ARGS_SIZE note, move it to PREV. */ + note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); + if (note != 0) + { + remove_note (insn, note); + gcc_assert (!find_reg_note (prev, REG_ARGS_SIZE, NULL_RTX)); + set_unique_reg_note (prev, REG_ARGS_SIZE, XEXP (note, 0)); + } + } + } + } +} + +/* Add an entry containing RTL X into SETS. */ +static inline void +add_to_set (vec<struct set> *sets, rtx x) +{ + struct set entry = {}; + entry.rtl = x; + sets->safe_push (entry); +} + +/* Record all the SETs in this instruction into SETS_PTR, + and return the number of recorded sets. */ +static int +find_sets_in_insn (rtx_insn *insn, vec<struct set> *psets) +{ + rtx x = PATTERN (insn); + + if (GET_CODE (x) == SET) + { + /* Ignore SETs that are unconditional jumps. + They never need cse processing, so this does not hurt. + The reason is not efficiency but rather + so that we can test at the end for instructions + that have been simplified to unconditional jumps + and not be misled by unchanged instructions + that were unconditional jumps to begin with. */ + if (SET_DEST (x) == pc_rtx + && GET_CODE (SET_SRC (x)) == LABEL_REF) + ; + /* Don't count call-insns, (set (reg 0) (call ...)), as a set. + The hard function value register is used only once, to copy to + someplace else, so it isn't worth cse'ing. */ + else if (GET_CODE (SET_SRC (x)) == CALL) + ; + else if (GET_CODE (SET_SRC (x)) == CONST_VECTOR + && GET_MODE_CLASS (GET_MODE (SET_SRC (x))) != MODE_VECTOR_BOOL + /* Prevent duplicates from being generated if the type is a V1 + type and a subreg. Folding this will result in the same + element as folding x itself. */ + && !(SUBREG_P (SET_DEST (x)) + && known_eq (GET_MODE_NUNITS (GET_MODE (SET_SRC (x))), 1))) + { + /* First register the vector itself. */ + add_to_set (psets, x); + rtx src = SET_SRC (x); + /* Go over the constants of the CONST_VECTOR in forward order, to + put them in the same order in the SETS array. */ + for (unsigned i = 0; i < const_vector_encoded_nelts (src) ; i++) + { + /* These are templates and don't actually get emitted but are + used to tell CSE how to get to a particular constant. */ + rtx y = simplify_gen_vec_select (SET_DEST (x), i); + gcc_assert (y); + add_to_set (psets, gen_rtx_SET (y, CONST_VECTOR_ELT (src, i))); + } + } + else + add_to_set (psets, x); + } + else if (GET_CODE (x) == PARALLEL) + { + int i, lim = XVECLEN (x, 0); + + /* Go over the expressions of the PARALLEL in forward order, to + put them in the same order in the SETS array. */ + for (i = 0; i < lim; i++) + { + rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == SET) + { + /* As above, we ignore unconditional jumps and call-insns and + ignore the result of apply_change_group. */ + if (SET_DEST (y) == pc_rtx + && GET_CODE (SET_SRC (y)) == LABEL_REF) + ; + else if (GET_CODE (SET_SRC (y)) == CALL) + ; + else + add_to_set (psets, y); + } + } + } + + return psets->length (); +} + +/* Subroutine of canonicalize_insn. X is an ASM_OPERANDS in INSN. */ + +static void +canon_asm_operands (rtx x, rtx_insn *insn) +{ + for (int i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) + { + rtx input = ASM_OPERANDS_INPUT (x, i); + if (!(REG_P (input) && HARD_REGISTER_P (input))) + { + input = canon_reg (input, insn); + validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1); + } + } +} + +/* Where possible, substitute every register reference in the N_SETS + number of SETS in INSN with the canonical register. + + Register canonicalization propagatest the earliest register (i.e. + one that is set before INSN) with the same value. This is a very + useful, simple form of CSE, to clean up warts from expanding GIMPLE + to RTL. For instance, a CONST for an address is usually expanded + multiple times to loads into different registers, thus creating many + subexpressions of the form: + + (set (reg1) (some_const)) + (set (mem (... reg1 ...) (thing))) + (set (reg2) (some_const)) + (set (mem (... reg2 ...) (thing))) + + After canonicalizing, the code takes the following form: + + (set (reg1) (some_const)) + (set (mem (... reg1 ...) (thing))) + (set (reg2) (some_const)) + (set (mem (... reg1 ...) (thing))) + + The set to reg2 is now trivially dead, and the memory reference (or + address, or whatever) may be a candidate for further CSEing. + + In this function, the result of apply_change_group can be ignored; + see canon_reg. */ + +static void +canonicalize_insn (rtx_insn *insn, vec<struct set> *psets) +{ + vec<struct set> sets = *psets; + int n_sets = sets.length (); + rtx tem; + rtx x = PATTERN (insn); + int i; + + if (CALL_P (insn)) + { + for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) + if (GET_CODE (XEXP (tem, 0)) != SET) + XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn); + } + + if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL) + { + canon_reg (SET_SRC (x), insn); + apply_change_group (); + fold_rtx (SET_SRC (x), insn); + } + else if (GET_CODE (x) == CLOBBER) + { + /* If we clobber memory, canon the address. + This does nothing when a register is clobbered + because we have already invalidated the reg. */ + if (MEM_P (XEXP (x, 0))) + canon_reg (XEXP (x, 0), insn); + } + else if (GET_CODE (x) == USE + && ! (REG_P (XEXP (x, 0)) + && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) + /* Canonicalize a USE of a pseudo register or memory location. */ + canon_reg (x, insn); + else if (GET_CODE (x) == ASM_OPERANDS) + canon_asm_operands (x, insn); + else if (GET_CODE (x) == CALL) + { + canon_reg (x, insn); + apply_change_group (); + fold_rtx (x, insn); + } + else if (DEBUG_INSN_P (insn)) + canon_reg (PATTERN (insn), insn); + else if (GET_CODE (x) == PARALLEL) + { + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL) + { + canon_reg (SET_SRC (y), insn); + apply_change_group (); + fold_rtx (SET_SRC (y), insn); + } + else if (GET_CODE (y) == CLOBBER) + { + if (MEM_P (XEXP (y, 0))) + canon_reg (XEXP (y, 0), insn); + } + else if (GET_CODE (y) == USE + && ! (REG_P (XEXP (y, 0)) + && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) + canon_reg (y, insn); + else if (GET_CODE (y) == ASM_OPERANDS) + canon_asm_operands (y, insn); + else if (GET_CODE (y) == CALL) + { + canon_reg (y, insn); + apply_change_group (); + fold_rtx (y, insn); + } + } + } + + if (n_sets == 1 && REG_NOTES (insn) != 0 + && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) + { + /* We potentially will process this insn many times. Therefore, + drop the REG_EQUAL note if it is equal to the SET_SRC of the + unique set in INSN. + + Do not do so if the REG_EQUAL note is for a STRICT_LOW_PART, + because cse_insn handles those specially. */ + if (GET_CODE (SET_DEST (sets[0].rtl)) != STRICT_LOW_PART + && rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))) + remove_note (insn, tem); + else + { + canon_reg (XEXP (tem, 0), insn); + apply_change_group (); + XEXP (tem, 0) = fold_rtx (XEXP (tem, 0), insn); + df_notes_rescan (insn); + } + } + + /* Canonicalize sources and addresses of destinations. + We do this in a separate pass to avoid problems when a MATCH_DUP is + present in the insn pattern. In that case, we want to ensure that + we don't break the duplicate nature of the pattern. So we will replace + both operands at the same time. Otherwise, we would fail to find an + equivalent substitution in the loop calling validate_change below. + + We used to suppress canonicalization of DEST if it appears in SRC, + but we don't do this any more. */ + + for (i = 0; i < n_sets; i++) + { + rtx dest = SET_DEST (sets[i].rtl); + rtx src = SET_SRC (sets[i].rtl); + rtx new_rtx = canon_reg (src, insn); + + validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); + + if (GET_CODE (dest) == ZERO_EXTRACT) + { + validate_change (insn, &XEXP (dest, 1), + canon_reg (XEXP (dest, 1), insn), 1); + validate_change (insn, &XEXP (dest, 2), + canon_reg (XEXP (dest, 2), insn), 1); + } + + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + + if (MEM_P (dest)) + canon_reg (dest, insn); + } + + /* Now that we have done all the replacements, we can apply the change + group and see if they all work. Note that this will cause some + canonicalizations that would have worked individually not to be applied + because some other canonicalization didn't work, but this should not + occur often. + + The result of apply_change_group can be ignored; see canon_reg. */ + + apply_change_group (); +} + +/* Main function of CSE. + First simplify sources and addresses of all assignments + in the instruction, using previously-computed equivalents values. + Then install the new sources and destinations in the table + of available values. */ + +static void +cse_insn (rtx_insn *insn) +{ + rtx x = PATTERN (insn); + int i; + rtx tem; + int n_sets = 0; + + rtx src_eqv = 0; + struct table_elt *src_eqv_elt = 0; + int src_eqv_volatile = 0; + int src_eqv_in_memory = 0; + unsigned src_eqv_hash = 0; + + this_insn = insn; + + /* Find all regs explicitly clobbered in this insn, + to ensure they are not replaced with any other regs + elsewhere in this insn. */ + invalidate_from_sets_and_clobbers (insn); + + /* Record all the SETs in this instruction. */ + auto_vec<struct set, 8> sets; + n_sets = find_sets_in_insn (insn, (vec<struct set>*)&sets); + + /* Substitute the canonical register where possible. */ + canonicalize_insn (insn, (vec<struct set>*)&sets); + + /* If this insn has a REG_EQUAL note, store the equivalent value in SRC_EQV, + if different, or if the DEST is a STRICT_LOW_PART/ZERO_EXTRACT. The + latter condition is necessary because SRC_EQV is handled specially for + this case, and if it isn't set, then there will be no equivalence + for the destination. */ + if (n_sets == 1 && REG_NOTES (insn) != 0 + && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) + { + + if (GET_CODE (SET_DEST (sets[0].rtl)) != ZERO_EXTRACT + && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)) + || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART)) + src_eqv = copy_rtx (XEXP (tem, 0)); + /* If DEST is of the form ZERO_EXTACT, as in: + (set (zero_extract:SI (reg:SI 119) + (const_int 16 [0x10]) + (const_int 16 [0x10])) + (const_int 51154 [0xc7d2])) + REG_EQUAL note will specify the value of register (reg:SI 119) at this + point. Note that this is different from SRC_EQV. We can however + calculate SRC_EQV with the position and width of ZERO_EXTRACT. */ + else if (GET_CODE (SET_DEST (sets[0].rtl)) == ZERO_EXTRACT + && CONST_INT_P (XEXP (tem, 0)) + && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 1)) + && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 2))) + { + rtx dest_reg = XEXP (SET_DEST (sets[0].rtl), 0); + /* This is the mode of XEXP (tem, 0) as well. */ + scalar_int_mode dest_mode + = as_a <scalar_int_mode> (GET_MODE (dest_reg)); + rtx width = XEXP (SET_DEST (sets[0].rtl), 1); + rtx pos = XEXP (SET_DEST (sets[0].rtl), 2); + HOST_WIDE_INT val = INTVAL (XEXP (tem, 0)); + HOST_WIDE_INT mask; + unsigned int shift; + if (BITS_BIG_ENDIAN) + shift = (GET_MODE_PRECISION (dest_mode) + - INTVAL (pos) - INTVAL (width)); + else + shift = INTVAL (pos); + if (INTVAL (width) == HOST_BITS_PER_WIDE_INT) + mask = HOST_WIDE_INT_M1; + else + mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1; + val = (val >> shift) & mask; + src_eqv = GEN_INT (val); + } + } + + /* Set sets[i].src_elt to the class each source belongs to. + Detect assignments from or to volatile things + and set set[i] to zero so they will be ignored + in the rest of this function. + + Nothing in this loop changes the hash table or the register chains. */ + + for (i = 0; i < n_sets; i++) + { + bool repeat = false; + bool noop_insn = false; + rtx src, dest; + rtx src_folded; + struct table_elt *elt = 0, *p; + machine_mode mode; + rtx src_eqv_here; + rtx src_const = 0; + rtx src_related = 0; + bool src_related_is_const_anchor = false; + struct table_elt *src_const_elt = 0; + int src_cost = MAX_COST; + int src_eqv_cost = MAX_COST; + int src_folded_cost = MAX_COST; + int src_related_cost = MAX_COST; + int src_elt_cost = MAX_COST; + int src_regcost = MAX_COST; + int src_eqv_regcost = MAX_COST; + int src_folded_regcost = MAX_COST; + int src_related_regcost = MAX_COST; + int src_elt_regcost = MAX_COST; + scalar_int_mode int_mode; + + dest = SET_DEST (sets[i].rtl); + src = SET_SRC (sets[i].rtl); + + /* If SRC is a constant that has no machine mode, + hash it with the destination's machine mode. + This way we can keep different modes separate. */ + + mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + sets[i].mode = mode; + + if (src_eqv) + { + machine_mode eqvmode = mode; + if (GET_CODE (dest) == STRICT_LOW_PART) + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + do_not_record = 0; + hash_arg_in_memory = 0; + src_eqv_hash = HASH (src_eqv, eqvmode); + + /* Find the equivalence class for the equivalent expression. */ + + if (!do_not_record) + src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode); + + src_eqv_volatile = do_not_record; + src_eqv_in_memory = hash_arg_in_memory; + } + + /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the + value of the INNER register, not the destination. So it is not + a valid substitution for the source. But save it for later. */ + if (GET_CODE (dest) == STRICT_LOW_PART) + src_eqv_here = 0; + else + src_eqv_here = src_eqv; + + /* Simplify and foldable subexpressions in SRC. Then get the fully- + simplified result, which may not necessarily be valid. */ + src_folded = fold_rtx (src, NULL); + +#if 0 + /* ??? This caused bad code to be generated for the m68k port with -O2. + Suppose src is (CONST_INT -1), and that after truncation src_folded + is (CONST_INT 3). Suppose src_folded is then used for src_const. + At the end we will add src and src_const to the same equivalence + class. We now have 3 and -1 on the same equivalence class. This + causes later instructions to be mis-optimized. */ + /* If storing a constant in a bitfield, pre-truncate the constant + so we will be able to record it later. */ + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT) + { + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + + if (CONST_INT_P (src) + && CONST_INT_P (width) + && INTVAL (width) < HOST_BITS_PER_WIDE_INT + && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) + src_folded + = GEN_INT (INTVAL (src) & ((HOST_WIDE_INT_1 + << INTVAL (width)) - 1)); + } +#endif + + /* Compute SRC's hash code, and also notice if it + should not be recorded at all. In that case, + prevent any further processing of this assignment. + + We set DO_NOT_RECORD if the destination has a REG_UNUSED note. + This avoids getting the source register into the tables, where it + may be invalidated later (via REG_QTY), then trigger an ICE upon + re-insertion. + + This is only a problem in multi-set insns. If it were a single + set the dead copy would have been removed. If the RHS were anything + but a simple REG, then we won't call insert_regs and thus there's + no potential for triggering the ICE. */ + do_not_record = (REG_P (dest) + && REG_P (src) + && find_reg_note (insn, REG_UNUSED, dest)); + hash_arg_in_memory = 0; + + sets[i].src = src; + sets[i].src_hash = HASH (src, mode); + sets[i].src_volatile = do_not_record; + sets[i].src_in_memory = hash_arg_in_memory; + + /* If SRC is a MEM, there is a REG_EQUIV note for SRC, and DEST is + a pseudo, do not record SRC. Using SRC as a replacement for + anything else will be incorrect in that situation. Note that + this usually occurs only for stack slots, in which case all the + RTL would be referring to SRC, so we don't lose any optimization + opportunities by not having SRC in the hash table. */ + + if (MEM_P (src) + && find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0 + && REG_P (dest) + && REGNO (dest) >= FIRST_PSEUDO_REGISTER) + sets[i].src_volatile = 1; + + else if (GET_CODE (src) == ASM_OPERANDS + && GET_CODE (x) == PARALLEL) + { + /* Do not record result of a non-volatile inline asm with + more than one result. */ + if (n_sets > 1) + sets[i].src_volatile = 1; + + int j, lim = XVECLEN (x, 0); + for (j = 0; j < lim; j++) + { + rtx y = XVECEXP (x, 0, j); + /* And do not record result of a non-volatile inline asm + with "memory" clobber. */ + if (GET_CODE (y) == CLOBBER && MEM_P (XEXP (y, 0))) + { + sets[i].src_volatile = 1; + break; + } + } + } + +#if 0 + /* It is no longer clear why we used to do this, but it doesn't + appear to still be needed. So let's try without it since this + code hurts cse'ing widened ops. */ + /* If source is a paradoxical subreg (such as QI treated as an SI), + treat it as volatile. It may do the work of an SI in one context + where the extra bits are not being used, but cannot replace an SI + in general. */ + if (paradoxical_subreg_p (src)) + sets[i].src_volatile = 1; +#endif + + /* Locate all possible equivalent forms for SRC. Try to replace + SRC in the insn with each cheaper equivalent. + + We have the following types of equivalents: SRC itself, a folded + version, a value given in a REG_EQUAL note, or a value related + to a constant. + + Each of these equivalents may be part of an additional class + of equivalents (if more than one is in the table, they must be in + the same class; we check for this). + + If the source is volatile, we don't do any table lookups. + + We note any constant equivalent for possible later use in a + REG_NOTE. */ + + if (!sets[i].src_volatile) + elt = lookup (src, sets[i].src_hash, mode); + + sets[i].src_elt = elt; + + if (elt && src_eqv_here && src_eqv_elt) + { + if (elt->first_same_value != src_eqv_elt->first_same_value) + { + /* The REG_EQUAL is indicating that two formerly distinct + classes are now equivalent. So merge them. */ + merge_equiv_classes (elt, src_eqv_elt); + src_eqv_hash = HASH (src_eqv, elt->mode); + src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode); + } + + src_eqv_here = 0; + } + + else if (src_eqv_elt) + elt = src_eqv_elt; + + /* Try to find a constant somewhere and record it in `src_const'. + Record its table element, if any, in `src_const_elt'. Look in + any known equivalences first. (If the constant is not in the + table, also set `sets[i].src_const_hash'). */ + if (elt) + for (p = elt->first_same_value; p; p = p->next_same_value) + if (p->is_const) + { + src_const = p->exp; + src_const_elt = elt; + break; + } + + if (src_const == 0 + && (CONSTANT_P (src_folded) + /* Consider (minus (label_ref L1) (label_ref L2)) as + "constant" here so we will record it. This allows us + to fold switch statements when an ADDR_DIFF_VEC is used. */ + || (GET_CODE (src_folded) == MINUS + && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF + && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF))) + src_const = src_folded, src_const_elt = elt; + else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here)) + src_const = src_eqv_here, src_const_elt = src_eqv_elt; + + /* If we don't know if the constant is in the table, get its + hash code and look it up. */ + if (src_const && src_const_elt == 0) + { + sets[i].src_const_hash = HASH (src_const, mode); + src_const_elt = lookup (src_const, sets[i].src_const_hash, mode); + } + + sets[i].src_const = src_const; + sets[i].src_const_elt = src_const_elt; + + /* If the constant and our source are both in the table, mark them as + equivalent. Otherwise, if a constant is in the table but the source + isn't, set ELT to it. */ + if (src_const_elt && elt + && src_const_elt->first_same_value != elt->first_same_value) + merge_equiv_classes (elt, src_const_elt); + else if (src_const_elt && elt == 0) + elt = src_const_elt; + + /* See if there is a register linearly related to a constant + equivalent of SRC. */ + if (src_const + && (GET_CODE (src_const) == CONST + || (src_const_elt && src_const_elt->related_value != 0))) + { + src_related = use_related_value (src_const, src_const_elt); + if (src_related) + { + struct table_elt *src_related_elt + = lookup (src_related, HASH (src_related, mode), mode); + if (src_related_elt && elt) + { + if (elt->first_same_value + != src_related_elt->first_same_value) + /* This can occur when we previously saw a CONST + involving a SYMBOL_REF and then see the SYMBOL_REF + twice. Merge the involved classes. */ + merge_equiv_classes (elt, src_related_elt); + + src_related = 0; + src_related_elt = 0; + } + else if (src_related_elt && elt == 0) + elt = src_related_elt; + } + } + + /* See if we have a CONST_INT that is already in a register in a + wider mode. */ + + if (src_const && src_related == 0 && CONST_INT_P (src_const) + && is_int_mode (mode, &int_mode) + && GET_MODE_PRECISION (int_mode) < BITS_PER_WORD) + { + opt_scalar_int_mode wider_mode_iter; + FOR_EACH_WIDER_MODE (wider_mode_iter, int_mode) + { + scalar_int_mode wider_mode = wider_mode_iter.require (); + if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD) + break; + + struct table_elt *const_elt + = lookup (src_const, HASH (src_const, wider_mode), wider_mode); + + if (const_elt == 0) + continue; + + for (const_elt = const_elt->first_same_value; + const_elt; const_elt = const_elt->next_same_value) + if (REG_P (const_elt->exp)) + { + src_related = gen_lowpart (int_mode, const_elt->exp); + break; + } + + if (src_related != 0) + break; + } + } + + /* Another possibility is that we have an AND with a constant in + a mode narrower than a word. If so, it might have been generated + as part of an "if" which would narrow the AND. If we already + have done the AND in a wider mode, we can use a SUBREG of that + value. */ + + if (flag_expensive_optimizations && ! src_related + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1)) + && GET_MODE_SIZE (int_mode) < UNITS_PER_WORD) + { + opt_scalar_int_mode tmode_iter; + rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1)); + + FOR_EACH_WIDER_MODE (tmode_iter, int_mode) + { + scalar_int_mode tmode = tmode_iter.require (); + if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD) + break; + + rtx inner = gen_lowpart (tmode, XEXP (src, 0)); + struct table_elt *larger_elt; + + if (inner) + { + PUT_MODE (new_and, tmode); + XEXP (new_and, 0) = inner; + larger_elt = lookup (new_and, HASH (new_and, tmode), tmode); + if (larger_elt == 0) + continue; + + for (larger_elt = larger_elt->first_same_value; + larger_elt; larger_elt = larger_elt->next_same_value) + if (REG_P (larger_elt->exp)) + { + src_related + = gen_lowpart (int_mode, larger_elt->exp); + break; + } + + if (src_related) + break; + } + } + } + + /* See if a MEM has already been loaded with a widening operation; + if it has, we can use a subreg of that. Many CISC machines + also have such operations, but this is only likely to be + beneficial on these machines. */ + + rtx_code extend_op; + if (flag_expensive_optimizations && src_related == 0 + && MEM_P (src) && ! do_not_record + && is_a <scalar_int_mode> (mode, &int_mode) + && (extend_op = load_extend_op (int_mode)) != UNKNOWN) + { + struct rtx_def memory_extend_buf; + rtx memory_extend_rtx = &memory_extend_buf; + + /* Set what we are trying to extend and the operation it might + have been extended with. */ + memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx)); + PUT_CODE (memory_extend_rtx, extend_op); + XEXP (memory_extend_rtx, 0) = src; + + opt_scalar_int_mode tmode_iter; + FOR_EACH_WIDER_MODE (tmode_iter, int_mode) + { + struct table_elt *larger_elt; + + scalar_int_mode tmode = tmode_iter.require (); + if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD) + break; + + PUT_MODE (memory_extend_rtx, tmode); + larger_elt = lookup (memory_extend_rtx, + HASH (memory_extend_rtx, tmode), tmode); + if (larger_elt == 0) + continue; + + for (larger_elt = larger_elt->first_same_value; + larger_elt; larger_elt = larger_elt->next_same_value) + if (REG_P (larger_elt->exp)) + { + src_related = gen_lowpart (int_mode, larger_elt->exp); + break; + } + + if (src_related) + break; + } + } + + /* Try to express the constant using a register+offset expression + derived from a constant anchor. */ + + if (targetm.const_anchor + && !src_related + && src_const + && GET_CODE (src_const) == CONST_INT) + { + src_related = try_const_anchors (src_const, mode); + src_related_is_const_anchor = src_related != NULL_RTX; + } + + /* Try to re-materialize a vec_dup with an existing constant. */ + rtx src_elt; + if ((!src_eqv_here || CONSTANT_P (src_eqv_here)) + && const_vec_duplicate_p (src, &src_elt)) + { + machine_mode const_mode = GET_MODE_INNER (GET_MODE (src)); + struct table_elt *related_elt + = lookup (src_elt, HASH (src_elt, const_mode), const_mode); + if (related_elt) + { + for (related_elt = related_elt->first_same_value; + related_elt; related_elt = related_elt->next_same_value) + if (REG_P (related_elt->exp)) + { + /* We don't need to compare costs with an existing (constant) + src_eqv_here, since any such src_eqv_here should already be + available in src_const. */ + src_eqv_here + = gen_rtx_VEC_DUPLICATE (GET_MODE (src), + related_elt->exp); + break; + } + } + } + + if (src == src_folded) + src_folded = 0; + + /* At this point, ELT, if nonzero, points to a class of expressions + equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED, + and SRC_RELATED, if nonzero, each contain additional equivalent + expressions. Prune these latter expressions by deleting expressions + already in the equivalence class. + + Check for an equivalent identical to the destination. If found, + this is the preferred equivalent since it will likely lead to + elimination of the insn. Indicate this by placing it in + `src_related'. */ + + if (elt) + elt = elt->first_same_value; + for (p = elt; p; p = p->next_same_value) + { + enum rtx_code code = GET_CODE (p->exp); + + /* If the expression is not valid, ignore it. Then we do not + have to check for validity below. In most cases, we can use + `rtx_equal_p', since canonicalization has already been done. */ + if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false)) + continue; + + /* Also skip paradoxical subregs, unless that's what we're + looking for. */ + if (paradoxical_subreg_p (p->exp) + && ! (src != 0 + && GET_CODE (src) == SUBREG + && GET_MODE (src) == GET_MODE (p->exp) + && partial_subreg_p (GET_MODE (SUBREG_REG (src)), + GET_MODE (SUBREG_REG (p->exp))))) + continue; + + if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp)) + src = 0; + else if (src_folded && GET_CODE (src_folded) == code + && rtx_equal_p (src_folded, p->exp)) + src_folded = 0; + else if (src_eqv_here && GET_CODE (src_eqv_here) == code + && rtx_equal_p (src_eqv_here, p->exp)) + src_eqv_here = 0; + else if (src_related && GET_CODE (src_related) == code + && rtx_equal_p (src_related, p->exp)) + src_related = 0; + + /* This is the same as the destination of the insns, we want + to prefer it. Copy it to src_related. The code below will + then give it a negative cost. */ + if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest)) + src_related = p->exp; + } + + /* Find the cheapest valid equivalent, trying all the available + possibilities. Prefer items not in the hash table to ones + that are when they are equal cost. Note that we can never + worsen an insn as the current contents will also succeed. + If we find an equivalent identical to the destination, use it as best, + since this insn will probably be eliminated in that case. */ + if (src) + { + if (rtx_equal_p (src, dest)) + src_cost = src_regcost = -1; + else + { + src_cost = COST (src, mode); + src_regcost = approx_reg_cost (src); + } + } + + if (src_eqv_here) + { + if (rtx_equal_p (src_eqv_here, dest)) + src_eqv_cost = src_eqv_regcost = -1; + else + { + src_eqv_cost = COST (src_eqv_here, mode); + src_eqv_regcost = approx_reg_cost (src_eqv_here); + } + } + + if (src_folded) + { + if (rtx_equal_p (src_folded, dest)) + src_folded_cost = src_folded_regcost = -1; + else + { + src_folded_cost = COST (src_folded, mode); + src_folded_regcost = approx_reg_cost (src_folded); + } + } + + if (src_related) + { + if (rtx_equal_p (src_related, dest)) + src_related_cost = src_related_regcost = -1; + else + { + src_related_cost = COST (src_related, mode); + src_related_regcost = approx_reg_cost (src_related); + + /* If a const-anchor is used to synthesize a constant that + normally requires multiple instructions then slightly prefer + it over the original sequence. These instructions are likely + to become redundant now. We can't compare against the cost + of src_eqv_here because, on MIPS for example, multi-insn + constants have zero cost; they are assumed to be hoisted from + loops. */ + if (src_related_is_const_anchor + && src_related_cost == src_cost + && src_eqv_here) + src_related_cost--; + } + } + + /* If this was an indirect jump insn, a known label will really be + cheaper even though it looks more expensive. */ + if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF) + src_folded = src_const, src_folded_cost = src_folded_regcost = -1; + + /* Terminate loop when replacement made. This must terminate since + the current contents will be tested and will always be valid. */ + while (1) + { + rtx trial; + + /* Skip invalid entries. */ + while (elt && !REG_P (elt->exp) + && ! exp_equiv_p (elt->exp, elt->exp, 1, false)) + elt = elt->next_same_value; + + /* A paradoxical subreg would be bad here: it'll be the right + size, but later may be adjusted so that the upper bits aren't + what we want. So reject it. */ + if (elt != 0 + && paradoxical_subreg_p (elt->exp) + /* It is okay, though, if the rtx we're trying to match + will ignore any of the bits we can't predict. */ + && ! (src != 0 + && GET_CODE (src) == SUBREG + && GET_MODE (src) == GET_MODE (elt->exp) + && partial_subreg_p (GET_MODE (SUBREG_REG (src)), + GET_MODE (SUBREG_REG (elt->exp))))) + { + elt = elt->next_same_value; + continue; + } + + if (elt) + { + src_elt_cost = elt->cost; + src_elt_regcost = elt->regcost; + } + + /* Find cheapest and skip it for the next time. For items + of equal cost, use this order: + src_folded, src, src_eqv, src_related and hash table entry. */ + if (src_folded + && preferable (src_folded_cost, src_folded_regcost, + src_cost, src_regcost) <= 0 + && preferable (src_folded_cost, src_folded_regcost, + src_eqv_cost, src_eqv_regcost) <= 0 + && preferable (src_folded_cost, src_folded_regcost, + src_related_cost, src_related_regcost) <= 0 + && preferable (src_folded_cost, src_folded_regcost, + src_elt_cost, src_elt_regcost) <= 0) + trial = src_folded, src_folded_cost = MAX_COST; + else if (src + && preferable (src_cost, src_regcost, + src_eqv_cost, src_eqv_regcost) <= 0 + && preferable (src_cost, src_regcost, + src_related_cost, src_related_regcost) <= 0 + && preferable (src_cost, src_regcost, + src_elt_cost, src_elt_regcost) <= 0) + trial = src, src_cost = MAX_COST; + else if (src_eqv_here + && preferable (src_eqv_cost, src_eqv_regcost, + src_related_cost, src_related_regcost) <= 0 + && preferable (src_eqv_cost, src_eqv_regcost, + src_elt_cost, src_elt_regcost) <= 0) + trial = src_eqv_here, src_eqv_cost = MAX_COST; + else if (src_related + && preferable (src_related_cost, src_related_regcost, + src_elt_cost, src_elt_regcost) <= 0) + trial = src_related, src_related_cost = MAX_COST; + else + { + trial = elt->exp; + elt = elt->next_same_value; + src_elt_cost = MAX_COST; + } + + /* Try to optimize + (set (reg:M N) (const_int A)) + (set (reg:M2 O) (const_int B)) + (set (zero_extract:M2 (reg:M N) (const_int C) (const_int D)) + (reg:M2 O)). */ + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT + && CONST_INT_P (trial) + && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1)) + && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2)) + && REG_P (XEXP (SET_DEST (sets[i].rtl), 0)) + && (known_ge + (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl))), + INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)))) + && ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)) + + (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2)) + <= HOST_BITS_PER_WIDE_INT)) + { + rtx dest_reg = XEXP (SET_DEST (sets[i].rtl), 0); + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + rtx pos = XEXP (SET_DEST (sets[i].rtl), 2); + unsigned int dest_hash = HASH (dest_reg, GET_MODE (dest_reg)); + struct table_elt *dest_elt + = lookup (dest_reg, dest_hash, GET_MODE (dest_reg)); + rtx dest_cst = NULL; + + if (dest_elt) + for (p = dest_elt->first_same_value; p; p = p->next_same_value) + if (p->is_const && CONST_INT_P (p->exp)) + { + dest_cst = p->exp; + break; + } + if (dest_cst) + { + HOST_WIDE_INT val = INTVAL (dest_cst); + HOST_WIDE_INT mask; + unsigned int shift; + /* This is the mode of DEST_CST as well. */ + scalar_int_mode dest_mode + = as_a <scalar_int_mode> (GET_MODE (dest_reg)); + if (BITS_BIG_ENDIAN) + shift = GET_MODE_PRECISION (dest_mode) + - INTVAL (pos) - INTVAL (width); + else + shift = INTVAL (pos); + if (INTVAL (width) == HOST_BITS_PER_WIDE_INT) + mask = HOST_WIDE_INT_M1; + else + mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1; + val &= ~(mask << shift); + val |= (INTVAL (trial) & mask) << shift; + val = trunc_int_for_mode (val, dest_mode); + validate_unshare_change (insn, &SET_DEST (sets[i].rtl), + dest_reg, 1); + validate_unshare_change (insn, &SET_SRC (sets[i].rtl), + GEN_INT (val), 1); + if (apply_change_group ()) + { + rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); + if (note) + { + remove_note (insn, note); + df_notes_rescan (insn); + } + src_eqv = NULL_RTX; + src_eqv_elt = NULL; + src_eqv_volatile = 0; + src_eqv_in_memory = 0; + src_eqv_hash = 0; + repeat = true; + break; + } + } + } + + /* We don't normally have an insn matching (set (pc) (pc)), so + check for this separately here. We will delete such an + insn below. + + For other cases such as a table jump or conditional jump + where we know the ultimate target, go ahead and replace the + operand. While that may not make a valid insn, we will + reemit the jump below (and also insert any necessary + barriers). */ + if (n_sets == 1 && dest == pc_rtx + && (trial == pc_rtx + || (GET_CODE (trial) == LABEL_REF + && ! condjump_p (insn)))) + { + /* Don't substitute non-local labels, this confuses CFG. */ + if (GET_CODE (trial) == LABEL_REF + && LABEL_REF_NONLOCAL_P (trial)) + continue; + + SET_SRC (sets[i].rtl) = trial; + cse_jumps_altered = true; + break; + } + + /* Similarly, lots of targets don't allow no-op + (set (mem x) (mem x)) moves. Even (set (reg x) (reg x)) + might be impossible for certain registers (like CC registers). */ + else if (n_sets == 1 + && !CALL_P (insn) + && (MEM_P (trial) || REG_P (trial)) + && rtx_equal_p (trial, dest) + && !side_effects_p (dest) + && (cfun->can_delete_dead_exceptions + || insn_nothrow_p (insn)) + /* We can only remove the later store if the earlier aliases + at least all accesses the later one. */ + && (!MEM_P (trial) + || ((MEM_ALIAS_SET (dest) == MEM_ALIAS_SET (trial) + || alias_set_subset_of (MEM_ALIAS_SET (dest), + MEM_ALIAS_SET (trial))) + && (!MEM_EXPR (trial) + || refs_same_for_tbaa_p (MEM_EXPR (trial), + MEM_EXPR (dest)))))) + { + SET_SRC (sets[i].rtl) = trial; + noop_insn = true; + break; + } + + /* Reject certain invalid forms of CONST that we create. */ + else if (CONSTANT_P (trial) + && GET_CODE (trial) == CONST + /* Reject cases that will cause decode_rtx_const to + die. On the alpha when simplifying a switch, we + get (const (truncate (minus (label_ref) + (label_ref)))). */ + && (GET_CODE (XEXP (trial, 0)) == TRUNCATE + /* Likewise on IA-64, except without the + truncate. */ + || (GET_CODE (XEXP (trial, 0)) == MINUS + && GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF))) + /* Do nothing for this case. */ + ; + + /* Do not replace anything with a MEM, except the replacement + is a no-op. This allows this loop to terminate. */ + else if (MEM_P (trial) && !rtx_equal_p (trial, SET_SRC(sets[i].rtl))) + /* Do nothing for this case. */ + ; + + /* Look for a substitution that makes a valid insn. */ + else if (validate_unshare_change (insn, &SET_SRC (sets[i].rtl), + trial, 0)) + { + rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn); + + /* The result of apply_change_group can be ignored; see + canon_reg. */ + + validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); + apply_change_group (); + + break; + } + + /* If the current function uses a constant pool and this is a + constant, try making a pool entry. Put it in src_folded + unless we already have done this since that is where it + likely came from. */ + + else if (crtl->uses_const_pool + && CONSTANT_P (trial) + && !CONST_INT_P (trial) + && (src_folded == 0 || !MEM_P (src_folded)) + && GET_MODE_CLASS (mode) != MODE_CC + && mode != VOIDmode) + { + src_folded = force_const_mem (mode, trial); + if (src_folded) + { + src_folded_cost = COST (src_folded, mode); + src_folded_regcost = approx_reg_cost (src_folded); + } + } + } + + /* If we changed the insn too much, handle this set from scratch. */ + if (repeat) + { + i--; + continue; + } + + src = SET_SRC (sets[i].rtl); + + /* In general, it is good to have a SET with SET_SRC == SET_DEST. + However, there is an important exception: If both are registers + that are not the head of their equivalence class, replace SET_SRC + with the head of the class. If we do not do this, we will have + both registers live over a portion of the basic block. This way, + their lifetimes will likely abut instead of overlapping. */ + if (REG_P (dest) + && REGNO_QTY_VALID_P (REGNO (dest))) + { + int dest_q = REG_QTY (REGNO (dest)); + struct qty_table_elem *dest_ent = &qty_table[dest_q]; + + if (dest_ent->mode == GET_MODE (dest) + && dest_ent->first_reg != REGNO (dest) + && REG_P (src) && REGNO (src) == REGNO (dest) + /* Don't do this if the original insn had a hard reg as + SET_SRC or SET_DEST. */ + && (!REG_P (sets[i].src) + || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER) + && (!REG_P (dest) || REGNO (dest) >= FIRST_PSEUDO_REGISTER)) + /* We can't call canon_reg here because it won't do anything if + SRC is a hard register. */ + { + int src_q = REG_QTY (REGNO (src)); + struct qty_table_elem *src_ent = &qty_table[src_q]; + int first = src_ent->first_reg; + rtx new_src + = (first >= FIRST_PSEUDO_REGISTER + ? regno_reg_rtx[first] : gen_rtx_REG (GET_MODE (src), first)); + + /* We must use validate-change even for this, because this + might be a special no-op instruction, suitable only to + tag notes onto. */ + if (validate_change (insn, &SET_SRC (sets[i].rtl), new_src, 0)) + { + src = new_src; + /* If we had a constant that is cheaper than what we are now + setting SRC to, use that constant. We ignored it when we + thought we could make this into a no-op. */ + if (src_const && COST (src_const, mode) < COST (src, mode) + && validate_change (insn, &SET_SRC (sets[i].rtl), + src_const, 0)) + src = src_const; + } + } + } + + /* If we made a change, recompute SRC values. */ + if (src != sets[i].src) + { + do_not_record = 0; + hash_arg_in_memory = 0; + sets[i].src = src; + sets[i].src_hash = HASH (src, mode); + sets[i].src_volatile = do_not_record; + sets[i].src_in_memory = hash_arg_in_memory; + sets[i].src_elt = lookup (src, sets[i].src_hash, mode); + } + + /* If this is a single SET, we are setting a register, and we have an + equivalent constant, we want to add a REG_EQUAL note if the constant + is different from the source. We don't want to do it for a constant + pseudo since verifying that this pseudo hasn't been eliminated is a + pain; moreover such a note won't help anything. + + Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF))) + which can be created for a reference to a compile time computable + entry in a jump table. */ + if (n_sets == 1 + && REG_P (dest) + && src_const + && !REG_P (src_const) + && !(GET_CODE (src_const) == SUBREG + && REG_P (SUBREG_REG (src_const))) + && !(GET_CODE (src_const) == CONST + && GET_CODE (XEXP (src_const, 0)) == MINUS + && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF) + && !rtx_equal_p (src, src_const)) + { + /* Make sure that the rtx is not shared. */ + src_const = copy_rtx (src_const); + + /* Record the actual constant value in a REG_EQUAL note, + making a new one if one does not already exist. */ + set_unique_reg_note (insn, REG_EQUAL, src_const); + df_notes_rescan (insn); + } + + /* Now deal with the destination. */ + do_not_record = 0; + + /* Look within any ZERO_EXTRACT to the MEM or REG within it. */ + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + + sets[i].inner_dest = dest; + + if (MEM_P (dest)) + { +#ifdef PUSH_ROUNDING + /* Stack pushes invalidate the stack pointer. */ + rtx addr = XEXP (dest, 0); + if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC + && XEXP (addr, 0) == stack_pointer_rtx) + invalidate (stack_pointer_rtx, VOIDmode); +#endif + dest = fold_rtx (dest, insn); + } + + /* Compute the hash code of the destination now, + before the effects of this instruction are recorded, + since the register values used in the address computation + are those before this instruction. */ + sets[i].dest_hash = HASH (dest, mode); + + /* Don't enter a bit-field in the hash table + because the value in it after the store + may not equal what was stored, due to truncation. */ + + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT) + { + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + + if (src_const != 0 && CONST_INT_P (src_const) + && CONST_INT_P (width) + && INTVAL (width) < HOST_BITS_PER_WIDE_INT + && ! (INTVAL (src_const) + & (HOST_WIDE_INT_M1U << INTVAL (width)))) + /* Exception: if the value is constant, + and it won't be truncated, record it. */ + ; + else + { + /* This is chosen so that the destination will be invalidated + but no new value will be recorded. + We must invalidate because sometimes constant + values can be recorded for bitfields. */ + sets[i].src_elt = 0; + sets[i].src_volatile = 1; + src_eqv = 0; + src_eqv_elt = 0; + } + } + + /* If only one set in a JUMP_INSN and it is now a no-op, we can delete + the insn. */ + else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx) + { + /* One less use of the label this insn used to jump to. */ + cse_cfg_altered |= delete_insn_and_edges (insn); + cse_jumps_altered = true; + /* No more processing for this set. */ + sets[i].rtl = 0; + } + + /* Similarly for no-op moves. */ + else if (noop_insn) + { + if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn)) + cse_cfg_altered = true; + cse_cfg_altered |= delete_insn_and_edges (insn); + /* No more processing for this set. */ + sets[i].rtl = 0; + } + + /* If this SET is now setting PC to a label, we know it used to + be a conditional or computed branch. */ + else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF + && !LABEL_REF_NONLOCAL_P (src)) + { + /* We reemit the jump in as many cases as possible just in + case the form of an unconditional jump is significantly + different than a computed jump or conditional jump. + + If this insn has multiple sets, then reemitting the + jump is nontrivial. So instead we just force rerecognition + and hope for the best. */ + if (n_sets == 1) + { + rtx_jump_insn *new_rtx; + rtx note; + + rtx_insn *seq = targetm.gen_jump (XEXP (src, 0)); + new_rtx = emit_jump_insn_before (seq, insn); + JUMP_LABEL (new_rtx) = XEXP (src, 0); + LABEL_NUSES (XEXP (src, 0))++; + + /* Make sure to copy over REG_NON_LOCAL_GOTO. */ + note = find_reg_note (insn, REG_NON_LOCAL_GOTO, 0); + if (note) + { + XEXP (note, 1) = NULL_RTX; + REG_NOTES (new_rtx) = note; + } + + cse_cfg_altered |= delete_insn_and_edges (insn); + insn = new_rtx; + } + else + INSN_CODE (insn) = -1; + + /* Do not bother deleting any unreachable code, let jump do it. */ + cse_jumps_altered = true; + sets[i].rtl = 0; + } + + /* If destination is volatile, invalidate it and then do no further + processing for this assignment. */ + + else if (do_not_record) + { + invalidate_dest (dest); + sets[i].rtl = 0; + } + + if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) + { + do_not_record = 0; + sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode); + if (do_not_record) + { + invalidate_dest (SET_DEST (sets[i].rtl)); + sets[i].rtl = 0; + } + } + } + + /* Now enter all non-volatile source expressions in the hash table + if they are not already present. + Record their equivalence classes in src_elt. + This way we can insert the corresponding destinations into + the same classes even if the actual sources are no longer in them + (having been invalidated). */ + + if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile + && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl))) + { + struct table_elt *elt; + struct table_elt *classp = sets[0].src_elt; + rtx dest = SET_DEST (sets[0].rtl); + machine_mode eqvmode = GET_MODE (dest); + + if (GET_CODE (dest) == STRICT_LOW_PART) + { + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + classp = 0; + } + if (insert_regs (src_eqv, classp, 0)) + { + rehash_using_reg (src_eqv); + src_eqv_hash = HASH (src_eqv, eqvmode); + } + elt = insert (src_eqv, classp, src_eqv_hash, eqvmode); + elt->in_memory = src_eqv_in_memory; + src_eqv_elt = elt; + + /* Check to see if src_eqv_elt is the same as a set source which + does not yet have an elt, and if so set the elt of the set source + to src_eqv_elt. */ + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && sets[i].src_elt == 0 + && rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv)) + sets[i].src_elt = src_eqv_elt; + } + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && ! sets[i].src_volatile + && ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) + { + if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) + { + /* REG_EQUAL in setting a STRICT_LOW_PART + gives an equivalent for the entire destination register, + not just for the subreg being stored in now. + This is a more interesting equivalence, so we arrange later + to treat the entire reg as the destination. */ + sets[i].src_elt = src_eqv_elt; + sets[i].src_hash = src_eqv_hash; + } + else + { + /* Insert source and constant equivalent into hash table, if not + already present. */ + struct table_elt *classp = src_eqv_elt; + rtx src = sets[i].src; + rtx dest = SET_DEST (sets[i].rtl); + machine_mode mode + = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + + /* It's possible that we have a source value known to be + constant but don't have a REG_EQUAL note on the insn. + Lack of a note will mean src_eqv_elt will be NULL. This + can happen where we've generated a SUBREG to access a + CONST_INT that is already in a register in a wider mode. + Ensure that the source expression is put in the proper + constant class. */ + if (!classp) + classp = sets[i].src_const_elt; + + if (sets[i].src_elt == 0) + { + struct table_elt *elt; + + /* Note that these insert_regs calls cannot remove + any of the src_elt's, because they would have failed to + match if not still valid. */ + if (insert_regs (src, classp, 0)) + { + rehash_using_reg (src); + sets[i].src_hash = HASH (src, mode); + } + elt = insert (src, classp, sets[i].src_hash, mode); + elt->in_memory = sets[i].src_in_memory; + /* If inline asm has any clobbers, ensure we only reuse + existing inline asms and never try to put the ASM_OPERANDS + into an insn that isn't inline asm. */ + if (GET_CODE (src) == ASM_OPERANDS + && GET_CODE (x) == PARALLEL) + elt->cost = MAX_COST; + sets[i].src_elt = classp = elt; + } + if (sets[i].src_const && sets[i].src_const_elt == 0 + && src != sets[i].src_const + && ! rtx_equal_p (sets[i].src_const, src)) + sets[i].src_elt = insert (sets[i].src_const, classp, + sets[i].src_const_hash, mode); + } + } + else if (sets[i].src_elt == 0) + /* If we did not insert the source into the hash table (e.g., it was + volatile), note the equivalence class for the REG_EQUAL value, if any, + so that the destination goes into that class. */ + sets[i].src_elt = src_eqv_elt; + + /* Record destination addresses in the hash table. This allows us to + check if they are invalidated by other sets. */ + for (i = 0; i < n_sets; i++) + { + if (sets[i].rtl) + { + rtx x = sets[i].inner_dest; + struct table_elt *elt; + machine_mode mode; + unsigned hash; + + if (MEM_P (x)) + { + x = XEXP (x, 0); + mode = GET_MODE (x); + hash = HASH (x, mode); + elt = lookup (x, hash, mode); + if (!elt) + { + if (insert_regs (x, NULL, 0)) + { + rtx dest = SET_DEST (sets[i].rtl); + + rehash_using_reg (x); + hash = HASH (x, mode); + sets[i].dest_hash = HASH (dest, GET_MODE (dest)); + } + elt = insert (x, NULL, hash, mode); + } + + sets[i].dest_addr_elt = elt; + } + else + sets[i].dest_addr_elt = NULL; + } + } + + invalidate_from_clobbers (insn); + + /* Some registers are invalidated by subroutine calls. Memory is + invalidated by non-constant calls. */ + + if (CALL_P (insn)) + { + if (!(RTL_CONST_OR_PURE_CALL_P (insn))) + invalidate_memory (); + else + /* For const/pure calls, invalidate any argument slots, because + those are owned by the callee. */ + for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) + if (GET_CODE (XEXP (tem, 0)) == USE + && MEM_P (XEXP (XEXP (tem, 0), 0))) + invalidate (XEXP (XEXP (tem, 0), 0), VOIDmode); + invalidate_for_call (insn); + } + + /* Now invalidate everything set by this instruction. + If a SUBREG or other funny destination is being set, + sets[i].rtl is still nonzero, so here we invalidate the reg + a part of which is being set. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + /* We can't use the inner dest, because the mode associated with + a ZERO_EXTRACT is significant. */ + rtx dest = SET_DEST (sets[i].rtl); + + /* Needed for registers to remove the register from its + previous quantity's chain. + Needed for memory if this is a nonvarying address, unless + we have just done an invalidate_memory that covers even those. */ + if (REG_P (dest) || GET_CODE (dest) == SUBREG) + invalidate (dest, VOIDmode); + else if (MEM_P (dest)) + invalidate (dest, VOIDmode); + else if (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == ZERO_EXTRACT) + invalidate (XEXP (dest, 0), GET_MODE (dest)); + } + + /* Don't cse over a call to setjmp; on some machines (eg VAX) + the regs restored by the longjmp come from a later time + than the setjmp. */ + if (CALL_P (insn) && find_reg_note (insn, REG_SETJMP, NULL)) + { + flush_hash_table (); + goto done; + } + + /* Make sure registers mentioned in destinations + are safe for use in an expression to be inserted. + This removes from the hash table + any invalid entry that refers to one of these registers. + + We don't care about the return value from mention_regs because + we are going to hash the SET_DEST values unconditionally. */ + + for (i = 0; i < n_sets; i++) + { + if (sets[i].rtl) + { + rtx x = SET_DEST (sets[i].rtl); + + if (!REG_P (x)) + mention_regs (x); + else + { + /* We used to rely on all references to a register becoming + inaccessible when a register changes to a new quantity, + since that changes the hash code. However, that is not + safe, since after HASH_SIZE new quantities we get a + hash 'collision' of a register with its own invalid + entries. And since SUBREGs have been changed not to + change their hash code with the hash code of the register, + it wouldn't work any longer at all. So we have to check + for any invalid references lying around now. + This code is similar to the REG case in mention_regs, + but it knows that reg_tick has been incremented, and + it leaves reg_in_table as -1 . */ + unsigned int regno = REGNO (x); + unsigned int endregno = END_REGNO (x); + unsigned int i; + + for (i = regno; i < endregno; i++) + { + if (REG_IN_TABLE (i) >= 0) + { + remove_invalid_refs (i); + REG_IN_TABLE (i) = -1; + } + } + } + } + } + + /* We may have just removed some of the src_elt's from the hash table. + So replace each one with the current head of the same class. + Also check if destination addresses have been removed. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + if (sets[i].dest_addr_elt + && sets[i].dest_addr_elt->first_same_value == 0) + { + /* The elt was removed, which means this destination is not + valid after this instruction. */ + sets[i].rtl = NULL_RTX; + } + else if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) + /* If elt was removed, find current head of same class, + or 0 if nothing remains of that class. */ + { + struct table_elt *elt = sets[i].src_elt; + + while (elt && elt->prev_same_value) + elt = elt->prev_same_value; + + while (elt && elt->first_same_value == 0) + elt = elt->next_same_value; + sets[i].src_elt = elt ? elt->first_same_value : 0; + } + } + + /* Now insert the destinations into their equivalence classes. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + rtx dest = SET_DEST (sets[i].rtl); + struct table_elt *elt; + + /* Don't record value if we are not supposed to risk allocating + floating-point values in registers that might be wider than + memory. */ + if ((flag_float_store + && MEM_P (dest) + && FLOAT_MODE_P (GET_MODE (dest))) + /* Don't record BLKmode values, because we don't know the + size of it, and can't be sure that other BLKmode values + have the same or smaller size. */ + || GET_MODE (dest) == BLKmode + /* If we didn't put a REG_EQUAL value or a source into the hash + table, there is no point is recording DEST. */ + || sets[i].src_elt == 0) + continue; + + /* STRICT_LOW_PART isn't part of the value BEING set, + and neither is the SUBREG inside it. + Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ + if (GET_CODE (dest) == STRICT_LOW_PART) + dest = SUBREG_REG (XEXP (dest, 0)); + + if (REG_P (dest) || GET_CODE (dest) == SUBREG) + /* Registers must also be inserted into chains for quantities. */ + if (insert_regs (dest, sets[i].src_elt, 1)) + { + /* If `insert_regs' changes something, the hash code must be + recalculated. */ + rehash_using_reg (dest); + sets[i].dest_hash = HASH (dest, GET_MODE (dest)); + } + + /* If DEST is a paradoxical SUBREG, don't record DEST since the bits + outside the mode of GET_MODE (SUBREG_REG (dest)) are undefined. */ + if (paradoxical_subreg_p (dest)) + continue; + + elt = insert (dest, sets[i].src_elt, + sets[i].dest_hash, GET_MODE (dest)); + + /* If this is a constant, insert the constant anchors with the + equivalent register-offset expressions using register DEST. */ + if (targetm.const_anchor + && REG_P (dest) + && SCALAR_INT_MODE_P (GET_MODE (dest)) + && GET_CODE (sets[i].src_elt->exp) == CONST_INT) + insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest)); + + elt->in_memory = (MEM_P (sets[i].inner_dest) + && !MEM_READONLY_P (sets[i].inner_dest)); + + /* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no + narrower than M2, and both M1 and M2 are the same number of words, + we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so + make that equivalence as well. + + However, BAR may have equivalences for which gen_lowpart + will produce a simpler value than gen_lowpart applied to + BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all + BAR's equivalences. If we don't get a simplified form, make + the SUBREG. It will not be used in an equivalence, but will + cause two similar assignments to be detected. + + Note the loop below will find SUBREG_REG (DEST) since we have + already entered SRC and DEST of the SET in the table. */ + + if (GET_CODE (dest) == SUBREG + && (known_equal_after_align_down + (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1, + GET_MODE_SIZE (GET_MODE (dest)) - 1, + UNITS_PER_WORD)) + && !partial_subreg_p (dest) + && sets[i].src_elt != 0) + { + machine_mode new_mode = GET_MODE (SUBREG_REG (dest)); + struct table_elt *elt, *classp = 0; + + for (elt = sets[i].src_elt->first_same_value; elt; + elt = elt->next_same_value) + { + rtx new_src = 0; + unsigned src_hash; + struct table_elt *src_elt; + + /* Ignore invalid entries. */ + if (!REG_P (elt->exp) + && ! exp_equiv_p (elt->exp, elt->exp, 1, false)) + continue; + + /* We may have already been playing subreg games. If the + mode is already correct for the destination, use it. */ + if (GET_MODE (elt->exp) == new_mode) + new_src = elt->exp; + else + { + poly_uint64 byte + = subreg_lowpart_offset (new_mode, GET_MODE (dest)); + new_src = simplify_gen_subreg (new_mode, elt->exp, + GET_MODE (dest), byte); + } + + /* The call to simplify_gen_subreg fails if the value + is VOIDmode, yet we can't do any simplification, e.g. + for EXPR_LISTs denoting function call results. + It is invalid to construct a SUBREG with a VOIDmode + SUBREG_REG, hence a zero new_src means we can't do + this substitution. */ + if (! new_src) + continue; + + src_hash = HASH (new_src, new_mode); + src_elt = lookup (new_src, src_hash, new_mode); + + /* Put the new source in the hash table is if isn't + already. */ + if (src_elt == 0) + { + if (insert_regs (new_src, classp, 0)) + { + rehash_using_reg (new_src); + src_hash = HASH (new_src, new_mode); + } + src_elt = insert (new_src, classp, src_hash, new_mode); + src_elt->in_memory = elt->in_memory; + if (GET_CODE (new_src) == ASM_OPERANDS + && elt->cost == MAX_COST) + src_elt->cost = MAX_COST; + } + else if (classp && classp != src_elt->first_same_value) + /* Show that two things that we've seen before are + actually the same. */ + merge_equiv_classes (src_elt, classp); + + classp = src_elt->first_same_value; + /* Ignore invalid entries. */ + while (classp + && !REG_P (classp->exp) + && ! exp_equiv_p (classp->exp, classp->exp, 1, false)) + classp = classp->next_same_value; + } + } + } + + /* Special handling for (set REG0 REG1) where REG0 is the + "cheapest", cheaper than REG1. After cse, REG1 will probably not + be used in the sequel, so (if easily done) change this insn to + (set REG1 REG0) and replace REG1 with REG0 in the previous insn + that computed their value. Then REG1 will become a dead store + and won't cloud the situation for later optimizations. + + Do not make this change if REG1 is a hard register, because it will + then be used in the sequel and we may be changing a two-operand insn + into a three-operand insn. + + Also do not do this if we are operating on a copy of INSN. */ + + if (n_sets == 1 && sets[0].rtl) + try_back_substitute_reg (sets[0].rtl, insn); + +done:; +} + +/* Remove from the hash table all expressions that reference memory. */ + +static void +invalidate_memory (void) +{ + int i; + struct table_elt *p, *next; + + for (i = 0; i < HASH_SIZE; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (p->in_memory) + remove_from_table (p, i); + } +} + +/* Perform invalidation on the basis of everything about INSN, + except for invalidating the actual places that are SET in it. + This includes the places CLOBBERed, and anything that might + alias with something that is SET or CLOBBERed. */ + +static void +invalidate_from_clobbers (rtx_insn *insn) +{ + rtx x = PATTERN (insn); + + if (GET_CODE (x) == CLOBBER) + { + rtx ref = XEXP (x, 0); + if (ref) + { + if (REG_P (ref) || GET_CODE (ref) == SUBREG + || MEM_P (ref)) + invalidate (ref, VOIDmode); + else if (GET_CODE (ref) == STRICT_LOW_PART + || GET_CODE (ref) == ZERO_EXTRACT) + invalidate (XEXP (ref, 0), GET_MODE (ref)); + } + } + else if (GET_CODE (x) == PARALLEL) + { + int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER) + { + rtx ref = XEXP (y, 0); + if (REG_P (ref) || GET_CODE (ref) == SUBREG + || MEM_P (ref)) + invalidate (ref, VOIDmode); + else if (GET_CODE (ref) == STRICT_LOW_PART + || GET_CODE (ref) == ZERO_EXTRACT) + invalidate (XEXP (ref, 0), GET_MODE (ref)); + } + } + } +} + +/* Perform invalidation on the basis of everything about INSN. + This includes the places CLOBBERed, and anything that might + alias with something that is SET or CLOBBERed. */ + +static void +invalidate_from_sets_and_clobbers (rtx_insn *insn) +{ + rtx tem; + rtx x = PATTERN (insn); + + if (CALL_P (insn)) + { + for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) + { + rtx temx = XEXP (tem, 0); + if (GET_CODE (temx) == CLOBBER) + invalidate (SET_DEST (temx), VOIDmode); + } + } + + /* Ensure we invalidate the destination register of a CALL insn. + This is necessary for machines where this register is a fixed_reg, + because no other code would invalidate it. */ + if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL) + invalidate (SET_DEST (x), VOIDmode); + + else if (GET_CODE (x) == PARALLEL) + { + int i; + + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER) + { + rtx clobbered = XEXP (y, 0); + + if (REG_P (clobbered) + || GET_CODE (clobbered) == SUBREG) + invalidate (clobbered, VOIDmode); + else if (GET_CODE (clobbered) == STRICT_LOW_PART + || GET_CODE (clobbered) == ZERO_EXTRACT) + invalidate (XEXP (clobbered, 0), GET_MODE (clobbered)); + } + else if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL) + invalidate (SET_DEST (y), VOIDmode); + } + } +} + +static rtx cse_process_note (rtx); + +/* A simplify_replace_fn_rtx callback for cse_process_note. Process X, + part of the REG_NOTES of an insn. Replace any registers with either + an equivalent constant or the canonical form of the register. + Only replace addresses if the containing MEM remains valid. + + Return the replacement for X, or null if it should be simplified + recursively. */ + +static rtx +cse_process_note_1 (rtx x, const_rtx, void *) +{ + if (MEM_P (x)) + { + validate_change (x, &XEXP (x, 0), cse_process_note (XEXP (x, 0)), false); + return x; + } + + if (REG_P (x)) + { + int i = REG_QTY (REGNO (x)); + + /* Return a constant or a constant register. */ + if (REGNO_QTY_VALID_P (REGNO (x))) + { + struct qty_table_elem *ent = &qty_table[i]; + + if (ent->const_rtx != NULL_RTX + && (CONSTANT_P (ent->const_rtx) + || REG_P (ent->const_rtx))) + { + rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx); + if (new_rtx) + return copy_rtx (new_rtx); + } + } + + /* Otherwise, canonicalize this register. */ + return canon_reg (x, NULL); + } + + return NULL_RTX; +} + +/* Process X, part of the REG_NOTES of an insn. Replace any registers in it + with either an equivalent constant or the canonical form of the register. + Only replace addresses if the containing MEM remains valid. */ + +static rtx +cse_process_note (rtx x) +{ + return simplify_replace_fn_rtx (x, NULL_RTX, cse_process_note_1, NULL); +} + + +/* Find a path in the CFG, starting with FIRST_BB to perform CSE on. + + DATA is a pointer to a struct cse_basic_block_data, that is used to + describe the path. + It is filled with a queue of basic blocks, starting with FIRST_BB + and following a trace through the CFG. + + If all paths starting at FIRST_BB have been followed, or no new path + starting at FIRST_BB can be constructed, this function returns FALSE. + Otherwise, DATA->path is filled and the function returns TRUE indicating + that a path to follow was found. + + If FOLLOW_JUMPS is false, the maximum path length is 1 and the only + block in the path will be FIRST_BB. */ + +static bool +cse_find_path (basic_block first_bb, struct cse_basic_block_data *data, + int follow_jumps) +{ + basic_block bb; + edge e; + int path_size; + + bitmap_set_bit (cse_visited_basic_blocks, first_bb->index); + + /* See if there is a previous path. */ + path_size = data->path_size; + + /* There is a previous path. Make sure it started with FIRST_BB. */ + if (path_size) + gcc_assert (data->path[0].bb == first_bb); + + /* There was only one basic block in the last path. Clear the path and + return, so that paths starting at another basic block can be tried. */ + if (path_size == 1) + { + path_size = 0; + goto done; + } + + /* If the path was empty from the beginning, construct a new path. */ + if (path_size == 0) + data->path[path_size++].bb = first_bb; + else + { + /* Otherwise, path_size must be equal to or greater than 2, because + a previous path exists that is at least two basic blocks long. + + Update the previous branch path, if any. If the last branch was + previously along the branch edge, take the fallthrough edge now. */ + while (path_size >= 2) + { + basic_block last_bb_in_path, previous_bb_in_path; + edge e; + + --path_size; + last_bb_in_path = data->path[path_size].bb; + previous_bb_in_path = data->path[path_size - 1].bb; + + /* If we previously followed a path along the branch edge, try + the fallthru edge now. */ + if (EDGE_COUNT (previous_bb_in_path->succs) == 2 + && any_condjump_p (BB_END (previous_bb_in_path)) + && (e = find_edge (previous_bb_in_path, last_bb_in_path)) + && e == BRANCH_EDGE (previous_bb_in_path)) + { + bb = FALLTHRU_EDGE (previous_bb_in_path)->dest; + if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun) + && single_pred_p (bb) + /* We used to assert here that we would only see blocks + that we have not visited yet. But we may end up + visiting basic blocks twice if the CFG has changed + in this run of cse_main, because when the CFG changes + the topological sort of the CFG also changes. A basic + blocks that previously had more than two predecessors + may now have a single predecessor, and become part of + a path that starts at another basic block. + + We still want to visit each basic block only once, so + halt the path here if we have already visited BB. */ + && !bitmap_bit_p (cse_visited_basic_blocks, bb->index)) + { + bitmap_set_bit (cse_visited_basic_blocks, bb->index); + data->path[path_size++].bb = bb; + break; + } + } + + data->path[path_size].bb = NULL; + } + + /* If only one block remains in the path, bail. */ + if (path_size == 1) + { + path_size = 0; + goto done; + } + } + + /* Extend the path if possible. */ + if (follow_jumps) + { + bb = data->path[path_size - 1].bb; + while (bb && path_size < param_max_cse_path_length) + { + if (single_succ_p (bb)) + e = single_succ_edge (bb); + else if (EDGE_COUNT (bb->succs) == 2 + && any_condjump_p (BB_END (bb))) + { + /* First try to follow the branch. If that doesn't lead + to a useful path, follow the fallthru edge. */ + e = BRANCH_EDGE (bb); + if (!single_pred_p (e->dest)) + e = FALLTHRU_EDGE (bb); + } + else + e = NULL; + + if (e + && !((e->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label) + && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun) + && single_pred_p (e->dest) + /* Avoid visiting basic blocks twice. The large comment + above explains why this can happen. */ + && !bitmap_bit_p (cse_visited_basic_blocks, e->dest->index)) + { + basic_block bb2 = e->dest; + bitmap_set_bit (cse_visited_basic_blocks, bb2->index); + data->path[path_size++].bb = bb2; + bb = bb2; + } + else + bb = NULL; + } + } + +done: + data->path_size = path_size; + return path_size != 0; +} + +/* Dump the path in DATA to file F. NSETS is the number of sets + in the path. */ + +static void +cse_dump_path (struct cse_basic_block_data *data, int nsets, FILE *f) +{ + int path_entry; + + fprintf (f, ";; Following path with %d sets: ", nsets); + for (path_entry = 0; path_entry < data->path_size; path_entry++) + fprintf (f, "%d ", (data->path[path_entry].bb)->index); + fputc ('\n', f); + fflush (f); +} + + +/* Return true if BB has exception handling successor edges. */ + +static bool +have_eh_succ_edges (basic_block bb) +{ + edge e; + edge_iterator ei; + + FOR_EACH_EDGE (e, ei, bb->succs) + if (e->flags & EDGE_EH) + return true; + + return false; +} + + +/* Scan to the end of the path described by DATA. Return an estimate of + the total number of SETs of all insns in the path. */ + +static void +cse_prescan_path (struct cse_basic_block_data *data) +{ + int nsets = 0; + int path_size = data->path_size; + int path_entry; + + /* Scan to end of each basic block in the path. */ + for (path_entry = 0; path_entry < path_size; path_entry++) + { + basic_block bb; + rtx_insn *insn; + + bb = data->path[path_entry].bb; + + FOR_BB_INSNS (bb, insn) + { + if (!INSN_P (insn)) + continue; + + /* A PARALLEL can have lots of SETs in it, + especially if it is really an ASM_OPERANDS. */ + if (GET_CODE (PATTERN (insn)) == PARALLEL) + nsets += XVECLEN (PATTERN (insn), 0); + else + nsets += 1; + } + } + + data->nsets = nsets; +} + +/* Return true if the pattern of INSN uses a LABEL_REF for which + there isn't a REG_LABEL_OPERAND note. */ + +static bool +check_for_label_ref (rtx_insn *insn) +{ + /* If this insn uses a LABEL_REF and there isn't a REG_LABEL_OPERAND + note for it, we must rerun jump since it needs to place the note. If + this is a LABEL_REF for a CODE_LABEL that isn't in the insn chain, + don't do this since no REG_LABEL_OPERAND will be added. */ + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL) + { + const_rtx x = *iter; + if (GET_CODE (x) == LABEL_REF + && !LABEL_REF_NONLOCAL_P (x) + && (!JUMP_P (insn) + || !label_is_jump_target_p (label_ref_label (x), insn)) + && LABEL_P (label_ref_label (x)) + && INSN_UID (label_ref_label (x)) != 0 + && !find_reg_note (insn, REG_LABEL_OPERAND, label_ref_label (x))) + return true; + } + return false; +} + +/* Process a single extended basic block described by EBB_DATA. */ + +static void +cse_extended_basic_block (struct cse_basic_block_data *ebb_data) +{ + int path_size = ebb_data->path_size; + int path_entry; + int num_insns = 0; + + /* Allocate the space needed by qty_table. */ + qty_table = XNEWVEC (struct qty_table_elem, max_qty); + + new_basic_block (); + cse_ebb_live_in = df_get_live_in (ebb_data->path[0].bb); + cse_ebb_live_out = df_get_live_out (ebb_data->path[path_size - 1].bb); + for (path_entry = 0; path_entry < path_size; path_entry++) + { + basic_block bb; + rtx_insn *insn; + + bb = ebb_data->path[path_entry].bb; + + /* Invalidate recorded information for eh regs if there is an EH + edge pointing to that bb. */ + if (bb_has_eh_pred (bb)) + { + df_ref def; + + FOR_EACH_ARTIFICIAL_DEF (def, bb->index) + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + invalidate (DF_REF_REG (def), GET_MODE (DF_REF_REG (def))); + } + + optimize_this_for_speed_p = optimize_bb_for_speed_p (bb); + FOR_BB_INSNS (bb, insn) + { + /* If we have processed 1,000 insns, flush the hash table to + avoid extreme quadratic behavior. We must not include NOTEs + in the count since there may be more of them when generating + debugging information. If we clear the table at different + times, code generated with -g -O might be different than code + generated with -O but not -g. + + FIXME: This is a real kludge and needs to be done some other + way. */ + if (NONDEBUG_INSN_P (insn) + && num_insns++ > param_max_cse_insns) + { + flush_hash_table (); + num_insns = 0; + } + + if (INSN_P (insn)) + { + /* Process notes first so we have all notes in canonical forms + when looking for duplicate operations. */ + bool changed = false; + for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_EQUAL) + { + rtx newval = cse_process_note (XEXP (note, 0)); + if (newval != XEXP (note, 0)) + { + XEXP (note, 0) = newval; + changed = true; + } + } + if (changed) + df_notes_rescan (insn); + + cse_insn (insn); + + /* If we haven't already found an insn where we added a LABEL_REF, + check this one. */ + if (INSN_P (insn) && !recorded_label_ref + && check_for_label_ref (insn)) + recorded_label_ref = true; + } + } + + /* With non-call exceptions, we are not always able to update + the CFG properly inside cse_insn. So clean up possibly + redundant EH edges here. */ + if (cfun->can_throw_non_call_exceptions && have_eh_succ_edges (bb)) + cse_cfg_altered |= purge_dead_edges (bb); + + /* If we changed a conditional jump, we may have terminated + the path we are following. Check that by verifying that + the edge we would take still exists. If the edge does + not exist anymore, purge the remainder of the path. + Note that this will cause us to return to the caller. */ + if (path_entry < path_size - 1) + { + basic_block next_bb = ebb_data->path[path_entry + 1].bb; + if (!find_edge (bb, next_bb)) + { + do + { + path_size--; + + /* If we truncate the path, we must also reset the + visited bit on the remaining blocks in the path, + or we will never visit them at all. */ + bitmap_clear_bit (cse_visited_basic_blocks, + ebb_data->path[path_size].bb->index); + ebb_data->path[path_size].bb = NULL; + } + while (path_size - 1 != path_entry); + ebb_data->path_size = path_size; + } + } + + /* If this is a conditional jump insn, record any known + equivalences due to the condition being tested. */ + insn = BB_END (bb); + if (path_entry < path_size - 1 + && EDGE_COUNT (bb->succs) == 2 + && JUMP_P (insn) + && single_set (insn) + && any_condjump_p (insn)) + { + basic_block next_bb = ebb_data->path[path_entry + 1].bb; + bool taken = (next_bb == BRANCH_EDGE (bb)->dest); + record_jump_equiv (insn, taken); + } + } + + gcc_assert (next_qty <= max_qty); + + free (qty_table); +} + + +/* Perform cse on the instructions of a function. + F is the first instruction. + NREGS is one plus the highest pseudo-reg number used in the instruction. + + Return 2 if jump optimizations should be redone due to simplifications + in conditional jump instructions. + Return 1 if the CFG should be cleaned up because it has been modified. + Return 0 otherwise. */ + +static int +cse_main (rtx_insn *f ATTRIBUTE_UNUSED, int nregs) +{ + struct cse_basic_block_data ebb_data; + basic_block bb; + int *rc_order = XNEWVEC (int, last_basic_block_for_fn (cfun)); + int i, n_blocks; + + /* CSE doesn't use dominane info but can invalidate it in different ways. + For simplicity free dominance info here. */ + free_dominance_info (CDI_DOMINATORS); + + df_set_flags (DF_LR_RUN_DCE); + df_note_add_problem (); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); + + reg_scan (get_insns (), max_reg_num ()); + init_cse_reg_info (nregs); + + ebb_data.path = XNEWVEC (struct branch_path, + param_max_cse_path_length); + + cse_cfg_altered = false; + cse_jumps_altered = false; + recorded_label_ref = false; + ebb_data.path_size = 0; + ebb_data.nsets = 0; + rtl_hooks = cse_rtl_hooks; + + init_recog (); + init_alias_analysis (); + + reg_eqv_table = XNEWVEC (struct reg_eqv_elem, nregs); + + /* Set up the table of already visited basic blocks. */ + cse_visited_basic_blocks = sbitmap_alloc (last_basic_block_for_fn (cfun)); + bitmap_clear (cse_visited_basic_blocks); + + /* Loop over basic blocks in reverse completion order (RPO), + excluding the ENTRY and EXIT blocks. */ + n_blocks = pre_and_rev_post_order_compute (NULL, rc_order, false); + i = 0; + while (i < n_blocks) + { + /* Find the first block in the RPO queue that we have not yet + processed before. */ + do + { + bb = BASIC_BLOCK_FOR_FN (cfun, rc_order[i++]); + } + while (bitmap_bit_p (cse_visited_basic_blocks, bb->index) + && i < n_blocks); + + /* Find all paths starting with BB, and process them. */ + while (cse_find_path (bb, &ebb_data, flag_cse_follow_jumps)) + { + /* Pre-scan the path. */ + cse_prescan_path (&ebb_data); + + /* If this basic block has no sets, skip it. */ + if (ebb_data.nsets == 0) + continue; + + /* Get a reasonable estimate for the maximum number of qty's + needed for this path. For this, we take the number of sets + and multiply that by MAX_RECOG_OPERANDS. */ + max_qty = ebb_data.nsets * MAX_RECOG_OPERANDS; + + /* Dump the path we're about to process. */ + if (dump_file) + cse_dump_path (&ebb_data, ebb_data.nsets, dump_file); + + cse_extended_basic_block (&ebb_data); + } + } + + /* Clean up. */ + end_alias_analysis (); + free (reg_eqv_table); + free (ebb_data.path); + sbitmap_free (cse_visited_basic_blocks); + free (rc_order); + rtl_hooks = general_rtl_hooks; + + if (cse_jumps_altered || recorded_label_ref) + return 2; + else if (cse_cfg_altered) + return 1; + else + return 0; +} + +/* Count the number of times registers are used (not set) in X. + COUNTS is an array in which we accumulate the count, INCR is how much + we count each register usage. + + Don't count a usage of DEST, which is the SET_DEST of a SET which + contains X in its SET_SRC. This is because such a SET does not + modify the liveness of DEST. + DEST is set to pc_rtx for a trapping insn, or for an insn with side effects. + We must then count uses of a SET_DEST regardless, because the insn can't be + deleted here. */ + +static void +count_reg_usage (rtx x, int *counts, rtx dest, int incr) +{ + enum rtx_code code; + rtx note; + const char *fmt; + int i, j; + + if (x == 0) + return; + + switch (code = GET_CODE (x)) + { + case REG: + if (x != dest) + counts[REGNO (x)] += incr; + return; + + case PC: + case CONST: + CASE_CONST_ANY: + case SYMBOL_REF: + case LABEL_REF: + return; + + case CLOBBER: + /* If we are clobbering a MEM, mark any registers inside the address + as being used. */ + if (MEM_P (XEXP (x, 0))) + count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr); + return; + + case SET: + /* Unless we are setting a REG, count everything in SET_DEST. */ + if (!REG_P (SET_DEST (x))) + count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr); + count_reg_usage (SET_SRC (x), counts, + dest ? dest : SET_DEST (x), + incr); + return; + + case DEBUG_INSN: + return; + + case CALL_INSN: + case INSN: + case JUMP_INSN: + /* We expect dest to be NULL_RTX here. If the insn may throw, + or if it cannot be deleted due to side-effects, mark this fact + by setting DEST to pc_rtx. */ + if ((!cfun->can_delete_dead_exceptions && !insn_nothrow_p (x)) + || side_effects_p (PATTERN (x))) + dest = pc_rtx; + if (code == CALL_INSN) + count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr); + count_reg_usage (PATTERN (x), counts, dest, incr); + + /* Things used in a REG_EQUAL note aren't dead since loop may try to + use them. */ + + note = find_reg_equal_equiv_note (x); + if (note) + { + rtx eqv = XEXP (note, 0); + + if (GET_CODE (eqv) == EXPR_LIST) + /* This REG_EQUAL note describes the result of a function call. + Process all the arguments. */ + do + { + count_reg_usage (XEXP (eqv, 0), counts, dest, incr); + eqv = XEXP (eqv, 1); + } + while (eqv && GET_CODE (eqv) == EXPR_LIST); + else + count_reg_usage (eqv, counts, dest, incr); + } + return; + + case EXPR_LIST: + if (REG_NOTE_KIND (x) == REG_EQUAL + || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE) + /* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)), + involving registers in the address. */ + || GET_CODE (XEXP (x, 0)) == CLOBBER) + count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr); + + count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr); + return; + + case ASM_OPERANDS: + /* Iterate over just the inputs, not the constraints as well. */ + for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) + count_reg_usage (ASM_OPERANDS_INPUT (x, i), counts, dest, incr); + return; + + case INSN_LIST: + case INT_LIST: + gcc_unreachable (); + + default: + break; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + count_reg_usage (XEXP (x, i), counts, dest, incr); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + count_reg_usage (XVECEXP (x, i, j), counts, dest, incr); + } +} + +/* Return true if X is a dead register. */ + +static inline int +is_dead_reg (const_rtx x, int *counts) +{ + return (REG_P (x) + && REGNO (x) >= FIRST_PSEUDO_REGISTER + && counts[REGNO (x)] == 0); +} + +/* Return true if set is live. */ +static bool +set_live_p (rtx set, int *counts) +{ + if (set_noop_p (set)) + return false; + + if (!is_dead_reg (SET_DEST (set), counts) + || side_effects_p (SET_SRC (set))) + return true; + + return false; +} + +/* Return true if insn is live. */ + +static bool +insn_live_p (rtx_insn *insn, int *counts) +{ + int i; + if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn)) + return true; + else if (GET_CODE (PATTERN (insn)) == SET) + return set_live_p (PATTERN (insn), counts); + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + { + rtx elt = XVECEXP (PATTERN (insn), 0, i); + + if (GET_CODE (elt) == SET) + { + if (set_live_p (elt, counts)) + return true; + } + else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE) + return true; + } + return false; + } + else if (DEBUG_INSN_P (insn)) + { + rtx_insn *next; + + if (DEBUG_MARKER_INSN_P (insn)) + return true; + + for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next)) + if (NOTE_P (next)) + continue; + else if (!DEBUG_INSN_P (next)) + return true; + /* If we find an inspection point, such as a debug begin stmt, + we want to keep the earlier debug insn. */ + else if (DEBUG_MARKER_INSN_P (next)) + return true; + else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next)) + return false; + + return true; + } + else + return true; +} + +/* Count the number of stores into pseudo. Callback for note_stores. */ + +static void +count_stores (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data) +{ + int *counts = (int *) data; + if (REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER) + counts[REGNO (x)]++; +} + +/* Return if DEBUG_INSN pattern PAT needs to be reset because some dead + pseudo doesn't have a replacement. COUNTS[X] is zero if register X + is dead and REPLACEMENTS[X] is null if it has no replacemenet. + Set *SEEN_REPL to true if we see a dead register that does have + a replacement. */ + +static bool +is_dead_debug_insn (const_rtx pat, int *counts, rtx *replacements, + bool *seen_repl) +{ + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, pat, NONCONST) + { + const_rtx x = *iter; + if (is_dead_reg (x, counts)) + { + if (replacements && replacements[REGNO (x)] != NULL_RTX) + *seen_repl = true; + else + return true; + } + } + return false; +} + +/* Replace a dead pseudo in a DEBUG_INSN with replacement DEBUG_EXPR. + Callback for simplify_replace_fn_rtx. */ + +static rtx +replace_dead_reg (rtx x, const_rtx old_rtx ATTRIBUTE_UNUSED, void *data) +{ + rtx *replacements = (rtx *) data; + + if (REG_P (x) + && REGNO (x) >= FIRST_PSEUDO_REGISTER + && replacements[REGNO (x)] != NULL_RTX) + { + if (GET_MODE (x) == GET_MODE (replacements[REGNO (x)])) + return replacements[REGNO (x)]; + return lowpart_subreg (GET_MODE (x), replacements[REGNO (x)], + GET_MODE (replacements[REGNO (x)])); + } + return NULL_RTX; +} + +/* Scan all the insns and delete any that are dead; i.e., they store a register + that is never used or they copy a register to itself. + + This is used to remove insns made obviously dead by cse, loop or other + optimizations. It improves the heuristics in loop since it won't try to + move dead invariants out of loops or make givs for dead quantities. The + remaining passes of the compilation are also sped up. */ + +int +delete_trivially_dead_insns (rtx_insn *insns, int nreg) +{ + int *counts; + rtx_insn *insn, *prev; + rtx *replacements = NULL; + int ndead = 0; + + timevar_push (TV_DELETE_TRIVIALLY_DEAD); + /* First count the number of times each register is used. */ + if (MAY_HAVE_DEBUG_BIND_INSNS) + { + counts = XCNEWVEC (int, nreg * 3); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (DEBUG_BIND_INSN_P (insn)) + count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, + NULL_RTX, 1); + else if (INSN_P (insn)) + { + count_reg_usage (insn, counts, NULL_RTX, 1); + note_stores (insn, count_stores, counts + nreg * 2); + } + /* If there can be debug insns, COUNTS are 3 consecutive arrays. + First one counts how many times each pseudo is used outside + of debug insns, second counts how many times each pseudo is + used in debug insns and third counts how many times a pseudo + is stored. */ + } + else + { + counts = XCNEWVEC (int, nreg); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + count_reg_usage (insn, counts, NULL_RTX, 1); + /* If no debug insns can be present, COUNTS is just an array + which counts how many times each pseudo is used. */ + } + /* Pseudo PIC register should be considered as used due to possible + new usages generated. */ + if (!reload_completed + && pic_offset_table_rtx + && REGNO (pic_offset_table_rtx) >= FIRST_PSEUDO_REGISTER) + counts[REGNO (pic_offset_table_rtx)]++; + /* Go from the last insn to the first and delete insns that only set unused + registers or copy a register to itself. As we delete an insn, remove + usage counts for registers it uses. + + The first jump optimization pass may leave a real insn as the last + insn in the function. We must not skip that insn or we may end + up deleting code that is not really dead. + + If some otherwise unused register is only used in DEBUG_INSNs, + try to create a DEBUG_EXPR temporary and emit a DEBUG_INSN before + the setter. Then go through DEBUG_INSNs and if a DEBUG_EXPR + has been created for the unused register, replace it with + the DEBUG_EXPR, otherwise reset the DEBUG_INSN. */ + for (insn = get_last_insn (); insn; insn = prev) + { + int live_insn = 0; + + prev = PREV_INSN (insn); + if (!INSN_P (insn)) + continue; + + live_insn = insn_live_p (insn, counts); + + /* If this is a dead insn, delete it and show registers in it aren't + being used. */ + + if (! live_insn && dbg_cnt (delete_trivial_dead)) + { + if (DEBUG_INSN_P (insn)) + { + if (DEBUG_BIND_INSN_P (insn)) + count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, + NULL_RTX, -1); + } + else + { + rtx set; + if (MAY_HAVE_DEBUG_BIND_INSNS + && (set = single_set (insn)) != NULL_RTX + && is_dead_reg (SET_DEST (set), counts) + /* Used at least once in some DEBUG_INSN. */ + && counts[REGNO (SET_DEST (set)) + nreg] > 0 + /* And set exactly once. */ + && counts[REGNO (SET_DEST (set)) + nreg * 2] == 1 + && !side_effects_p (SET_SRC (set)) + && asm_noperands (PATTERN (insn)) < 0) + { + rtx dval, bind_var_loc; + rtx_insn *bind; + + /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL). */ + dval = make_debug_expr_from_rtl (SET_DEST (set)); + + /* Emit a debug bind insn before the insn in which + reg dies. */ + bind_var_loc = + gen_rtx_VAR_LOCATION (GET_MODE (SET_DEST (set)), + DEBUG_EXPR_TREE_DECL (dval), + SET_SRC (set), + VAR_INIT_STATUS_INITIALIZED); + count_reg_usage (bind_var_loc, counts + nreg, NULL_RTX, 1); + + bind = emit_debug_insn_before (bind_var_loc, insn); + df_insn_rescan (bind); + + if (replacements == NULL) + replacements = XCNEWVEC (rtx, nreg); + replacements[REGNO (SET_DEST (set))] = dval; + } + + count_reg_usage (insn, counts, NULL_RTX, -1); + ndead++; + } + cse_cfg_altered |= delete_insn_and_edges (insn); + } + } + + if (MAY_HAVE_DEBUG_BIND_INSNS) + { + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + if (DEBUG_BIND_INSN_P (insn)) + { + /* If this debug insn references a dead register that wasn't replaced + with an DEBUG_EXPR, reset the DEBUG_INSN. */ + bool seen_repl = false; + if (is_dead_debug_insn (INSN_VAR_LOCATION_LOC (insn), + counts, replacements, &seen_repl)) + { + INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); + df_insn_rescan (insn); + } + else if (seen_repl) + { + INSN_VAR_LOCATION_LOC (insn) + = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn), + NULL_RTX, replace_dead_reg, + replacements); + df_insn_rescan (insn); + } + } + free (replacements); + } + + if (dump_file && ndead) + fprintf (dump_file, "Deleted %i trivially dead insns\n", + ndead); + /* Clean up. */ + free (counts); + timevar_pop (TV_DELETE_TRIVIALLY_DEAD); + return ndead; +} + +/* If LOC contains references to NEWREG in a different mode, change them + to use NEWREG instead. */ + +static void +cse_change_cc_mode (subrtx_ptr_iterator::array_type &array, + rtx *loc, rtx_insn *insn, rtx newreg) +{ + FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST) + { + rtx *loc = *iter; + rtx x = *loc; + if (x + && REG_P (x) + && REGNO (x) == REGNO (newreg) + && GET_MODE (x) != GET_MODE (newreg)) + { + validate_change (insn, loc, newreg, 1); + iter.skip_subrtxes (); + } + } +} + +/* Change the mode of any reference to the register REGNO (NEWREG) to + GET_MODE (NEWREG) in INSN. */ + +static void +cse_change_cc_mode_insn (rtx_insn *insn, rtx newreg) +{ + int success; + + if (!INSN_P (insn)) + return; + + subrtx_ptr_iterator::array_type array; + cse_change_cc_mode (array, &PATTERN (insn), insn, newreg); + cse_change_cc_mode (array, ®_NOTES (insn), insn, newreg); + + /* If the following assertion was triggered, there is most probably + something wrong with the cc_modes_compatible back end function. + CC modes only can be considered compatible if the insn - with the mode + replaced by any of the compatible modes - can still be recognized. */ + success = apply_change_group (); + gcc_assert (success); +} + +/* Change the mode of any reference to the register REGNO (NEWREG) to + GET_MODE (NEWREG), starting at START. Stop before END. Stop at + any instruction which modifies NEWREG. */ + +static void +cse_change_cc_mode_insns (rtx_insn *start, rtx_insn *end, rtx newreg) +{ + rtx_insn *insn; + + for (insn = start; insn != end; insn = NEXT_INSN (insn)) + { + if (! INSN_P (insn)) + continue; + + if (reg_set_p (newreg, insn)) + return; + + cse_change_cc_mode_insn (insn, newreg); + } +} + +/* BB is a basic block which finishes with CC_REG as a condition code + register which is set to CC_SRC. Look through the successors of BB + to find blocks which have a single predecessor (i.e., this one), + and look through those blocks for an assignment to CC_REG which is + equivalent to CC_SRC. CAN_CHANGE_MODE indicates whether we are + permitted to change the mode of CC_SRC to a compatible mode. This + returns VOIDmode if no equivalent assignments were found. + Otherwise it returns the mode which CC_SRC should wind up with. + ORIG_BB should be the same as BB in the outermost cse_cc_succs call, + but is passed unmodified down to recursive calls in order to prevent + endless recursion. + + The main complexity in this function is handling the mode issues. + We may have more than one duplicate which we can eliminate, and we + try to find a mode which will work for multiple duplicates. */ + +static machine_mode +cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src, + bool can_change_mode) +{ + bool found_equiv; + machine_mode mode; + unsigned int insn_count; + edge e; + rtx_insn *insns[2]; + machine_mode modes[2]; + rtx_insn *last_insns[2]; + unsigned int i; + rtx newreg; + edge_iterator ei; + + /* We expect to have two successors. Look at both before picking + the final mode for the comparison. If we have more successors + (i.e., some sort of table jump, although that seems unlikely), + then we require all beyond the first two to use the same + mode. */ + + found_equiv = false; + mode = GET_MODE (cc_src); + insn_count = 0; + FOR_EACH_EDGE (e, ei, bb->succs) + { + rtx_insn *insn; + rtx_insn *end; + + if (e->flags & EDGE_COMPLEX) + continue; + + if (EDGE_COUNT (e->dest->preds) != 1 + || e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun) + /* Avoid endless recursion on unreachable blocks. */ + || e->dest == orig_bb) + continue; + + end = NEXT_INSN (BB_END (e->dest)); + for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + + /* If CC_SRC is modified, we have to stop looking for + something which uses it. */ + if (modified_in_p (cc_src, insn)) + break; + + /* Check whether INSN sets CC_REG to CC_SRC. */ + set = single_set (insn); + if (set + && REG_P (SET_DEST (set)) + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + bool found; + machine_mode set_mode; + machine_mode comp_mode; + + found = false; + set_mode = GET_MODE (SET_SRC (set)); + comp_mode = set_mode; + if (rtx_equal_p (cc_src, SET_SRC (set))) + found = true; + else if (GET_CODE (cc_src) == COMPARE + && GET_CODE (SET_SRC (set)) == COMPARE + && mode != set_mode + && rtx_equal_p (XEXP (cc_src, 0), + XEXP (SET_SRC (set), 0)) + && rtx_equal_p (XEXP (cc_src, 1), + XEXP (SET_SRC (set), 1))) + + { + comp_mode = targetm.cc_modes_compatible (mode, set_mode); + if (comp_mode != VOIDmode + && (can_change_mode || comp_mode == mode)) + found = true; + } + + if (found) + { + found_equiv = true; + if (insn_count < ARRAY_SIZE (insns)) + { + insns[insn_count] = insn; + modes[insn_count] = set_mode; + last_insns[insn_count] = end; + ++insn_count; + + if (mode != comp_mode) + { + gcc_assert (can_change_mode); + mode = comp_mode; + + /* The modified insn will be re-recognized later. */ + PUT_MODE (cc_src, mode); + } + } + else + { + if (set_mode != mode) + { + /* We found a matching expression in the + wrong mode, but we don't have room to + store it in the array. Punt. This case + should be rare. */ + break; + } + /* INSN sets CC_REG to a value equal to CC_SRC + with the right mode. We can simply delete + it. */ + delete_insn (insn); + } + + /* We found an instruction to delete. Keep looking, + in the hopes of finding a three-way jump. */ + continue; + } + + /* We found an instruction which sets the condition + code, so don't look any farther. */ + break; + } + + /* If INSN sets CC_REG in some other way, don't look any + farther. */ + if (reg_set_p (cc_reg, insn)) + break; + } + + /* If we fell off the bottom of the block, we can keep looking + through successors. We pass CAN_CHANGE_MODE as false because + we aren't prepared to handle compatibility between the + further blocks and this block. */ + if (insn == end) + { + machine_mode submode; + + submode = cse_cc_succs (e->dest, orig_bb, cc_reg, cc_src, false); + if (submode != VOIDmode) + { + gcc_assert (submode == mode); + found_equiv = true; + can_change_mode = false; + } + } + } + + if (! found_equiv) + return VOIDmode; + + /* Now INSN_COUNT is the number of instructions we found which set + CC_REG to a value equivalent to CC_SRC. The instructions are in + INSNS. The modes used by those instructions are in MODES. */ + + newreg = NULL_RTX; + for (i = 0; i < insn_count; ++i) + { + if (modes[i] != mode) + { + /* We need to change the mode of CC_REG in INSNS[i] and + subsequent instructions. */ + if (! newreg) + { + if (GET_MODE (cc_reg) == mode) + newreg = cc_reg; + else + newreg = gen_rtx_REG (mode, REGNO (cc_reg)); + } + cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i], + newreg); + } + + cse_cfg_altered |= delete_insn_and_edges (insns[i]); + } + + return mode; +} + +/* If we have a fixed condition code register (or two), walk through + the instructions and try to eliminate duplicate assignments. */ + +static void +cse_condition_code_reg (void) +{ + unsigned int cc_regno_1; + unsigned int cc_regno_2; + rtx cc_reg_1; + rtx cc_reg_2; + basic_block bb; + + if (! targetm.fixed_condition_code_regs (&cc_regno_1, &cc_regno_2)) + return; + + cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1); + if (cc_regno_2 != INVALID_REGNUM) + cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2); + else + cc_reg_2 = NULL_RTX; + + FOR_EACH_BB_FN (bb, cfun) + { + rtx_insn *last_insn; + rtx cc_reg; + rtx_insn *insn; + rtx_insn *cc_src_insn; + rtx cc_src; + machine_mode mode; + machine_mode orig_mode; + + /* Look for blocks which end with a conditional jump based on a + condition code register. Then look for the instruction which + sets the condition code register. Then look through the + successor blocks for instructions which set the condition + code register to the same value. There are other possible + uses of the condition code register, but these are by far the + most common and the ones which we are most likely to be able + to optimize. */ + + last_insn = BB_END (bb); + if (!JUMP_P (last_insn)) + continue; + + if (reg_referenced_p (cc_reg_1, PATTERN (last_insn))) + cc_reg = cc_reg_1; + else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn))) + cc_reg = cc_reg_2; + else + continue; + + cc_src_insn = NULL; + cc_src = NULL_RTX; + for (insn = PREV_INSN (last_insn); + insn && insn != PREV_INSN (BB_HEAD (bb)); + insn = PREV_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + set = single_set (insn); + if (set + && REG_P (SET_DEST (set)) + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + cc_src_insn = insn; + cc_src = SET_SRC (set); + break; + } + else if (reg_set_p (cc_reg, insn)) + break; + } + + if (! cc_src_insn) + continue; + + if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn))) + continue; + + /* Now CC_REG is a condition code register used for a + conditional jump at the end of the block, and CC_SRC, in + CC_SRC_INSN, is the value to which that condition code + register is set, and CC_SRC is still meaningful at the end of + the basic block. */ + + orig_mode = GET_MODE (cc_src); + mode = cse_cc_succs (bb, bb, cc_reg, cc_src, true); + if (mode != VOIDmode) + { + gcc_assert (mode == GET_MODE (cc_src)); + if (mode != orig_mode) + { + rtx newreg = gen_rtx_REG (mode, REGNO (cc_reg)); + + cse_change_cc_mode_insn (cc_src_insn, newreg); + + /* Do the same in the following insns that use the + current value of CC_REG within BB. */ + cse_change_cc_mode_insns (NEXT_INSN (cc_src_insn), + NEXT_INSN (last_insn), + newreg); + } + } + } +} + + +/* Perform common subexpression elimination. Nonzero value from + `cse_main' means that jumps were simplified and some code may now + be unreachable, so do jump optimization again. */ +static unsigned int +rest_of_handle_cse (void) +{ + int tem; + + if (dump_file) + dump_flow_info (dump_file, dump_flags); + + tem = cse_main (get_insns (), max_reg_num ()); + + /* If we are not running more CSE passes, then we are no longer + expecting CSE to be run. But always rerun it in a cheap mode. */ + cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse; + + if (tem == 2) + { + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); + timevar_pop (TV_JUMP); + } + else if (tem == 1 || optimize > 1) + cse_cfg_altered |= cleanup_cfg (0); + + return 0; +} + +namespace { + +const pass_data pass_data_cse = +{ + RTL_PASS, /* type */ + "cse1", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_CSE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_cse : public rtl_opt_pass +{ +public: + pass_cse (gcc::context *ctxt) + : rtl_opt_pass (pass_data_cse, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return optimize > 0; } + virtual unsigned int execute (function *) { return rest_of_handle_cse (); } + +}; // class pass_cse + +} // anon namespace + +rtl_opt_pass * +make_pass_cse (gcc::context *ctxt) +{ + return new pass_cse (ctxt); +} + + +/* Run second CSE pass after loop optimizations. */ +static unsigned int +rest_of_handle_cse2 (void) +{ + int tem; + + if (dump_file) + dump_flow_info (dump_file, dump_flags); + + tem = cse_main (get_insns (), max_reg_num ()); + + /* Run a pass to eliminate duplicated assignments to condition code + registers. We have to run this after bypass_jumps, because it + makes it harder for that pass to determine whether a jump can be + bypassed safely. */ + cse_condition_code_reg (); + + delete_trivially_dead_insns (get_insns (), max_reg_num ()); + + if (tem == 2) + { + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); + timevar_pop (TV_JUMP); + } + else if (tem == 1 || cse_cfg_altered) + cse_cfg_altered |= cleanup_cfg (0); + + cse_not_expected = 1; + return 0; +} + + +namespace { + +const pass_data pass_data_cse2 = +{ + RTL_PASS, /* type */ + "cse2", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_CSE2, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_cse2 : public rtl_opt_pass +{ +public: + pass_cse2 (gcc::context *ctxt) + : rtl_opt_pass (pass_data_cse2, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return optimize > 0 && flag_rerun_cse_after_loop; + } + + virtual unsigned int execute (function *) { return rest_of_handle_cse2 (); } + +}; // class pass_cse2 + +} // anon namespace + +rtl_opt_pass * +make_pass_cse2 (gcc::context *ctxt) +{ + return new pass_cse2 (ctxt); +} + +/* Run second CSE pass after loop optimizations. */ +static unsigned int +rest_of_handle_cse_after_global_opts (void) +{ + int save_cfj; + int tem; + + /* We only want to do local CSE, so don't follow jumps. */ + save_cfj = flag_cse_follow_jumps; + flag_cse_follow_jumps = 0; + + rebuild_jump_labels (get_insns ()); + tem = cse_main (get_insns (), max_reg_num ()); + cse_cfg_altered |= purge_all_dead_edges (); + delete_trivially_dead_insns (get_insns (), max_reg_num ()); + + cse_not_expected = !flag_rerun_cse_after_loop; + + /* If cse altered any jumps, rerun jump opts to clean things up. */ + if (tem == 2) + { + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); + timevar_pop (TV_JUMP); + } + else if (tem == 1 || cse_cfg_altered) + cse_cfg_altered |= cleanup_cfg (0); + + flag_cse_follow_jumps = save_cfj; + return 0; +} + +namespace { + +const pass_data pass_data_cse_after_global_opts = +{ + RTL_PASS, /* type */ + "cse_local", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_CSE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_cse_after_global_opts : public rtl_opt_pass +{ +public: + pass_cse_after_global_opts (gcc::context *ctxt) + : rtl_opt_pass (pass_data_cse_after_global_opts, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return optimize > 0 && flag_rerun_cse_after_global_opts; + } + + virtual unsigned int execute (function *) + { + return rest_of_handle_cse_after_global_opts (); + } + +}; // class pass_cse_after_global_opts + +} // anon namespace + +rtl_opt_pass * +make_pass_cse_after_global_opts (gcc::context *ctxt) +{ + return new pass_cse_after_global_opts (ctxt); +} |