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/tree-ssa-structalias.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/tree-ssa-structalias.cc')
-rw-r--r-- | gcc/tree-ssa-structalias.cc | 8753 |
1 files changed, 8753 insertions, 0 deletions
diff --git a/gcc/tree-ssa-structalias.cc b/gcc/tree-ssa-structalias.cc new file mode 100644 index 0000000..d318f88 --- /dev/null +++ b/gcc/tree-ssa-structalias.cc @@ -0,0 +1,8753 @@ +/* Tree based points-to analysis + Copyright (C) 2005-2022 Free Software Foundation, Inc. + Contributed by Daniel Berlin <dberlin@dberlin.org> + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, 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 "rtl.h" +#include "tree.h" +#include "gimple.h" +#include "alloc-pool.h" +#include "tree-pass.h" +#include "ssa.h" +#include "cgraph.h" +#include "tree-pretty-print.h" +#include "diagnostic-core.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "stmt.h" +#include "gimple-iterator.h" +#include "tree-into-ssa.h" +#include "tree-dfa.h" +#include "gimple-walk.h" +#include "varasm.h" +#include "stringpool.h" +#include "attribs.h" +#include "tree-ssa.h" +#include "tree-cfg.h" +#include "gimple-range.h" +#include "ipa-modref-tree.h" +#include "ipa-modref.h" +#include "attr-fnspec.h" + +/* The idea behind this analyzer is to generate set constraints from the + program, then solve the resulting constraints in order to generate the + points-to sets. + + Set constraints are a way of modeling program analysis problems that + involve sets. They consist of an inclusion constraint language, + describing the variables (each variable is a set) and operations that + are involved on the variables, and a set of rules that derive facts + from these operations. To solve a system of set constraints, you derive + all possible facts under the rules, which gives you the correct sets + as a consequence. + + See "Efficient Field-sensitive pointer analysis for C" by "David + J. Pearce and Paul H. J. Kelly and Chris Hankin", at + http://citeseer.ist.psu.edu/pearce04efficient.html + + Also see "Ultra-fast Aliasing Analysis using CLA: A Million Lines + of C Code in a Second" by "Nevin Heintze and Olivier Tardieu" at + http://citeseer.ist.psu.edu/heintze01ultrafast.html + + There are three types of real constraint expressions, DEREF, + ADDRESSOF, and SCALAR. Each constraint expression consists + of a constraint type, a variable, and an offset. + + SCALAR is a constraint expression type used to represent x, whether + it appears on the LHS or the RHS of a statement. + DEREF is a constraint expression type used to represent *x, whether + it appears on the LHS or the RHS of a statement. + ADDRESSOF is a constraint expression used to represent &x, whether + it appears on the LHS or the RHS of a statement. + + Each pointer variable in the program is assigned an integer id, and + each field of a structure variable is assigned an integer id as well. + + Structure variables are linked to their list of fields through a "next + field" in each variable that points to the next field in offset + order. + Each variable for a structure field has + + 1. "size", that tells the size in bits of that field. + 2. "fullsize", that tells the size in bits of the entire structure. + 3. "offset", that tells the offset in bits from the beginning of the + structure to this field. + + Thus, + struct f + { + int a; + int b; + } foo; + int *bar; + + looks like + + foo.a -> id 1, size 32, offset 0, fullsize 64, next foo.b + foo.b -> id 2, size 32, offset 32, fullsize 64, next NULL + bar -> id 3, size 32, offset 0, fullsize 32, next NULL + + + In order to solve the system of set constraints, the following is + done: + + 1. Each constraint variable x has a solution set associated with it, + Sol(x). + + 2. Constraints are separated into direct, copy, and complex. + Direct constraints are ADDRESSOF constraints that require no extra + processing, such as P = &Q + Copy constraints are those of the form P = Q. + Complex constraints are all the constraints involving dereferences + and offsets (including offsetted copies). + + 3. All direct constraints of the form P = &Q are processed, such + that Q is added to Sol(P) + + 4. All complex constraints for a given constraint variable are stored in a + linked list attached to that variable's node. + + 5. A directed graph is built out of the copy constraints. Each + constraint variable is a node in the graph, and an edge from + Q to P is added for each copy constraint of the form P = Q + + 6. The graph is then walked, and solution sets are + propagated along the copy edges, such that an edge from Q to P + causes Sol(P) <- Sol(P) union Sol(Q). + + 7. As we visit each node, all complex constraints associated with + that node are processed by adding appropriate copy edges to the graph, or the + appropriate variables to the solution set. + + 8. The process of walking the graph is iterated until no solution + sets change. + + Prior to walking the graph in steps 6 and 7, We perform static + cycle elimination on the constraint graph, as well + as off-line variable substitution. + + TODO: Adding offsets to pointer-to-structures can be handled (IE not punted + on and turned into anything), but isn't. You can just see what offset + inside the pointed-to struct it's going to access. + + TODO: Constant bounded arrays can be handled as if they were structs of the + same number of elements. + + TODO: Modeling heap and incoming pointers becomes much better if we + add fields to them as we discover them, which we could do. + + TODO: We could handle unions, but to be honest, it's probably not + worth the pain or slowdown. */ + +/* IPA-PTA optimizations possible. + + When the indirect function called is ANYTHING we can add disambiguation + based on the function signatures (or simply the parameter count which + is the varinfo size). We also do not need to consider functions that + do not have their address taken. + + The is_global_var bit which marks escape points is overly conservative + in IPA mode. Split it to is_escape_point and is_global_var - only + externally visible globals are escape points in IPA mode. + There is now is_ipa_escape_point but this is only used in a few + selected places. + + The way we introduce DECL_PT_UID to avoid fixing up all points-to + sets in the translation unit when we copy a DECL during inlining + pessimizes precision. The advantage is that the DECL_PT_UID keeps + compile-time and memory usage overhead low - the points-to sets + do not grow or get unshared as they would during a fixup phase. + An alternative solution is to delay IPA PTA until after all + inlining transformations have been applied. + + The way we propagate clobber/use information isn't optimized. + It should use a new complex constraint that properly filters + out local variables of the callee (though that would make + the sets invalid after inlining). OTOH we might as well + admit defeat to WHOPR and simply do all the clobber/use analysis + and propagation after PTA finished but before we threw away + points-to information for memory variables. WHOPR and PTA + do not play along well anyway - the whole constraint solving + would need to be done in WPA phase and it will be very interesting + to apply the results to local SSA names during LTRANS phase. + + We probably should compute a per-function unit-ESCAPE solution + propagating it simply like the clobber / uses solutions. The + solution can go alongside the non-IPA escaped solution and be + used to query which vars escape the unit through a function. + This is also required to make the escaped-HEAP trick work in IPA mode. + + We never put function decls in points-to sets so we do not + keep the set of called functions for indirect calls. + + And probably more. */ + +static bool use_field_sensitive = true; +static int in_ipa_mode = 0; + +/* Used for predecessor bitmaps. */ +static bitmap_obstack predbitmap_obstack; + +/* Used for points-to sets. */ +static bitmap_obstack pta_obstack; + +/* Used for oldsolution members of variables. */ +static bitmap_obstack oldpta_obstack; + +/* Used for per-solver-iteration bitmaps. */ +static bitmap_obstack iteration_obstack; + +static unsigned int create_variable_info_for (tree, const char *, bool); +typedef struct constraint_graph *constraint_graph_t; +static void unify_nodes (constraint_graph_t, unsigned int, unsigned int, bool); + +struct constraint; +typedef struct constraint *constraint_t; + + +#define EXECUTE_IF_IN_NONNULL_BITMAP(a, b, c, d) \ + if (a) \ + EXECUTE_IF_SET_IN_BITMAP (a, b, c, d) + +static struct constraint_stats +{ + unsigned int total_vars; + unsigned int nonpointer_vars; + unsigned int unified_vars_static; + unsigned int unified_vars_dynamic; + unsigned int iterations; + unsigned int num_edges; + unsigned int num_implicit_edges; + unsigned int points_to_sets_created; +} stats; + +struct variable_info +{ + /* ID of this variable */ + unsigned int id; + + /* True if this is a variable created by the constraint analysis, such as + heap variables and constraints we had to break up. */ + unsigned int is_artificial_var : 1; + + /* True if this is a special variable whose solution set should not be + changed. */ + unsigned int is_special_var : 1; + + /* True for variables whose size is not known or variable. */ + unsigned int is_unknown_size_var : 1; + + /* True for (sub-)fields that represent a whole variable. */ + unsigned int is_full_var : 1; + + /* True if this is a heap variable. */ + unsigned int is_heap_var : 1; + + /* True if this is a register variable. */ + unsigned int is_reg_var : 1; + + /* True if this field may contain pointers. */ + unsigned int may_have_pointers : 1; + + /* True if this field has only restrict qualified pointers. */ + unsigned int only_restrict_pointers : 1; + + /* True if this represents a heap var created for a restrict qualified + pointer. */ + unsigned int is_restrict_var : 1; + + /* True if this represents a global variable. */ + unsigned int is_global_var : 1; + + /* True if this represents a module escape point for IPA analysis. */ + unsigned int is_ipa_escape_point : 1; + + /* True if this represents a IPA function info. */ + unsigned int is_fn_info : 1; + + /* True if this appears as RHS in a ADDRESSOF constraint. */ + unsigned int address_taken : 1; + + /* ??? Store somewhere better. */ + unsigned short ruid; + + /* The ID of the variable for the next field in this structure + or zero for the last field in this structure. */ + unsigned next; + + /* The ID of the variable for the first field in this structure. */ + unsigned head; + + /* Offset of this variable, in bits, from the base variable */ + unsigned HOST_WIDE_INT offset; + + /* Size of the variable, in bits. */ + unsigned HOST_WIDE_INT size; + + /* Full size of the base variable, in bits. */ + unsigned HOST_WIDE_INT fullsize; + + /* In IPA mode the shadow UID in case the variable needs to be duplicated in + the final points-to solution because it reaches its containing + function recursively. Zero if none is needed. */ + unsigned int shadow_var_uid; + + /* Name of this variable */ + const char *name; + + /* Tree that this variable is associated with. */ + tree decl; + + /* Points-to set for this variable. */ + bitmap solution; + + /* Old points-to set for this variable. */ + bitmap oldsolution; +}; +typedef struct variable_info *varinfo_t; + +static varinfo_t first_vi_for_offset (varinfo_t, unsigned HOST_WIDE_INT); +static varinfo_t first_or_preceding_vi_for_offset (varinfo_t, + unsigned HOST_WIDE_INT); +static varinfo_t lookup_vi_for_tree (tree); +static inline bool type_can_have_subvars (const_tree); +static void make_param_constraints (varinfo_t); + +/* Pool of variable info structures. */ +static object_allocator<variable_info> variable_info_pool + ("Variable info pool"); + +/* Map varinfo to final pt_solution. */ +static hash_map<varinfo_t, pt_solution *> *final_solutions; +struct obstack final_solutions_obstack; + +/* Table of variable info structures for constraint variables. + Indexed directly by variable info id. */ +static vec<varinfo_t> varmap; + +/* Return the varmap element N */ + +static inline varinfo_t +get_varinfo (unsigned int n) +{ + return varmap[n]; +} + +/* Return the next variable in the list of sub-variables of VI + or NULL if VI is the last sub-variable. */ + +static inline varinfo_t +vi_next (varinfo_t vi) +{ + return get_varinfo (vi->next); +} + +/* Static IDs for the special variables. Variable ID zero is unused + and used as terminator for the sub-variable chain. */ +enum { nothing_id = 1, anything_id = 2, string_id = 3, + escaped_id = 4, nonlocal_id = 5, + storedanything_id = 6, integer_id = 7 }; + +/* Return a new variable info structure consisting for a variable + named NAME, and using constraint graph node NODE. Append it + to the vector of variable info structures. */ + +static varinfo_t +new_var_info (tree t, const char *name, bool add_id) +{ + unsigned index = varmap.length (); + varinfo_t ret = variable_info_pool.allocate (); + + if (dump_file && add_id) + { + char *tempname = xasprintf ("%s(%d)", name, index); + name = ggc_strdup (tempname); + free (tempname); + } + + ret->id = index; + ret->name = name; + ret->decl = t; + /* Vars without decl are artificial and do not have sub-variables. */ + ret->is_artificial_var = (t == NULL_TREE); + ret->is_special_var = false; + ret->is_unknown_size_var = false; + ret->is_full_var = (t == NULL_TREE); + ret->is_heap_var = false; + ret->may_have_pointers = true; + ret->only_restrict_pointers = false; + ret->is_restrict_var = false; + ret->ruid = 0; + ret->is_global_var = (t == NULL_TREE); + ret->is_ipa_escape_point = false; + ret->is_fn_info = false; + ret->address_taken = false; + if (t && DECL_P (t)) + ret->is_global_var = (is_global_var (t) + /* We have to treat even local register variables + as escape points. */ + || (VAR_P (t) && DECL_HARD_REGISTER (t))); + ret->is_reg_var = (t && TREE_CODE (t) == SSA_NAME); + ret->solution = BITMAP_ALLOC (&pta_obstack); + ret->oldsolution = NULL; + ret->next = 0; + ret->shadow_var_uid = 0; + ret->head = ret->id; + + stats.total_vars++; + + varmap.safe_push (ret); + + return ret; +} + +/* A map mapping call statements to per-stmt variables for uses + and clobbers specific to the call. */ +static hash_map<gimple *, varinfo_t> *call_stmt_vars; + +/* Lookup or create the variable for the call statement CALL. */ + +static varinfo_t +get_call_vi (gcall *call) +{ + varinfo_t vi, vi2; + + bool existed; + varinfo_t *slot_p = &call_stmt_vars->get_or_insert (call, &existed); + if (existed) + return *slot_p; + + vi = new_var_info (NULL_TREE, "CALLUSED", true); + vi->offset = 0; + vi->size = 1; + vi->fullsize = 2; + vi->is_full_var = true; + vi->is_reg_var = true; + + vi2 = new_var_info (NULL_TREE, "CALLCLOBBERED", true); + vi2->offset = 1; + vi2->size = 1; + vi2->fullsize = 2; + vi2->is_full_var = true; + vi2->is_reg_var = true; + + vi->next = vi2->id; + + *slot_p = vi; + return vi; +} + +/* Lookup the variable for the call statement CALL representing + the uses. Returns NULL if there is nothing special about this call. */ + +static varinfo_t +lookup_call_use_vi (gcall *call) +{ + varinfo_t *slot_p = call_stmt_vars->get (call); + if (slot_p) + return *slot_p; + + return NULL; +} + +/* Lookup the variable for the call statement CALL representing + the clobbers. Returns NULL if there is nothing special about this call. */ + +static varinfo_t +lookup_call_clobber_vi (gcall *call) +{ + varinfo_t uses = lookup_call_use_vi (call); + if (!uses) + return NULL; + + return vi_next (uses); +} + +/* Lookup or create the variable for the call statement CALL representing + the uses. */ + +static varinfo_t +get_call_use_vi (gcall *call) +{ + return get_call_vi (call); +} + +/* Lookup or create the variable for the call statement CALL representing + the clobbers. */ + +static varinfo_t ATTRIBUTE_UNUSED +get_call_clobber_vi (gcall *call) +{ + return vi_next (get_call_vi (call)); +} + + +enum constraint_expr_type {SCALAR, DEREF, ADDRESSOF}; + +/* An expression that appears in a constraint. */ + +struct constraint_expr +{ + /* Constraint type. */ + constraint_expr_type type; + + /* Variable we are referring to in the constraint. */ + unsigned int var; + + /* Offset, in bits, of this constraint from the beginning of + variables it ends up referring to. + + IOW, in a deref constraint, we would deref, get the result set, + then add OFFSET to each member. */ + HOST_WIDE_INT offset; +}; + +/* Use 0x8000... as special unknown offset. */ +#define UNKNOWN_OFFSET HOST_WIDE_INT_MIN + +typedef struct constraint_expr ce_s; +static void get_constraint_for_1 (tree, vec<ce_s> *, bool, bool); +static void get_constraint_for (tree, vec<ce_s> *); +static void get_constraint_for_rhs (tree, vec<ce_s> *); +static void do_deref (vec<ce_s> *); + +/* Our set constraints are made up of two constraint expressions, one + LHS, and one RHS. + + As described in the introduction, our set constraints each represent an + operation between set valued variables. +*/ +struct constraint +{ + struct constraint_expr lhs; + struct constraint_expr rhs; +}; + +/* List of constraints that we use to build the constraint graph from. */ + +static vec<constraint_t> constraints; +static object_allocator<constraint> constraint_pool ("Constraint pool"); + +/* The constraint graph is represented as an array of bitmaps + containing successor nodes. */ + +struct constraint_graph +{ + /* Size of this graph, which may be different than the number of + nodes in the variable map. */ + unsigned int size; + + /* Explicit successors of each node. */ + bitmap *succs; + + /* Implicit predecessors of each node (Used for variable + substitution). */ + bitmap *implicit_preds; + + /* Explicit predecessors of each node (Used for variable substitution). */ + bitmap *preds; + + /* Indirect cycle representatives, or -1 if the node has no indirect + cycles. */ + int *indirect_cycles; + + /* Representative node for a node. rep[a] == a unless the node has + been unified. */ + unsigned int *rep; + + /* Equivalence class representative for a label. This is used for + variable substitution. */ + int *eq_rep; + + /* Pointer equivalence label for a node. All nodes with the same + pointer equivalence label can be unified together at some point + (either during constraint optimization or after the constraint + graph is built). */ + unsigned int *pe; + + /* Pointer equivalence representative for a label. This is used to + handle nodes that are pointer equivalent but not location + equivalent. We can unite these once the addressof constraints + are transformed into initial points-to sets. */ + int *pe_rep; + + /* Pointer equivalence label for each node, used during variable + substitution. */ + unsigned int *pointer_label; + + /* Location equivalence label for each node, used during location + equivalence finding. */ + unsigned int *loc_label; + + /* Pointed-by set for each node, used during location equivalence + finding. This is pointed-by rather than pointed-to, because it + is constructed using the predecessor graph. */ + bitmap *pointed_by; + + /* Points to sets for pointer equivalence. This is *not* the actual + points-to sets for nodes. */ + bitmap *points_to; + + /* Bitmap of nodes where the bit is set if the node is a direct + node. Used for variable substitution. */ + sbitmap direct_nodes; + + /* Bitmap of nodes where the bit is set if the node is address + taken. Used for variable substitution. */ + bitmap address_taken; + + /* Vector of complex constraints for each graph node. Complex + constraints are those involving dereferences or offsets that are + not 0. */ + vec<constraint_t> *complex; +}; + +static constraint_graph_t graph; + +/* During variable substitution and the offline version of indirect + cycle finding, we create nodes to represent dereferences and + address taken constraints. These represent where these start and + end. */ +#define FIRST_REF_NODE (varmap).length () +#define LAST_REF_NODE (FIRST_REF_NODE + (FIRST_REF_NODE - 1)) + +/* Return the representative node for NODE, if NODE has been unioned + with another NODE. + This function performs path compression along the way to finding + the representative. */ + +static unsigned int +find (unsigned int node) +{ + gcc_checking_assert (node < graph->size); + if (graph->rep[node] != node) + return graph->rep[node] = find (graph->rep[node]); + return node; +} + +/* Union the TO and FROM nodes to the TO nodes. + Note that at some point in the future, we may want to do + union-by-rank, in which case we are going to have to return the + node we unified to. */ + +static bool +unite (unsigned int to, unsigned int from) +{ + gcc_checking_assert (to < graph->size && from < graph->size); + if (to != from && graph->rep[from] != to) + { + graph->rep[from] = to; + return true; + } + return false; +} + +/* Create a new constraint consisting of LHS and RHS expressions. */ + +static constraint_t +new_constraint (const struct constraint_expr lhs, + const struct constraint_expr rhs) +{ + constraint_t ret = constraint_pool.allocate (); + ret->lhs = lhs; + ret->rhs = rhs; + return ret; +} + +/* Print out constraint C to FILE. */ + +static void +dump_constraint (FILE *file, constraint_t c) +{ + if (c->lhs.type == ADDRESSOF) + fprintf (file, "&"); + else if (c->lhs.type == DEREF) + fprintf (file, "*"); + if (dump_file) + fprintf (file, "%s", get_varinfo (c->lhs.var)->name); + else + fprintf (file, "V%d", c->lhs.var); + if (c->lhs.offset == UNKNOWN_OFFSET) + fprintf (file, " + UNKNOWN"); + else if (c->lhs.offset != 0) + fprintf (file, " + " HOST_WIDE_INT_PRINT_DEC, c->lhs.offset); + fprintf (file, " = "); + if (c->rhs.type == ADDRESSOF) + fprintf (file, "&"); + else if (c->rhs.type == DEREF) + fprintf (file, "*"); + if (dump_file) + fprintf (file, "%s", get_varinfo (c->rhs.var)->name); + else + fprintf (file, "V%d", c->rhs.var); + if (c->rhs.offset == UNKNOWN_OFFSET) + fprintf (file, " + UNKNOWN"); + else if (c->rhs.offset != 0) + fprintf (file, " + " HOST_WIDE_INT_PRINT_DEC, c->rhs.offset); +} + + +void debug_constraint (constraint_t); +void debug_constraints (void); +void debug_constraint_graph (void); +void debug_solution_for_var (unsigned int); +void debug_sa_points_to_info (void); +void debug_varinfo (varinfo_t); +void debug_varmap (void); + +/* Print out constraint C to stderr. */ + +DEBUG_FUNCTION void +debug_constraint (constraint_t c) +{ + dump_constraint (stderr, c); + fprintf (stderr, "\n"); +} + +/* Print out all constraints to FILE */ + +static void +dump_constraints (FILE *file, int from) +{ + int i; + constraint_t c; + for (i = from; constraints.iterate (i, &c); i++) + if (c) + { + dump_constraint (file, c); + fprintf (file, "\n"); + } +} + +/* Print out all constraints to stderr. */ + +DEBUG_FUNCTION void +debug_constraints (void) +{ + dump_constraints (stderr, 0); +} + +/* Print the constraint graph in dot format. */ + +static void +dump_constraint_graph (FILE *file) +{ + unsigned int i; + + /* Only print the graph if it has already been initialized: */ + if (!graph) + return; + + /* Prints the header of the dot file: */ + fprintf (file, "strict digraph {\n"); + fprintf (file, " node [\n shape = box\n ]\n"); + fprintf (file, " edge [\n fontsize = \"12\"\n ]\n"); + fprintf (file, "\n // List of nodes and complex constraints in " + "the constraint graph:\n"); + + /* The next lines print the nodes in the graph together with the + complex constraints attached to them. */ + for (i = 1; i < graph->size; i++) + { + if (i == FIRST_REF_NODE) + continue; + if (find (i) != i) + continue; + if (i < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name); + if (graph->complex[i].exists ()) + { + unsigned j; + constraint_t c; + fprintf (file, " [label=\"\\N\\n"); + for (j = 0; graph->complex[i].iterate (j, &c); ++j) + { + dump_constraint (file, c); + fprintf (file, "\\l"); + } + fprintf (file, "\"]"); + } + fprintf (file, ";\n"); + } + + /* Go over the edges. */ + fprintf (file, "\n // Edges in the constraint graph:\n"); + for (i = 1; i < graph->size; i++) + { + unsigned j; + bitmap_iterator bi; + if (find (i) != i) + continue; + EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i], 0, j, bi) + { + unsigned to = find (j); + if (i == to) + continue; + if (i < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name); + fprintf (file, " -> "); + if (to < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (to)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (to - FIRST_REF_NODE)->name); + fprintf (file, ";\n"); + } + } + + /* Prints the tail of the dot file. */ + fprintf (file, "}\n"); +} + +/* Print out the constraint graph to stderr. */ + +DEBUG_FUNCTION void +debug_constraint_graph (void) +{ + dump_constraint_graph (stderr); +} + +/* SOLVER FUNCTIONS + + The solver is a simple worklist solver, that works on the following + algorithm: + + sbitmap changed_nodes = all zeroes; + changed_count = 0; + For each node that is not already collapsed: + changed_count++; + set bit in changed nodes + + while (changed_count > 0) + { + compute topological ordering for constraint graph + + find and collapse cycles in the constraint graph (updating + changed if necessary) + + for each node (n) in the graph in topological order: + changed_count--; + + Process each complex constraint associated with the node, + updating changed if necessary. + + For each outgoing edge from n, propagate the solution from n to + the destination of the edge, updating changed as necessary. + + } */ + +/* Return true if two constraint expressions A and B are equal. */ + +static bool +constraint_expr_equal (struct constraint_expr a, struct constraint_expr b) +{ + return a.type == b.type && a.var == b.var && a.offset == b.offset; +} + +/* Return true if constraint expression A is less than constraint expression + B. This is just arbitrary, but consistent, in order to give them an + ordering. */ + +static bool +constraint_expr_less (struct constraint_expr a, struct constraint_expr b) +{ + if (a.type == b.type) + { + if (a.var == b.var) + return a.offset < b.offset; + else + return a.var < b.var; + } + else + return a.type < b.type; +} + +/* Return true if constraint A is less than constraint B. This is just + arbitrary, but consistent, in order to give them an ordering. */ + +static bool +constraint_less (const constraint_t &a, const constraint_t &b) +{ + if (constraint_expr_less (a->lhs, b->lhs)) + return true; + else if (constraint_expr_less (b->lhs, a->lhs)) + return false; + else + return constraint_expr_less (a->rhs, b->rhs); +} + +/* Return true if two constraints A and B are equal. */ + +static bool +constraint_equal (struct constraint a, struct constraint b) +{ + return constraint_expr_equal (a.lhs, b.lhs) + && constraint_expr_equal (a.rhs, b.rhs); +} + + +/* Find a constraint LOOKFOR in the sorted constraint vector VEC */ + +static constraint_t +constraint_vec_find (vec<constraint_t> vec, + struct constraint lookfor) +{ + unsigned int place; + constraint_t found; + + if (!vec.exists ()) + return NULL; + + place = vec.lower_bound (&lookfor, constraint_less); + if (place >= vec.length ()) + return NULL; + found = vec[place]; + if (!constraint_equal (*found, lookfor)) + return NULL; + return found; +} + +/* Union two constraint vectors, TO and FROM. Put the result in TO. + Returns true of TO set is changed. */ + +static bool +constraint_set_union (vec<constraint_t> *to, + vec<constraint_t> *from) +{ + int i; + constraint_t c; + bool any_change = false; + + FOR_EACH_VEC_ELT (*from, i, c) + { + if (constraint_vec_find (*to, *c) == NULL) + { + unsigned int place = to->lower_bound (c, constraint_less); + to->safe_insert (place, c); + any_change = true; + } + } + return any_change; +} + +/* Expands the solution in SET to all sub-fields of variables included. */ + +static bitmap +solution_set_expand (bitmap set, bitmap *expanded) +{ + bitmap_iterator bi; + unsigned j; + + if (*expanded) + return *expanded; + + *expanded = BITMAP_ALLOC (&iteration_obstack); + + /* In a first pass expand to the head of the variables we need to + add all sub-fields off. This avoids quadratic behavior. */ + EXECUTE_IF_SET_IN_BITMAP (set, 0, j, bi) + { + varinfo_t v = get_varinfo (j); + if (v->is_artificial_var + || v->is_full_var) + continue; + bitmap_set_bit (*expanded, v->head); + } + + /* In the second pass now expand all head variables with subfields. */ + EXECUTE_IF_SET_IN_BITMAP (*expanded, 0, j, bi) + { + varinfo_t v = get_varinfo (j); + if (v->head != j) + continue; + for (v = vi_next (v); v != NULL; v = vi_next (v)) + bitmap_set_bit (*expanded, v->id); + } + + /* And finally set the rest of the bits from SET. */ + bitmap_ior_into (*expanded, set); + + return *expanded; +} + +/* Union solution sets TO and DELTA, and add INC to each member of DELTA in the + process. */ + +static bool +set_union_with_increment (bitmap to, bitmap delta, HOST_WIDE_INT inc, + bitmap *expanded_delta) +{ + bool changed = false; + bitmap_iterator bi; + unsigned int i; + + /* If the solution of DELTA contains anything it is good enough to transfer + this to TO. */ + if (bitmap_bit_p (delta, anything_id)) + return bitmap_set_bit (to, anything_id); + + /* If the offset is unknown we have to expand the solution to + all subfields. */ + if (inc == UNKNOWN_OFFSET) + { + delta = solution_set_expand (delta, expanded_delta); + changed |= bitmap_ior_into (to, delta); + return changed; + } + + /* For non-zero offset union the offsetted solution into the destination. */ + EXECUTE_IF_SET_IN_BITMAP (delta, 0, i, bi) + { + varinfo_t vi = get_varinfo (i); + + /* If this is a variable with just one field just set its bit + in the result. */ + if (vi->is_artificial_var + || vi->is_unknown_size_var + || vi->is_full_var) + changed |= bitmap_set_bit (to, i); + else + { + HOST_WIDE_INT fieldoffset = vi->offset + inc; + unsigned HOST_WIDE_INT size = vi->size; + + /* If the offset makes the pointer point to before the + variable use offset zero for the field lookup. */ + if (fieldoffset < 0) + vi = get_varinfo (vi->head); + else + vi = first_or_preceding_vi_for_offset (vi, fieldoffset); + + do + { + changed |= bitmap_set_bit (to, vi->id); + if (vi->is_full_var + || vi->next == 0) + break; + + /* We have to include all fields that overlap the current field + shifted by inc. */ + vi = vi_next (vi); + } + while (vi->offset < fieldoffset + size); + } + } + + return changed; +} + +/* Insert constraint C into the list of complex constraints for graph + node VAR. */ + +static void +insert_into_complex (constraint_graph_t graph, + unsigned int var, constraint_t c) +{ + vec<constraint_t> complex = graph->complex[var]; + unsigned int place = complex.lower_bound (c, constraint_less); + + /* Only insert constraints that do not already exist. */ + if (place >= complex.length () + || !constraint_equal (*c, *complex[place])) + graph->complex[var].safe_insert (place, c); +} + + +/* Condense two variable nodes into a single variable node, by moving + all associated info from FROM to TO. Returns true if TO node's + constraint set changes after the merge. */ + +static bool +merge_node_constraints (constraint_graph_t graph, unsigned int to, + unsigned int from) +{ + unsigned int i; + constraint_t c; + bool any_change = false; + + gcc_checking_assert (find (from) == to); + + /* Move all complex constraints from src node into to node */ + FOR_EACH_VEC_ELT (graph->complex[from], i, c) + { + /* In complex constraints for node FROM, we may have either + a = *FROM, and *FROM = a, or an offseted constraint which are + always added to the rhs node's constraints. */ + + if (c->rhs.type == DEREF) + c->rhs.var = to; + else if (c->lhs.type == DEREF) + c->lhs.var = to; + else + c->rhs.var = to; + + } + any_change = constraint_set_union (&graph->complex[to], + &graph->complex[from]); + graph->complex[from].release (); + return any_change; +} + + +/* Remove edges involving NODE from GRAPH. */ + +static void +clear_edges_for_node (constraint_graph_t graph, unsigned int node) +{ + if (graph->succs[node]) + BITMAP_FREE (graph->succs[node]); +} + +/* Merge GRAPH nodes FROM and TO into node TO. */ + +static void +merge_graph_nodes (constraint_graph_t graph, unsigned int to, + unsigned int from) +{ + if (graph->indirect_cycles[from] != -1) + { + /* If we have indirect cycles with the from node, and we have + none on the to node, the to node has indirect cycles from the + from node now that they are unified. + If indirect cycles exist on both, unify the nodes that they + are in a cycle with, since we know they are in a cycle with + each other. */ + if (graph->indirect_cycles[to] == -1) + graph->indirect_cycles[to] = graph->indirect_cycles[from]; + } + + /* Merge all the successor edges. */ + if (graph->succs[from]) + { + if (!graph->succs[to]) + graph->succs[to] = BITMAP_ALLOC (&pta_obstack); + bitmap_ior_into (graph->succs[to], + graph->succs[from]); + } + + clear_edges_for_node (graph, from); +} + + +/* Add an indirect graph edge to GRAPH, going from TO to FROM if + it doesn't exist in the graph already. */ + +static void +add_implicit_graph_edge (constraint_graph_t graph, unsigned int to, + unsigned int from) +{ + if (to == from) + return; + + if (!graph->implicit_preds[to]) + graph->implicit_preds[to] = BITMAP_ALLOC (&predbitmap_obstack); + + if (bitmap_set_bit (graph->implicit_preds[to], from)) + stats.num_implicit_edges++; +} + +/* Add a predecessor graph edge to GRAPH, going from TO to FROM if + it doesn't exist in the graph already. + Return false if the edge already existed, true otherwise. */ + +static void +add_pred_graph_edge (constraint_graph_t graph, unsigned int to, + unsigned int from) +{ + if (!graph->preds[to]) + graph->preds[to] = BITMAP_ALLOC (&predbitmap_obstack); + bitmap_set_bit (graph->preds[to], from); +} + +/* Add a graph edge to GRAPH, going from FROM to TO if + it doesn't exist in the graph already. + Return false if the edge already existed, true otherwise. */ + +static bool +add_graph_edge (constraint_graph_t graph, unsigned int to, + unsigned int from) +{ + if (to == from) + { + return false; + } + else + { + bool r = false; + + if (!graph->succs[from]) + graph->succs[from] = BITMAP_ALLOC (&pta_obstack); + + /* The graph solving process does not avoid "triangles", thus + there can be multiple paths from a node to another involving + intermediate other nodes. That causes extra copying which is + most difficult to avoid when the intermediate node is ESCAPED + because there are no edges added from ESCAPED. Avoid + adding the direct edge FROM -> TO when we have FROM -> ESCAPED + and TO contains ESCAPED. + ??? Note this is only a heuristic, it does not prevent the + situation from occuring. The heuristic helps PR38474 and + PR99912 significantly. */ + if (to < FIRST_REF_NODE + && bitmap_bit_p (graph->succs[from], find (escaped_id)) + && bitmap_bit_p (get_varinfo (find (to))->solution, escaped_id)) + return false; + + if (bitmap_set_bit (graph->succs[from], to)) + { + r = true; + if (to < FIRST_REF_NODE && from < FIRST_REF_NODE) + stats.num_edges++; + } + return r; + } +} + + +/* Initialize the constraint graph structure to contain SIZE nodes. */ + +static void +init_graph (unsigned int size) +{ + unsigned int j; + + graph = XCNEW (struct constraint_graph); + graph->size = size; + graph->succs = XCNEWVEC (bitmap, graph->size); + graph->indirect_cycles = XNEWVEC (int, graph->size); + graph->rep = XNEWVEC (unsigned int, graph->size); + /* ??? Macros do not support template types with multiple arguments, + so we use a typedef to work around it. */ + typedef vec<constraint_t> vec_constraint_t_heap; + graph->complex = XCNEWVEC (vec_constraint_t_heap, size); + graph->pe = XCNEWVEC (unsigned int, graph->size); + graph->pe_rep = XNEWVEC (int, graph->size); + + for (j = 0; j < graph->size; j++) + { + graph->rep[j] = j; + graph->pe_rep[j] = -1; + graph->indirect_cycles[j] = -1; + } +} + +/* Build the constraint graph, adding only predecessor edges right now. */ + +static void +build_pred_graph (void) +{ + int i; + constraint_t c; + unsigned int j; + + graph->implicit_preds = XCNEWVEC (bitmap, graph->size); + graph->preds = XCNEWVEC (bitmap, graph->size); + graph->pointer_label = XCNEWVEC (unsigned int, graph->size); + graph->loc_label = XCNEWVEC (unsigned int, graph->size); + graph->pointed_by = XCNEWVEC (bitmap, graph->size); + graph->points_to = XCNEWVEC (bitmap, graph->size); + graph->eq_rep = XNEWVEC (int, graph->size); + graph->direct_nodes = sbitmap_alloc (graph->size); + graph->address_taken = BITMAP_ALLOC (&predbitmap_obstack); + bitmap_clear (graph->direct_nodes); + + for (j = 1; j < FIRST_REF_NODE; j++) + { + if (!get_varinfo (j)->is_special_var) + bitmap_set_bit (graph->direct_nodes, j); + } + + for (j = 0; j < graph->size; j++) + graph->eq_rep[j] = -1; + + for (j = 0; j < varmap.length (); j++) + graph->indirect_cycles[j] = -1; + + FOR_EACH_VEC_ELT (constraints, i, c) + { + struct constraint_expr lhs = c->lhs; + struct constraint_expr rhs = c->rhs; + unsigned int lhsvar = lhs.var; + unsigned int rhsvar = rhs.var; + + if (lhs.type == DEREF) + { + /* *x = y. */ + if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR) + add_pred_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar); + } + else if (rhs.type == DEREF) + { + /* x = *y */ + if (rhs.offset == 0 && lhs.offset == 0 && lhs.type == SCALAR) + add_pred_graph_edge (graph, lhsvar, FIRST_REF_NODE + rhsvar); + else + bitmap_clear_bit (graph->direct_nodes, lhsvar); + } + else if (rhs.type == ADDRESSOF) + { + varinfo_t v; + + /* x = &y */ + if (graph->points_to[lhsvar] == NULL) + graph->points_to[lhsvar] = BITMAP_ALLOC (&predbitmap_obstack); + bitmap_set_bit (graph->points_to[lhsvar], rhsvar); + + if (graph->pointed_by[rhsvar] == NULL) + graph->pointed_by[rhsvar] = BITMAP_ALLOC (&predbitmap_obstack); + bitmap_set_bit (graph->pointed_by[rhsvar], lhsvar); + + /* Implicitly, *x = y */ + add_implicit_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar); + + /* All related variables are no longer direct nodes. */ + bitmap_clear_bit (graph->direct_nodes, rhsvar); + v = get_varinfo (rhsvar); + if (!v->is_full_var) + { + v = get_varinfo (v->head); + do + { + bitmap_clear_bit (graph->direct_nodes, v->id); + v = vi_next (v); + } + while (v != NULL); + } + bitmap_set_bit (graph->address_taken, rhsvar); + } + else if (lhsvar > anything_id + && lhsvar != rhsvar && lhs.offset == 0 && rhs.offset == 0) + { + /* x = y */ + add_pred_graph_edge (graph, lhsvar, rhsvar); + /* Implicitly, *x = *y */ + add_implicit_graph_edge (graph, FIRST_REF_NODE + lhsvar, + FIRST_REF_NODE + rhsvar); + } + else if (lhs.offset != 0 || rhs.offset != 0) + { + if (rhs.offset != 0) + bitmap_clear_bit (graph->direct_nodes, lhs.var); + else if (lhs.offset != 0) + bitmap_clear_bit (graph->direct_nodes, rhs.var); + } + } +} + +/* Build the constraint graph, adding successor edges. */ + +static void +build_succ_graph (void) +{ + unsigned i, t; + constraint_t c; + + FOR_EACH_VEC_ELT (constraints, i, c) + { + struct constraint_expr lhs; + struct constraint_expr rhs; + unsigned int lhsvar; + unsigned int rhsvar; + + if (!c) + continue; + + lhs = c->lhs; + rhs = c->rhs; + lhsvar = find (lhs.var); + rhsvar = find (rhs.var); + + if (lhs.type == DEREF) + { + if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR) + add_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar); + } + else if (rhs.type == DEREF) + { + if (rhs.offset == 0 && lhs.offset == 0 && lhs.type == SCALAR) + add_graph_edge (graph, lhsvar, FIRST_REF_NODE + rhsvar); + } + else if (rhs.type == ADDRESSOF) + { + /* x = &y */ + gcc_checking_assert (find (rhs.var) == rhs.var); + bitmap_set_bit (get_varinfo (lhsvar)->solution, rhsvar); + } + else if (lhsvar > anything_id + && lhsvar != rhsvar && lhs.offset == 0 && rhs.offset == 0) + { + add_graph_edge (graph, lhsvar, rhsvar); + } + } + + /* Add edges from STOREDANYTHING to all non-direct nodes that can + receive pointers. */ + t = find (storedanything_id); + for (i = integer_id + 1; i < FIRST_REF_NODE; ++i) + { + if (!bitmap_bit_p (graph->direct_nodes, i) + && get_varinfo (i)->may_have_pointers) + add_graph_edge (graph, find (i), t); + } + + /* Everything stored to ANYTHING also potentially escapes. */ + add_graph_edge (graph, find (escaped_id), t); +} + + +/* Changed variables on the last iteration. */ +static bitmap changed; + +/* Strongly Connected Component visitation info. */ + +class scc_info +{ +public: + scc_info (size_t size); + ~scc_info (); + + auto_sbitmap visited; + auto_sbitmap deleted; + unsigned int *dfs; + unsigned int *node_mapping; + int current_index; + auto_vec<unsigned> scc_stack; +}; + + +/* Recursive routine to find strongly connected components in GRAPH. + SI is the SCC info to store the information in, and N is the id of current + graph node we are processing. + + This is Tarjan's strongly connected component finding algorithm, as + modified by Nuutila to keep only non-root nodes on the stack. + The algorithm can be found in "On finding the strongly connected + connected components in a directed graph" by Esko Nuutila and Eljas + Soisalon-Soininen, in Information Processing Letters volume 49, + number 1, pages 9-14. */ + +static void +scc_visit (constraint_graph_t graph, class scc_info *si, unsigned int n) +{ + unsigned int i; + bitmap_iterator bi; + unsigned int my_dfs; + + bitmap_set_bit (si->visited, n); + si->dfs[n] = si->current_index ++; + my_dfs = si->dfs[n]; + + /* Visit all the successors. */ + EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[n], 0, i, bi) + { + unsigned int w; + + if (i > LAST_REF_NODE) + break; + + w = find (i); + if (bitmap_bit_p (si->deleted, w)) + continue; + + if (!bitmap_bit_p (si->visited, w)) + scc_visit (graph, si, w); + + unsigned int t = find (w); + gcc_checking_assert (find (n) == n); + if (si->dfs[t] < si->dfs[n]) + si->dfs[n] = si->dfs[t]; + } + + /* See if any components have been identified. */ + if (si->dfs[n] == my_dfs) + { + if (si->scc_stack.length () > 0 + && si->dfs[si->scc_stack.last ()] >= my_dfs) + { + bitmap scc = BITMAP_ALLOC (NULL); + unsigned int lowest_node; + bitmap_iterator bi; + + bitmap_set_bit (scc, n); + + while (si->scc_stack.length () != 0 + && si->dfs[si->scc_stack.last ()] >= my_dfs) + { + unsigned int w = si->scc_stack.pop (); + + bitmap_set_bit (scc, w); + } + + lowest_node = bitmap_first_set_bit (scc); + gcc_assert (lowest_node < FIRST_REF_NODE); + + /* Collapse the SCC nodes into a single node, and mark the + indirect cycles. */ + EXECUTE_IF_SET_IN_BITMAP (scc, 0, i, bi) + { + if (i < FIRST_REF_NODE) + { + if (unite (lowest_node, i)) + unify_nodes (graph, lowest_node, i, false); + } + else + { + unite (lowest_node, i); + graph->indirect_cycles[i - FIRST_REF_NODE] = lowest_node; + } + } + } + bitmap_set_bit (si->deleted, n); + } + else + si->scc_stack.safe_push (n); +} + +/* Unify node FROM into node TO, updating the changed count if + necessary when UPDATE_CHANGED is true. */ + +static void +unify_nodes (constraint_graph_t graph, unsigned int to, unsigned int from, + bool update_changed) +{ + gcc_checking_assert (to != from && find (to) == to); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Unifying %s to %s\n", + get_varinfo (from)->name, + get_varinfo (to)->name); + + if (update_changed) + stats.unified_vars_dynamic++; + else + stats.unified_vars_static++; + + merge_graph_nodes (graph, to, from); + if (merge_node_constraints (graph, to, from)) + { + if (update_changed) + bitmap_set_bit (changed, to); + } + + /* Mark TO as changed if FROM was changed. If TO was already marked + as changed, decrease the changed count. */ + + if (update_changed + && bitmap_clear_bit (changed, from)) + bitmap_set_bit (changed, to); + varinfo_t fromvi = get_varinfo (from); + if (fromvi->solution) + { + /* If the solution changes because of the merging, we need to mark + the variable as changed. */ + varinfo_t tovi = get_varinfo (to); + if (bitmap_ior_into (tovi->solution, fromvi->solution)) + { + if (update_changed) + bitmap_set_bit (changed, to); + } + + BITMAP_FREE (fromvi->solution); + if (fromvi->oldsolution) + BITMAP_FREE (fromvi->oldsolution); + + if (stats.iterations > 0 + && tovi->oldsolution) + BITMAP_FREE (tovi->oldsolution); + } + if (graph->succs[to]) + bitmap_clear_bit (graph->succs[to], to); +} + +/* Information needed to compute the topological ordering of a graph. */ + +struct topo_info +{ + /* sbitmap of visited nodes. */ + sbitmap visited; + /* Array that stores the topological order of the graph, *in + reverse*. */ + vec<unsigned> topo_order; +}; + + +/* Initialize and return a topological info structure. */ + +static struct topo_info * +init_topo_info (void) +{ + size_t size = graph->size; + struct topo_info *ti = XNEW (struct topo_info); + ti->visited = sbitmap_alloc (size); + bitmap_clear (ti->visited); + ti->topo_order.create (1); + return ti; +} + + +/* Free the topological sort info pointed to by TI. */ + +static void +free_topo_info (struct topo_info *ti) +{ + sbitmap_free (ti->visited); + ti->topo_order.release (); + free (ti); +} + +/* Visit the graph in topological order, and store the order in the + topo_info structure. */ + +static void +topo_visit (constraint_graph_t graph, struct topo_info *ti, + unsigned int n) +{ + bitmap_iterator bi; + unsigned int j; + + bitmap_set_bit (ti->visited, n); + + if (graph->succs[n]) + EXECUTE_IF_SET_IN_BITMAP (graph->succs[n], 0, j, bi) + { + if (!bitmap_bit_p (ti->visited, j)) + topo_visit (graph, ti, j); + } + + ti->topo_order.safe_push (n); +} + +/* Process a constraint C that represents x = *(y + off), using DELTA as the + starting solution for y. */ + +static void +do_sd_constraint (constraint_graph_t graph, constraint_t c, + bitmap delta, bitmap *expanded_delta) +{ + unsigned int lhs = c->lhs.var; + bool flag = false; + bitmap sol = get_varinfo (lhs)->solution; + unsigned int j; + bitmap_iterator bi; + HOST_WIDE_INT roffset = c->rhs.offset; + + /* Our IL does not allow this. */ + gcc_checking_assert (c->lhs.offset == 0); + + /* If the solution of Y contains anything it is good enough to transfer + this to the LHS. */ + if (bitmap_bit_p (delta, anything_id)) + { + flag |= bitmap_set_bit (sol, anything_id); + goto done; + } + + /* If we do not know at with offset the rhs is dereferenced compute + the reachability set of DELTA, conservatively assuming it is + dereferenced at all valid offsets. */ + if (roffset == UNKNOWN_OFFSET) + { + delta = solution_set_expand (delta, expanded_delta); + /* No further offset processing is necessary. */ + roffset = 0; + } + + /* For each variable j in delta (Sol(y)), add + an edge in the graph from j to x, and union Sol(j) into Sol(x). */ + EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi) + { + varinfo_t v = get_varinfo (j); + HOST_WIDE_INT fieldoffset = v->offset + roffset; + unsigned HOST_WIDE_INT size = v->size; + unsigned int t; + + if (v->is_full_var) + ; + else if (roffset != 0) + { + if (fieldoffset < 0) + v = get_varinfo (v->head); + else + v = first_or_preceding_vi_for_offset (v, fieldoffset); + } + + /* We have to include all fields that overlap the current field + shifted by roffset. */ + do + { + t = find (v->id); + + /* Adding edges from the special vars is pointless. + They don't have sets that can change. */ + if (get_varinfo (t)->is_special_var) + flag |= bitmap_ior_into (sol, get_varinfo (t)->solution); + /* Merging the solution from ESCAPED needlessly increases + the set. Use ESCAPED as representative instead. */ + else if (v->id == escaped_id) + flag |= bitmap_set_bit (sol, escaped_id); + else if (v->may_have_pointers + && add_graph_edge (graph, lhs, t)) + flag |= bitmap_ior_into (sol, get_varinfo (t)->solution); + + if (v->is_full_var + || v->next == 0) + break; + + v = vi_next (v); + } + while (v->offset < fieldoffset + size); + } + +done: + /* If the LHS solution changed, mark the var as changed. */ + if (flag) + { + get_varinfo (lhs)->solution = sol; + bitmap_set_bit (changed, lhs); + } +} + +/* Process a constraint C that represents *(x + off) = y using DELTA + as the starting solution for x. */ + +static void +do_ds_constraint (constraint_t c, bitmap delta, bitmap *expanded_delta) +{ + unsigned int rhs = c->rhs.var; + bitmap sol = get_varinfo (rhs)->solution; + unsigned int j; + bitmap_iterator bi; + HOST_WIDE_INT loff = c->lhs.offset; + bool escaped_p = false; + + /* Our IL does not allow this. */ + gcc_checking_assert (c->rhs.offset == 0); + + /* If the solution of y contains ANYTHING simply use the ANYTHING + solution. This avoids needlessly increasing the points-to sets. */ + if (bitmap_bit_p (sol, anything_id)) + sol = get_varinfo (find (anything_id))->solution; + + /* If the solution for x contains ANYTHING we have to merge the + solution of y into all pointer variables which we do via + STOREDANYTHING. */ + if (bitmap_bit_p (delta, anything_id)) + { + unsigned t = find (storedanything_id); + if (add_graph_edge (graph, t, rhs)) + { + if (bitmap_ior_into (get_varinfo (t)->solution, sol)) + bitmap_set_bit (changed, t); + } + return; + } + + /* If we do not know at with offset the rhs is dereferenced compute + the reachability set of DELTA, conservatively assuming it is + dereferenced at all valid offsets. */ + if (loff == UNKNOWN_OFFSET) + { + delta = solution_set_expand (delta, expanded_delta); + loff = 0; + } + + /* For each member j of delta (Sol(x)), add an edge from y to j and + union Sol(y) into Sol(j) */ + EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi) + { + varinfo_t v = get_varinfo (j); + unsigned int t; + HOST_WIDE_INT fieldoffset = v->offset + loff; + unsigned HOST_WIDE_INT size = v->size; + + if (v->is_full_var) + ; + else if (loff != 0) + { + if (fieldoffset < 0) + v = get_varinfo (v->head); + else + v = first_or_preceding_vi_for_offset (v, fieldoffset); + } + + /* We have to include all fields that overlap the current field + shifted by loff. */ + do + { + if (v->may_have_pointers) + { + /* If v is a global variable then this is an escape point. */ + if (v->is_global_var + && !escaped_p) + { + t = find (escaped_id); + if (add_graph_edge (graph, t, rhs) + && bitmap_ior_into (get_varinfo (t)->solution, sol)) + bitmap_set_bit (changed, t); + /* Enough to let rhs escape once. */ + escaped_p = true; + } + + if (v->is_special_var) + break; + + t = find (v->id); + if (add_graph_edge (graph, t, rhs) + && bitmap_ior_into (get_varinfo (t)->solution, sol)) + bitmap_set_bit (changed, t); + } + + if (v->is_full_var + || v->next == 0) + break; + + v = vi_next (v); + } + while (v->offset < fieldoffset + size); + } +} + +/* Handle a non-simple (simple meaning requires no iteration), + constraint (IE *x = &y, x = *y, *x = y, and x = y with offsets involved). */ + +static void +do_complex_constraint (constraint_graph_t graph, constraint_t c, bitmap delta, + bitmap *expanded_delta) +{ + if (c->lhs.type == DEREF) + { + if (c->rhs.type == ADDRESSOF) + { + gcc_unreachable (); + } + else + { + /* *x = y */ + do_ds_constraint (c, delta, expanded_delta); + } + } + else if (c->rhs.type == DEREF) + { + /* x = *y */ + if (!(get_varinfo (c->lhs.var)->is_special_var)) + do_sd_constraint (graph, c, delta, expanded_delta); + } + else + { + bitmap tmp; + bool flag = false; + + gcc_checking_assert (c->rhs.type == SCALAR && c->lhs.type == SCALAR + && c->rhs.offset != 0 && c->lhs.offset == 0); + tmp = get_varinfo (c->lhs.var)->solution; + + flag = set_union_with_increment (tmp, delta, c->rhs.offset, + expanded_delta); + + if (flag) + bitmap_set_bit (changed, c->lhs.var); + } +} + +/* Initialize and return a new SCC info structure. */ + +scc_info::scc_info (size_t size) : + visited (size), deleted (size), current_index (0), scc_stack (1) +{ + bitmap_clear (visited); + bitmap_clear (deleted); + node_mapping = XNEWVEC (unsigned int, size); + dfs = XCNEWVEC (unsigned int, size); + + for (size_t i = 0; i < size; i++) + node_mapping[i] = i; +} + +/* Free an SCC info structure pointed to by SI */ + +scc_info::~scc_info () +{ + free (node_mapping); + free (dfs); +} + + +/* Find indirect cycles in GRAPH that occur, using strongly connected + components, and note them in the indirect cycles map. + + This technique comes from Ben Hardekopf and Calvin Lin, + "It Pays to be Lazy: Fast and Accurate Pointer Analysis for Millions of + Lines of Code", submitted to PLDI 2007. */ + +static void +find_indirect_cycles (constraint_graph_t graph) +{ + unsigned int i; + unsigned int size = graph->size; + scc_info si (size); + + for (i = 0; i < MIN (LAST_REF_NODE, size); i ++ ) + if (!bitmap_bit_p (si.visited, i) && find (i) == i) + scc_visit (graph, &si, i); +} + +/* Compute a topological ordering for GRAPH, and store the result in the + topo_info structure TI. */ + +static void +compute_topo_order (constraint_graph_t graph, + struct topo_info *ti) +{ + unsigned int i; + unsigned int size = graph->size; + + for (i = 0; i != size; ++i) + if (!bitmap_bit_p (ti->visited, i) && find (i) == i) + topo_visit (graph, ti, i); +} + +/* Structure used to for hash value numbering of pointer equivalence + classes. */ + +typedef struct equiv_class_label +{ + hashval_t hashcode; + unsigned int equivalence_class; + bitmap labels; +} *equiv_class_label_t; +typedef const struct equiv_class_label *const_equiv_class_label_t; + +/* Equiv_class_label hashtable helpers. */ + +struct equiv_class_hasher : nofree_ptr_hash <equiv_class_label> +{ + static inline hashval_t hash (const equiv_class_label *); + static inline bool equal (const equiv_class_label *, + const equiv_class_label *); +}; + +/* Hash function for a equiv_class_label_t */ + +inline hashval_t +equiv_class_hasher::hash (const equiv_class_label *ecl) +{ + return ecl->hashcode; +} + +/* Equality function for two equiv_class_label_t's. */ + +inline bool +equiv_class_hasher::equal (const equiv_class_label *eql1, + const equiv_class_label *eql2) +{ + return (eql1->hashcode == eql2->hashcode + && bitmap_equal_p (eql1->labels, eql2->labels)); +} + +/* A hashtable for mapping a bitmap of labels->pointer equivalence + classes. */ +static hash_table<equiv_class_hasher> *pointer_equiv_class_table; + +/* A hashtable for mapping a bitmap of labels->location equivalence + classes. */ +static hash_table<equiv_class_hasher> *location_equiv_class_table; + +struct obstack equiv_class_obstack; + +/* Lookup a equivalence class in TABLE by the bitmap of LABELS with + hash HAS it contains. Sets *REF_LABELS to the bitmap LABELS + is equivalent to. */ + +static equiv_class_label * +equiv_class_lookup_or_add (hash_table<equiv_class_hasher> *table, + bitmap labels) +{ + equiv_class_label **slot; + equiv_class_label ecl; + + ecl.labels = labels; + ecl.hashcode = bitmap_hash (labels); + slot = table->find_slot (&ecl, INSERT); + if (!*slot) + { + *slot = XOBNEW (&equiv_class_obstack, struct equiv_class_label); + (*slot)->labels = labels; + (*slot)->hashcode = ecl.hashcode; + (*slot)->equivalence_class = 0; + } + + return *slot; +} + +/* Perform offline variable substitution. + + This is a worst case quadratic time way of identifying variables + that must have equivalent points-to sets, including those caused by + static cycles, and single entry subgraphs, in the constraint graph. + + The technique is described in "Exploiting Pointer and Location + Equivalence to Optimize Pointer Analysis. In the 14th International + Static Analysis Symposium (SAS), August 2007." It is known as the + "HU" algorithm, and is equivalent to value numbering the collapsed + constraint graph including evaluating unions. + + The general method of finding equivalence classes is as follows: + Add fake nodes (REF nodes) and edges for *a = b and a = *b constraints. + Initialize all non-REF nodes to be direct nodes. + For each constraint a = a U {b}, we set pts(a) = pts(a) u {fresh + variable} + For each constraint containing the dereference, we also do the same + thing. + + We then compute SCC's in the graph and unify nodes in the same SCC, + including pts sets. + + For each non-collapsed node x: + Visit all unvisited explicit incoming edges. + Ignoring all non-pointers, set pts(x) = Union of pts(a) for y + where y->x. + Lookup the equivalence class for pts(x). + If we found one, equivalence_class(x) = found class. + Otherwise, equivalence_class(x) = new class, and new_class is + added to the lookup table. + + All direct nodes with the same equivalence class can be replaced + with a single representative node. + All unlabeled nodes (label == 0) are not pointers and all edges + involving them can be eliminated. + We perform these optimizations during rewrite_constraints + + In addition to pointer equivalence class finding, we also perform + location equivalence class finding. This is the set of variables + that always appear together in points-to sets. We use this to + compress the size of the points-to sets. */ + +/* Current maximum pointer equivalence class id. */ +static int pointer_equiv_class; + +/* Current maximum location equivalence class id. */ +static int location_equiv_class; + +/* Recursive routine to find strongly connected components in GRAPH, + and label it's nodes with DFS numbers. */ + +static void +condense_visit (constraint_graph_t graph, class scc_info *si, unsigned int n) +{ + unsigned int i; + bitmap_iterator bi; + unsigned int my_dfs; + + gcc_checking_assert (si->node_mapping[n] == n); + bitmap_set_bit (si->visited, n); + si->dfs[n] = si->current_index ++; + my_dfs = si->dfs[n]; + + /* Visit all the successors. */ + EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi) + { + unsigned int w = si->node_mapping[i]; + + if (bitmap_bit_p (si->deleted, w)) + continue; + + if (!bitmap_bit_p (si->visited, w)) + condense_visit (graph, si, w); + + unsigned int t = si->node_mapping[w]; + gcc_checking_assert (si->node_mapping[n] == n); + if (si->dfs[t] < si->dfs[n]) + si->dfs[n] = si->dfs[t]; + } + + /* Visit all the implicit predecessors. */ + EXECUTE_IF_IN_NONNULL_BITMAP (graph->implicit_preds[n], 0, i, bi) + { + unsigned int w = si->node_mapping[i]; + + if (bitmap_bit_p (si->deleted, w)) + continue; + + if (!bitmap_bit_p (si->visited, w)) + condense_visit (graph, si, w); + + unsigned int t = si->node_mapping[w]; + gcc_assert (si->node_mapping[n] == n); + if (si->dfs[t] < si->dfs[n]) + si->dfs[n] = si->dfs[t]; + } + + /* See if any components have been identified. */ + if (si->dfs[n] == my_dfs) + { + if (si->scc_stack.length () != 0 + && si->dfs[si->scc_stack.last ()] >= my_dfs) + { + /* Find the first node of the SCC and do non-bitmap work. */ + bool direct_p = true; + unsigned first = si->scc_stack.length (); + do + { + --first; + unsigned int w = si->scc_stack[first]; + si->node_mapping[w] = n; + if (!bitmap_bit_p (graph->direct_nodes, w)) + direct_p = false; + } + while (first > 0 + && si->dfs[si->scc_stack[first - 1]] >= my_dfs); + if (!direct_p) + bitmap_clear_bit (graph->direct_nodes, n); + + /* Want to reduce to node n, push that first. */ + si->scc_stack.reserve (1); + si->scc_stack.quick_push (si->scc_stack[first]); + si->scc_stack[first] = n; + + unsigned scc_size = si->scc_stack.length () - first; + unsigned split = scc_size / 2; + unsigned carry = scc_size - split * 2; + while (split > 0) + { + for (unsigned i = 0; i < split; ++i) + { + unsigned a = si->scc_stack[first + i]; + unsigned b = si->scc_stack[first + split + carry + i]; + + /* Unify our nodes. */ + if (graph->preds[b]) + { + if (!graph->preds[a]) + std::swap (graph->preds[a], graph->preds[b]); + else + bitmap_ior_into_and_free (graph->preds[a], + &graph->preds[b]); + } + if (graph->implicit_preds[b]) + { + if (!graph->implicit_preds[a]) + std::swap (graph->implicit_preds[a], + graph->implicit_preds[b]); + else + bitmap_ior_into_and_free (graph->implicit_preds[a], + &graph->implicit_preds[b]); + } + if (graph->points_to[b]) + { + if (!graph->points_to[a]) + std::swap (graph->points_to[a], graph->points_to[b]); + else + bitmap_ior_into_and_free (graph->points_to[a], + &graph->points_to[b]); + } + } + unsigned remain = split + carry; + split = remain / 2; + carry = remain - split * 2; + } + /* Actually pop the SCC. */ + si->scc_stack.truncate (first); + } + bitmap_set_bit (si->deleted, n); + } + else + si->scc_stack.safe_push (n); +} + +/* Label pointer equivalences. + + This performs a value numbering of the constraint graph to + discover which variables will always have the same points-to sets + under the current set of constraints. + + The way it value numbers is to store the set of points-to bits + generated by the constraints and graph edges. This is just used as a + hash and equality comparison. The *actual set of points-to bits* is + completely irrelevant, in that we don't care about being able to + extract them later. + + The equality values (currently bitmaps) just have to satisfy a few + constraints, the main ones being: + 1. The combining operation must be order independent. + 2. The end result of a given set of operations must be unique iff the + combination of input values is unique + 3. Hashable. */ + +static void +label_visit (constraint_graph_t graph, class scc_info *si, unsigned int n) +{ + unsigned int i, first_pred; + bitmap_iterator bi; + + bitmap_set_bit (si->visited, n); + + /* Label and union our incoming edges's points to sets. */ + first_pred = -1U; + EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi) + { + unsigned int w = si->node_mapping[i]; + if (!bitmap_bit_p (si->visited, w)) + label_visit (graph, si, w); + + /* Skip unused edges */ + if (w == n || graph->pointer_label[w] == 0) + continue; + + if (graph->points_to[w]) + { + if (!graph->points_to[n]) + { + if (first_pred == -1U) + first_pred = w; + else + { + graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack); + bitmap_ior (graph->points_to[n], + graph->points_to[first_pred], + graph->points_to[w]); + } + } + else + bitmap_ior_into (graph->points_to[n], graph->points_to[w]); + } + } + + /* Indirect nodes get fresh variables and a new pointer equiv class. */ + if (!bitmap_bit_p (graph->direct_nodes, n)) + { + if (!graph->points_to[n]) + { + graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack); + if (first_pred != -1U) + bitmap_copy (graph->points_to[n], graph->points_to[first_pred]); + } + bitmap_set_bit (graph->points_to[n], FIRST_REF_NODE + n); + graph->pointer_label[n] = pointer_equiv_class++; + equiv_class_label_t ecl; + ecl = equiv_class_lookup_or_add (pointer_equiv_class_table, + graph->points_to[n]); + ecl->equivalence_class = graph->pointer_label[n]; + return; + } + + /* If there was only a single non-empty predecessor the pointer equiv + class is the same. */ + if (!graph->points_to[n]) + { + if (first_pred != -1U) + { + graph->pointer_label[n] = graph->pointer_label[first_pred]; + graph->points_to[n] = graph->points_to[first_pred]; + } + return; + } + + if (!bitmap_empty_p (graph->points_to[n])) + { + equiv_class_label_t ecl; + ecl = equiv_class_lookup_or_add (pointer_equiv_class_table, + graph->points_to[n]); + if (ecl->equivalence_class == 0) + ecl->equivalence_class = pointer_equiv_class++; + else + { + BITMAP_FREE (graph->points_to[n]); + graph->points_to[n] = ecl->labels; + } + graph->pointer_label[n] = ecl->equivalence_class; + } +} + +/* Print the pred graph in dot format. */ + +static void +dump_pred_graph (class scc_info *si, FILE *file) +{ + unsigned int i; + + /* Only print the graph if it has already been initialized: */ + if (!graph) + return; + + /* Prints the header of the dot file: */ + fprintf (file, "strict digraph {\n"); + fprintf (file, " node [\n shape = box\n ]\n"); + fprintf (file, " edge [\n fontsize = \"12\"\n ]\n"); + fprintf (file, "\n // List of nodes and complex constraints in " + "the constraint graph:\n"); + + /* The next lines print the nodes in the graph together with the + complex constraints attached to them. */ + for (i = 1; i < graph->size; i++) + { + if (i == FIRST_REF_NODE) + continue; + if (si->node_mapping[i] != i) + continue; + if (i < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name); + if (graph->points_to[i] + && !bitmap_empty_p (graph->points_to[i])) + { + if (i < FIRST_REF_NODE) + fprintf (file, "[label=\"%s = {", get_varinfo (i)->name); + else + fprintf (file, "[label=\"*%s = {", + get_varinfo (i - FIRST_REF_NODE)->name); + unsigned j; + bitmap_iterator bi; + EXECUTE_IF_SET_IN_BITMAP (graph->points_to[i], 0, j, bi) + fprintf (file, " %d", j); + fprintf (file, " }\"]"); + } + fprintf (file, ";\n"); + } + + /* Go over the edges. */ + fprintf (file, "\n // Edges in the constraint graph:\n"); + for (i = 1; i < graph->size; i++) + { + unsigned j; + bitmap_iterator bi; + if (si->node_mapping[i] != i) + continue; + EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[i], 0, j, bi) + { + unsigned from = si->node_mapping[j]; + if (from < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (from)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (from - FIRST_REF_NODE)->name); + fprintf (file, " -> "); + if (i < FIRST_REF_NODE) + fprintf (file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name); + fprintf (file, ";\n"); + } + } + + /* Prints the tail of the dot file. */ + fprintf (file, "}\n"); +} + +/* Perform offline variable substitution, discovering equivalence + classes, and eliminating non-pointer variables. */ + +static class scc_info * +perform_var_substitution (constraint_graph_t graph) +{ + unsigned int i; + unsigned int size = graph->size; + scc_info *si = new scc_info (size); + + bitmap_obstack_initialize (&iteration_obstack); + gcc_obstack_init (&equiv_class_obstack); + pointer_equiv_class_table = new hash_table<equiv_class_hasher> (511); + location_equiv_class_table + = new hash_table<equiv_class_hasher> (511); + pointer_equiv_class = 1; + location_equiv_class = 1; + + /* Condense the nodes, which means to find SCC's, count incoming + predecessors, and unite nodes in SCC's. */ + for (i = 1; i < FIRST_REF_NODE; i++) + if (!bitmap_bit_p (si->visited, si->node_mapping[i])) + condense_visit (graph, si, si->node_mapping[i]); + + if (dump_file && (dump_flags & TDF_GRAPH)) + { + fprintf (dump_file, "\n\n// The constraint graph before var-substitution " + "in dot format:\n"); + dump_pred_graph (si, dump_file); + fprintf (dump_file, "\n\n"); + } + + bitmap_clear (si->visited); + /* Actually the label the nodes for pointer equivalences */ + for (i = 1; i < FIRST_REF_NODE; i++) + if (!bitmap_bit_p (si->visited, si->node_mapping[i])) + label_visit (graph, si, si->node_mapping[i]); + + /* Calculate location equivalence labels. */ + for (i = 1; i < FIRST_REF_NODE; i++) + { + bitmap pointed_by; + bitmap_iterator bi; + unsigned int j; + + if (!graph->pointed_by[i]) + continue; + pointed_by = BITMAP_ALLOC (&iteration_obstack); + + /* Translate the pointed-by mapping for pointer equivalence + labels. */ + EXECUTE_IF_SET_IN_BITMAP (graph->pointed_by[i], 0, j, bi) + { + bitmap_set_bit (pointed_by, + graph->pointer_label[si->node_mapping[j]]); + } + /* The original pointed_by is now dead. */ + BITMAP_FREE (graph->pointed_by[i]); + + /* Look up the location equivalence label if one exists, or make + one otherwise. */ + equiv_class_label_t ecl; + ecl = equiv_class_lookup_or_add (location_equiv_class_table, pointed_by); + if (ecl->equivalence_class == 0) + ecl->equivalence_class = location_equiv_class++; + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Found location equivalence for node %s\n", + get_varinfo (i)->name); + BITMAP_FREE (pointed_by); + } + graph->loc_label[i] = ecl->equivalence_class; + + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + for (i = 1; i < FIRST_REF_NODE; i++) + { + unsigned j = si->node_mapping[i]; + if (j != i) + { + fprintf (dump_file, "%s node id %d ", + bitmap_bit_p (graph->direct_nodes, i) + ? "Direct" : "Indirect", i); + if (i < FIRST_REF_NODE) + fprintf (dump_file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (dump_file, "\"*%s\"", + get_varinfo (i - FIRST_REF_NODE)->name); + fprintf (dump_file, " mapped to SCC leader node id %d ", j); + if (j < FIRST_REF_NODE) + fprintf (dump_file, "\"%s\"\n", get_varinfo (j)->name); + else + fprintf (dump_file, "\"*%s\"\n", + get_varinfo (j - FIRST_REF_NODE)->name); + } + else + { + fprintf (dump_file, + "Equivalence classes for %s node id %d ", + bitmap_bit_p (graph->direct_nodes, i) + ? "direct" : "indirect", i); + if (i < FIRST_REF_NODE) + fprintf (dump_file, "\"%s\"", get_varinfo (i)->name); + else + fprintf (dump_file, "\"*%s\"", + get_varinfo (i - FIRST_REF_NODE)->name); + fprintf (dump_file, + ": pointer %d, location %d\n", + graph->pointer_label[i], graph->loc_label[i]); + } + } + + /* Quickly eliminate our non-pointer variables. */ + + for (i = 1; i < FIRST_REF_NODE; i++) + { + unsigned int node = si->node_mapping[i]; + + if (graph->pointer_label[node] == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "%s is a non-pointer variable, eliminating edges.\n", + get_varinfo (node)->name); + stats.nonpointer_vars++; + clear_edges_for_node (graph, node); + } + } + + return si; +} + +/* Free information that was only necessary for variable + substitution. */ + +static void +free_var_substitution_info (class scc_info *si) +{ + delete si; + free (graph->pointer_label); + free (graph->loc_label); + free (graph->pointed_by); + free (graph->points_to); + free (graph->eq_rep); + sbitmap_free (graph->direct_nodes); + delete pointer_equiv_class_table; + pointer_equiv_class_table = NULL; + delete location_equiv_class_table; + location_equiv_class_table = NULL; + obstack_free (&equiv_class_obstack, NULL); + bitmap_obstack_release (&iteration_obstack); +} + +/* Return an existing node that is equivalent to NODE, which has + equivalence class LABEL, if one exists. Return NODE otherwise. */ + +static unsigned int +find_equivalent_node (constraint_graph_t graph, + unsigned int node, unsigned int label) +{ + /* If the address version of this variable is unused, we can + substitute it for anything else with the same label. + Otherwise, we know the pointers are equivalent, but not the + locations, and we can unite them later. */ + + if (!bitmap_bit_p (graph->address_taken, node)) + { + gcc_checking_assert (label < graph->size); + + if (graph->eq_rep[label] != -1) + { + /* Unify the two variables since we know they are equivalent. */ + if (unite (graph->eq_rep[label], node)) + unify_nodes (graph, graph->eq_rep[label], node, false); + return graph->eq_rep[label]; + } + else + { + graph->eq_rep[label] = node; + graph->pe_rep[label] = node; + } + } + else + { + gcc_checking_assert (label < graph->size); + graph->pe[node] = label; + if (graph->pe_rep[label] == -1) + graph->pe_rep[label] = node; + } + + return node; +} + +/* Unite pointer equivalent but not location equivalent nodes in + GRAPH. This may only be performed once variable substitution is + finished. */ + +static void +unite_pointer_equivalences (constraint_graph_t graph) +{ + unsigned int i; + + /* Go through the pointer equivalences and unite them to their + representative, if they aren't already. */ + for (i = 1; i < FIRST_REF_NODE; i++) + { + unsigned int label = graph->pe[i]; + if (label) + { + int label_rep = graph->pe_rep[label]; + + if (label_rep == -1) + continue; + + label_rep = find (label_rep); + if (label_rep >= 0 && unite (label_rep, find (i))) + unify_nodes (graph, label_rep, i, false); + } + } +} + +/* Move complex constraints to the GRAPH nodes they belong to. */ + +static void +move_complex_constraints (constraint_graph_t graph) +{ + int i; + constraint_t c; + + FOR_EACH_VEC_ELT (constraints, i, c) + { + if (c) + { + struct constraint_expr lhs = c->lhs; + struct constraint_expr rhs = c->rhs; + + if (lhs.type == DEREF) + { + insert_into_complex (graph, lhs.var, c); + } + else if (rhs.type == DEREF) + { + if (!(get_varinfo (lhs.var)->is_special_var)) + insert_into_complex (graph, rhs.var, c); + } + else if (rhs.type != ADDRESSOF && lhs.var > anything_id + && (lhs.offset != 0 || rhs.offset != 0)) + { + insert_into_complex (graph, rhs.var, c); + } + } + } +} + + +/* Optimize and rewrite complex constraints while performing + collapsing of equivalent nodes. SI is the SCC_INFO that is the + result of perform_variable_substitution. */ + +static void +rewrite_constraints (constraint_graph_t graph, + class scc_info *si) +{ + int i; + constraint_t c; + + if (flag_checking) + { + for (unsigned int j = 0; j < graph->size; j++) + gcc_assert (find (j) == j); + } + + FOR_EACH_VEC_ELT (constraints, i, c) + { + struct constraint_expr lhs = c->lhs; + struct constraint_expr rhs = c->rhs; + unsigned int lhsvar = find (lhs.var); + unsigned int rhsvar = find (rhs.var); + unsigned int lhsnode, rhsnode; + unsigned int lhslabel, rhslabel; + + lhsnode = si->node_mapping[lhsvar]; + rhsnode = si->node_mapping[rhsvar]; + lhslabel = graph->pointer_label[lhsnode]; + rhslabel = graph->pointer_label[rhsnode]; + + /* See if it is really a non-pointer variable, and if so, ignore + the constraint. */ + if (lhslabel == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + + fprintf (dump_file, "%s is a non-pointer variable, " + "ignoring constraint:", + get_varinfo (lhs.var)->name); + dump_constraint (dump_file, c); + fprintf (dump_file, "\n"); + } + constraints[i] = NULL; + continue; + } + + if (rhslabel == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + + fprintf (dump_file, "%s is a non-pointer variable, " + "ignoring constraint:", + get_varinfo (rhs.var)->name); + dump_constraint (dump_file, c); + fprintf (dump_file, "\n"); + } + constraints[i] = NULL; + continue; + } + + lhsvar = find_equivalent_node (graph, lhsvar, lhslabel); + rhsvar = find_equivalent_node (graph, rhsvar, rhslabel); + c->lhs.var = lhsvar; + c->rhs.var = rhsvar; + } +} + +/* Eliminate indirect cycles involving NODE. Return true if NODE was + part of an SCC, false otherwise. */ + +static bool +eliminate_indirect_cycles (unsigned int node) +{ + if (graph->indirect_cycles[node] != -1 + && !bitmap_empty_p (get_varinfo (node)->solution)) + { + unsigned int i; + auto_vec<unsigned> queue; + int queuepos; + unsigned int to = find (graph->indirect_cycles[node]); + bitmap_iterator bi; + + /* We can't touch the solution set and call unify_nodes + at the same time, because unify_nodes is going to do + bitmap unions into it. */ + + EXECUTE_IF_SET_IN_BITMAP (get_varinfo (node)->solution, 0, i, bi) + { + if (find (i) == i && i != to) + { + if (unite (to, i)) + queue.safe_push (i); + } + } + + for (queuepos = 0; + queue.iterate (queuepos, &i); + queuepos++) + { + unify_nodes (graph, to, i, true); + } + return true; + } + return false; +} + +/* Solve the constraint graph GRAPH using our worklist solver. + This is based on the PW* family of solvers from the "Efficient Field + Sensitive Pointer Analysis for C" paper. + It works by iterating over all the graph nodes, processing the complex + constraints and propagating the copy constraints, until everything stops + changed. This corresponds to steps 6-8 in the solving list given above. */ + +static void +solve_graph (constraint_graph_t graph) +{ + unsigned int size = graph->size; + unsigned int i; + bitmap pts; + + changed = BITMAP_ALLOC (NULL); + + /* Mark all initial non-collapsed nodes as changed. */ + for (i = 1; i < size; i++) + { + varinfo_t ivi = get_varinfo (i); + if (find (i) == i && !bitmap_empty_p (ivi->solution) + && ((graph->succs[i] && !bitmap_empty_p (graph->succs[i])) + || graph->complex[i].length () > 0)) + bitmap_set_bit (changed, i); + } + + /* Allocate a bitmap to be used to store the changed bits. */ + pts = BITMAP_ALLOC (&pta_obstack); + + while (!bitmap_empty_p (changed)) + { + unsigned int i; + struct topo_info *ti = init_topo_info (); + stats.iterations++; + + bitmap_obstack_initialize (&iteration_obstack); + + compute_topo_order (graph, ti); + + while (ti->topo_order.length () != 0) + { + + i = ti->topo_order.pop (); + + /* If this variable is not a representative, skip it. */ + if (find (i) != i) + continue; + + /* In certain indirect cycle cases, we may merge this + variable to another. */ + if (eliminate_indirect_cycles (i) && find (i) != i) + continue; + + /* If the node has changed, we need to process the + complex constraints and outgoing edges again. */ + if (bitmap_clear_bit (changed, i)) + { + unsigned int j; + constraint_t c; + bitmap solution; + vec<constraint_t> complex = graph->complex[i]; + varinfo_t vi = get_varinfo (i); + bool solution_empty; + + /* Compute the changed set of solution bits. If anything + is in the solution just propagate that. */ + if (bitmap_bit_p (vi->solution, anything_id)) + { + /* If anything is also in the old solution there is + nothing to do. + ??? But we shouldn't ended up with "changed" set ... */ + if (vi->oldsolution + && bitmap_bit_p (vi->oldsolution, anything_id)) + continue; + bitmap_copy (pts, get_varinfo (find (anything_id))->solution); + } + else if (vi->oldsolution) + bitmap_and_compl (pts, vi->solution, vi->oldsolution); + else + bitmap_copy (pts, vi->solution); + + if (bitmap_empty_p (pts)) + continue; + + if (vi->oldsolution) + bitmap_ior_into (vi->oldsolution, pts); + else + { + vi->oldsolution = BITMAP_ALLOC (&oldpta_obstack); + bitmap_copy (vi->oldsolution, pts); + } + + solution = vi->solution; + solution_empty = bitmap_empty_p (solution); + + /* Process the complex constraints */ + bitmap expanded_pts = NULL; + FOR_EACH_VEC_ELT (complex, j, c) + { + /* XXX: This is going to unsort the constraints in + some cases, which will occasionally add duplicate + constraints during unification. This does not + affect correctness. */ + c->lhs.var = find (c->lhs.var); + c->rhs.var = find (c->rhs.var); + + /* The only complex constraint that can change our + solution to non-empty, given an empty solution, + is a constraint where the lhs side is receiving + some set from elsewhere. */ + if (!solution_empty || c->lhs.type != DEREF) + do_complex_constraint (graph, c, pts, &expanded_pts); + } + BITMAP_FREE (expanded_pts); + + solution_empty = bitmap_empty_p (solution); + + if (!solution_empty) + { + bitmap_iterator bi; + unsigned eff_escaped_id = find (escaped_id); + + /* Propagate solution to all successors. */ + unsigned to_remove = ~0U; + EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i], + 0, j, bi) + { + if (to_remove != ~0U) + { + bitmap_clear_bit (graph->succs[i], to_remove); + to_remove = ~0U; + } + unsigned int to = find (j); + if (to != j) + { + /* Update the succ graph, avoiding duplicate + work. */ + to_remove = j; + if (! bitmap_set_bit (graph->succs[i], to)) + continue; + /* We eventually end up processing 'to' twice + as it is undefined whether bitmap iteration + iterates over bits set during iteration. + Play safe instead of doing tricks. */ + } + /* Don't try to propagate to ourselves. */ + if (to == i) + continue; + + bitmap tmp = get_varinfo (to)->solution; + bool flag = false; + + /* If we propagate from ESCAPED use ESCAPED as + placeholder. */ + if (i == eff_escaped_id) + flag = bitmap_set_bit (tmp, escaped_id); + else + flag = bitmap_ior_into (tmp, pts); + + if (flag) + bitmap_set_bit (changed, to); + } + if (to_remove != ~0U) + bitmap_clear_bit (graph->succs[i], to_remove); + } + } + } + free_topo_info (ti); + bitmap_obstack_release (&iteration_obstack); + } + + BITMAP_FREE (pts); + BITMAP_FREE (changed); + bitmap_obstack_release (&oldpta_obstack); +} + +/* Map from trees to variable infos. */ +static hash_map<tree, varinfo_t> *vi_for_tree; + + +/* Insert ID as the variable id for tree T in the vi_for_tree map. */ + +static void +insert_vi_for_tree (tree t, varinfo_t vi) +{ + gcc_assert (vi); + gcc_assert (!vi_for_tree->put (t, vi)); +} + +/* Find the variable info for tree T in VI_FOR_TREE. If T does not + exist in the map, return NULL, otherwise, return the varinfo we found. */ + +static varinfo_t +lookup_vi_for_tree (tree t) +{ + varinfo_t *slot = vi_for_tree->get (t); + if (slot == NULL) + return NULL; + + return *slot; +} + +/* Return a printable name for DECL */ + +static const char * +alias_get_name (tree decl) +{ + const char *res = "NULL"; + if (dump_file) + { + char *temp = NULL; + if (TREE_CODE (decl) == SSA_NAME) + { + res = get_name (decl); + temp = xasprintf ("%s_%u", res ? res : "", SSA_NAME_VERSION (decl)); + } + else if (HAS_DECL_ASSEMBLER_NAME_P (decl) + && DECL_ASSEMBLER_NAME_SET_P (decl)) + res = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME_RAW (decl)); + else if (DECL_P (decl)) + { + res = get_name (decl); + if (!res) + temp = xasprintf ("D.%u", DECL_UID (decl)); + } + + if (temp) + { + res = ggc_strdup (temp); + free (temp); + } + } + + return res; +} + +/* Find the variable id for tree T in the map. + If T doesn't exist in the map, create an entry for it and return it. */ + +static varinfo_t +get_vi_for_tree (tree t) +{ + varinfo_t *slot = vi_for_tree->get (t); + if (slot == NULL) + { + unsigned int id = create_variable_info_for (t, alias_get_name (t), false); + return get_varinfo (id); + } + + return *slot; +} + +/* Get a scalar constraint expression for a new temporary variable. */ + +static struct constraint_expr +new_scalar_tmp_constraint_exp (const char *name, bool add_id) +{ + struct constraint_expr tmp; + varinfo_t vi; + + vi = new_var_info (NULL_TREE, name, add_id); + vi->offset = 0; + vi->size = -1; + vi->fullsize = -1; + vi->is_full_var = 1; + vi->is_reg_var = 1; + + tmp.var = vi->id; + tmp.type = SCALAR; + tmp.offset = 0; + + return tmp; +} + +/* Get a constraint expression vector from an SSA_VAR_P node. + If address_p is true, the result will be taken its address of. */ + +static void +get_constraint_for_ssa_var (tree t, vec<ce_s> *results, bool address_p) +{ + struct constraint_expr cexpr; + varinfo_t vi; + + /* We allow FUNCTION_DECLs here even though it doesn't make much sense. */ + gcc_assert (TREE_CODE (t) == SSA_NAME || DECL_P (t)); + + if (TREE_CODE (t) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (t)) + { + /* For parameters, get at the points-to set for the actual parm + decl. */ + if (TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL + || TREE_CODE (SSA_NAME_VAR (t)) == RESULT_DECL) + { + get_constraint_for_ssa_var (SSA_NAME_VAR (t), results, address_p); + return; + } + /* For undefined SSA names return nothing. */ + else if (!ssa_defined_default_def_p (t)) + { + cexpr.var = nothing_id; + cexpr.type = SCALAR; + cexpr.offset = 0; + results->safe_push (cexpr); + return; + } + } + + /* For global variables resort to the alias target. */ + if (VAR_P (t) && (TREE_STATIC (t) || DECL_EXTERNAL (t))) + { + varpool_node *node = varpool_node::get (t); + if (node && node->alias && node->analyzed) + { + node = node->ultimate_alias_target (); + /* Canonicalize the PT uid of all aliases to the ultimate target. + ??? Hopefully the set of aliases can't change in a way that + changes the ultimate alias target. */ + gcc_assert ((! DECL_PT_UID_SET_P (node->decl) + || DECL_PT_UID (node->decl) == DECL_UID (node->decl)) + && (! DECL_PT_UID_SET_P (t) + || DECL_PT_UID (t) == DECL_UID (node->decl))); + DECL_PT_UID (t) = DECL_UID (node->decl); + t = node->decl; + } + + /* If this is decl may bind to NULL note that. */ + if (address_p + && (! node || ! node->nonzero_address ())) + { + cexpr.var = nothing_id; + cexpr.type = SCALAR; + cexpr.offset = 0; + results->safe_push (cexpr); + } + } + + vi = get_vi_for_tree (t); + cexpr.var = vi->id; + cexpr.type = SCALAR; + cexpr.offset = 0; + + /* If we are not taking the address of the constraint expr, add all + sub-fiels of the variable as well. */ + if (!address_p + && !vi->is_full_var) + { + for (; vi; vi = vi_next (vi)) + { + cexpr.var = vi->id; + results->safe_push (cexpr); + } + return; + } + + results->safe_push (cexpr); +} + +/* Process constraint T, performing various simplifications and then + adding it to our list of overall constraints. */ + +static void +process_constraint (constraint_t t) +{ + struct constraint_expr rhs = t->rhs; + struct constraint_expr lhs = t->lhs; + + gcc_assert (rhs.var < varmap.length ()); + gcc_assert (lhs.var < varmap.length ()); + + /* If we didn't get any useful constraint from the lhs we get + &ANYTHING as fallback from get_constraint_for. Deal with + it here by turning it into *ANYTHING. */ + if (lhs.type == ADDRESSOF + && lhs.var == anything_id) + lhs.type = DEREF; + + /* ADDRESSOF on the lhs is invalid. */ + gcc_assert (lhs.type != ADDRESSOF); + + /* We shouldn't add constraints from things that cannot have pointers. + It's not completely trivial to avoid in the callers, so do it here. */ + if (rhs.type != ADDRESSOF + && !get_varinfo (rhs.var)->may_have_pointers) + return; + + /* Likewise adding to the solution of a non-pointer var isn't useful. */ + if (!get_varinfo (lhs.var)->may_have_pointers) + return; + + /* This can happen in our IR with things like n->a = *p */ + if (rhs.type == DEREF && lhs.type == DEREF && rhs.var != anything_id) + { + /* Split into tmp = *rhs, *lhs = tmp */ + struct constraint_expr tmplhs; + tmplhs = new_scalar_tmp_constraint_exp ("doubledereftmp", true); + process_constraint (new_constraint (tmplhs, rhs)); + process_constraint (new_constraint (lhs, tmplhs)); + } + else if ((rhs.type != SCALAR || rhs.offset != 0) && lhs.type == DEREF) + { + /* Split into tmp = &rhs, *lhs = tmp */ + struct constraint_expr tmplhs; + tmplhs = new_scalar_tmp_constraint_exp ("derefaddrtmp", true); + process_constraint (new_constraint (tmplhs, rhs)); + process_constraint (new_constraint (lhs, tmplhs)); + } + else + { + gcc_assert (rhs.type != ADDRESSOF || rhs.offset == 0); + if (rhs.type == ADDRESSOF) + get_varinfo (get_varinfo (rhs.var)->head)->address_taken = true; + constraints.safe_push (t); + } +} + + +/* Return the position, in bits, of FIELD_DECL from the beginning of its + structure. */ + +static HOST_WIDE_INT +bitpos_of_field (const tree fdecl) +{ + if (!tree_fits_shwi_p (DECL_FIELD_OFFSET (fdecl)) + || !tree_fits_shwi_p (DECL_FIELD_BIT_OFFSET (fdecl))) + return -1; + + return (tree_to_shwi (DECL_FIELD_OFFSET (fdecl)) * BITS_PER_UNIT + + tree_to_shwi (DECL_FIELD_BIT_OFFSET (fdecl))); +} + + +/* Get constraint expressions for offsetting PTR by OFFSET. Stores the + resulting constraint expressions in *RESULTS. */ + +static void +get_constraint_for_ptr_offset (tree ptr, tree offset, + vec<ce_s> *results) +{ + struct constraint_expr c; + unsigned int j, n; + HOST_WIDE_INT rhsoffset; + + /* If we do not do field-sensitive PTA adding offsets to pointers + does not change the points-to solution. */ + if (!use_field_sensitive) + { + get_constraint_for_rhs (ptr, results); + return; + } + + /* If the offset is not a non-negative integer constant that fits + in a HOST_WIDE_INT, we have to fall back to a conservative + solution which includes all sub-fields of all pointed-to + variables of ptr. */ + if (offset == NULL_TREE + || TREE_CODE (offset) != INTEGER_CST) + rhsoffset = UNKNOWN_OFFSET; + else + { + /* Sign-extend the offset. */ + offset_int soffset = offset_int::from (wi::to_wide (offset), SIGNED); + if (!wi::fits_shwi_p (soffset)) + rhsoffset = UNKNOWN_OFFSET; + else + { + /* Make sure the bit-offset also fits. */ + HOST_WIDE_INT rhsunitoffset = soffset.to_shwi (); + rhsoffset = rhsunitoffset * (unsigned HOST_WIDE_INT) BITS_PER_UNIT; + if (rhsunitoffset != rhsoffset / BITS_PER_UNIT) + rhsoffset = UNKNOWN_OFFSET; + } + } + + get_constraint_for_rhs (ptr, results); + if (rhsoffset == 0) + return; + + /* As we are eventually appending to the solution do not use + vec::iterate here. */ + n = results->length (); + for (j = 0; j < n; j++) + { + varinfo_t curr; + c = (*results)[j]; + curr = get_varinfo (c.var); + + if (c.type == ADDRESSOF + /* If this varinfo represents a full variable just use it. */ + && curr->is_full_var) + ; + else if (c.type == ADDRESSOF + /* If we do not know the offset add all subfields. */ + && rhsoffset == UNKNOWN_OFFSET) + { + varinfo_t temp = get_varinfo (curr->head); + do + { + struct constraint_expr c2; + c2.var = temp->id; + c2.type = ADDRESSOF; + c2.offset = 0; + if (c2.var != c.var) + results->safe_push (c2); + temp = vi_next (temp); + } + while (temp); + } + else if (c.type == ADDRESSOF) + { + varinfo_t temp; + unsigned HOST_WIDE_INT offset = curr->offset + rhsoffset; + + /* If curr->offset + rhsoffset is less than zero adjust it. */ + if (rhsoffset < 0 + && curr->offset < offset) + offset = 0; + + /* We have to include all fields that overlap the current + field shifted by rhsoffset. And we include at least + the last or the first field of the variable to represent + reachability of off-bound addresses, in particular &object + 1, + conservatively correct. */ + temp = first_or_preceding_vi_for_offset (curr, offset); + c.var = temp->id; + c.offset = 0; + temp = vi_next (temp); + while (temp + && temp->offset < offset + curr->size) + { + struct constraint_expr c2; + c2.var = temp->id; + c2.type = ADDRESSOF; + c2.offset = 0; + results->safe_push (c2); + temp = vi_next (temp); + } + } + else if (c.type == SCALAR) + { + gcc_assert (c.offset == 0); + c.offset = rhsoffset; + } + else + /* We shouldn't get any DEREFs here. */ + gcc_unreachable (); + + (*results)[j] = c; + } +} + + +/* Given a COMPONENT_REF T, return the constraint_expr vector for it. + If address_p is true the result will be taken its address of. + If lhs_p is true then the constraint expression is assumed to be used + as the lhs. */ + +static void +get_constraint_for_component_ref (tree t, vec<ce_s> *results, + bool address_p, bool lhs_p) +{ + tree orig_t = t; + poly_int64 bitsize = -1; + poly_int64 bitmaxsize = -1; + poly_int64 bitpos; + bool reverse; + tree forzero; + + /* Some people like to do cute things like take the address of + &0->a.b */ + forzero = t; + while (handled_component_p (forzero) + || INDIRECT_REF_P (forzero) + || TREE_CODE (forzero) == MEM_REF) + forzero = TREE_OPERAND (forzero, 0); + + if (CONSTANT_CLASS_P (forzero) && integer_zerop (forzero)) + { + struct constraint_expr temp; + + temp.offset = 0; + temp.var = integer_id; + temp.type = SCALAR; + results->safe_push (temp); + return; + } + + t = get_ref_base_and_extent (t, &bitpos, &bitsize, &bitmaxsize, &reverse); + + /* We can end up here for component references on a + VIEW_CONVERT_EXPR <>(&foobar) or things like a + BIT_FIELD_REF <&MEM[(void *)&b + 4B], ...>. So for + symbolic constants simply give up. */ + if (TREE_CODE (t) == ADDR_EXPR) + { + constraint_expr result; + result.type = SCALAR; + result.var = anything_id; + result.offset = 0; + results->safe_push (result); + return; + } + + /* Avoid creating pointer-offset constraints, so handle MEM_REF + offsets directly. Pretend to take the address of the base, + we'll take care of adding the required subset of sub-fields below. */ + if (TREE_CODE (t) == MEM_REF + && !integer_zerop (TREE_OPERAND (t, 0))) + { + poly_offset_int off = mem_ref_offset (t); + off <<= LOG2_BITS_PER_UNIT; + off += bitpos; + poly_int64 off_hwi; + if (off.to_shwi (&off_hwi)) + bitpos = off_hwi; + else + { + bitpos = 0; + bitmaxsize = -1; + } + get_constraint_for_1 (TREE_OPERAND (t, 0), results, false, lhs_p); + do_deref (results); + } + else + get_constraint_for_1 (t, results, true, lhs_p); + + /* Strip off nothing_id. */ + if (results->length () == 2) + { + gcc_assert ((*results)[0].var == nothing_id); + results->unordered_remove (0); + } + gcc_assert (results->length () == 1); + struct constraint_expr &result = results->last (); + + if (result.type == SCALAR + && get_varinfo (result.var)->is_full_var) + /* For single-field vars do not bother about the offset. */ + result.offset = 0; + else if (result.type == SCALAR) + { + /* In languages like C, you can access one past the end of an + array. You aren't allowed to dereference it, so we can + ignore this constraint. When we handle pointer subtraction, + we may have to do something cute here. */ + + if (maybe_lt (poly_uint64 (bitpos), get_varinfo (result.var)->fullsize) + && maybe_ne (bitmaxsize, 0)) + { + /* It's also not true that the constraint will actually start at the + right offset, it may start in some padding. We only care about + setting the constraint to the first actual field it touches, so + walk to find it. */ + struct constraint_expr cexpr = result; + varinfo_t curr; + results->pop (); + cexpr.offset = 0; + for (curr = get_varinfo (cexpr.var); curr; curr = vi_next (curr)) + { + if (ranges_maybe_overlap_p (poly_int64 (curr->offset), + curr->size, bitpos, bitmaxsize)) + { + cexpr.var = curr->id; + results->safe_push (cexpr); + if (address_p) + break; + } + } + /* If we are going to take the address of this field then + to be able to compute reachability correctly add at least + the last field of the variable. */ + if (address_p && results->length () == 0) + { + curr = get_varinfo (cexpr.var); + while (curr->next != 0) + curr = vi_next (curr); + cexpr.var = curr->id; + results->safe_push (cexpr); + } + else if (results->length () == 0) + /* Assert that we found *some* field there. The user couldn't be + accessing *only* padding. */ + /* Still the user could access one past the end of an array + embedded in a struct resulting in accessing *only* padding. */ + /* Or accessing only padding via type-punning to a type + that has a filed just in padding space. */ + { + cexpr.type = SCALAR; + cexpr.var = anything_id; + cexpr.offset = 0; + results->safe_push (cexpr); + } + } + else if (known_eq (bitmaxsize, 0)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Access to zero-sized part of variable, " + "ignoring\n"); + } + else + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Access to past the end of variable, ignoring\n"); + } + else if (result.type == DEREF) + { + /* If we do not know exactly where the access goes say so. Note + that only for non-structure accesses we know that we access + at most one subfiled of any variable. */ + HOST_WIDE_INT const_bitpos; + if (!bitpos.is_constant (&const_bitpos) + || const_bitpos == -1 + || maybe_ne (bitsize, bitmaxsize) + || AGGREGATE_TYPE_P (TREE_TYPE (orig_t)) + || result.offset == UNKNOWN_OFFSET) + result.offset = UNKNOWN_OFFSET; + else + result.offset += const_bitpos; + } + else if (result.type == ADDRESSOF) + { + /* We can end up here for component references on constants like + VIEW_CONVERT_EXPR <>({ 0, 1, 2, 3 })[i]. */ + result.type = SCALAR; + result.var = anything_id; + result.offset = 0; + } + else + gcc_unreachable (); +} + + +/* Dereference the constraint expression CONS, and return the result. + DEREF (ADDRESSOF) = SCALAR + DEREF (SCALAR) = DEREF + DEREF (DEREF) = (temp = DEREF1; result = DEREF(temp)) + This is needed so that we can handle dereferencing DEREF constraints. */ + +static void +do_deref (vec<ce_s> *constraints) +{ + struct constraint_expr *c; + unsigned int i = 0; + + FOR_EACH_VEC_ELT (*constraints, i, c) + { + if (c->type == SCALAR) + c->type = DEREF; + else if (c->type == ADDRESSOF) + c->type = SCALAR; + else if (c->type == DEREF) + { + struct constraint_expr tmplhs; + tmplhs = new_scalar_tmp_constraint_exp ("dereftmp", true); + process_constraint (new_constraint (tmplhs, *c)); + c->var = tmplhs.var; + } + else + gcc_unreachable (); + } +} + +/* Given a tree T, return the constraint expression for taking the + address of it. */ + +static void +get_constraint_for_address_of (tree t, vec<ce_s> *results) +{ + struct constraint_expr *c; + unsigned int i; + + get_constraint_for_1 (t, results, true, true); + + FOR_EACH_VEC_ELT (*results, i, c) + { + if (c->type == DEREF) + c->type = SCALAR; + else + c->type = ADDRESSOF; + } +} + +/* Given a tree T, return the constraint expression for it. */ + +static void +get_constraint_for_1 (tree t, vec<ce_s> *results, bool address_p, + bool lhs_p) +{ + struct constraint_expr temp; + + /* x = integer is all glommed to a single variable, which doesn't + point to anything by itself. That is, of course, unless it is an + integer constant being treated as a pointer, in which case, we + will return that this is really the addressof anything. This + happens below, since it will fall into the default case. The only + case we know something about an integer treated like a pointer is + when it is the NULL pointer, and then we just say it points to + NULL. + + Do not do that if -fno-delete-null-pointer-checks though, because + in that case *NULL does not fail, so it _should_ alias *anything. + It is not worth adding a new option or renaming the existing one, + since this case is relatively obscure. */ + if ((TREE_CODE (t) == INTEGER_CST + && integer_zerop (t)) + /* The only valid CONSTRUCTORs in gimple with pointer typed + elements are zero-initializer. But in IPA mode we also + process global initializers, so verify at least. */ + || (TREE_CODE (t) == CONSTRUCTOR + && CONSTRUCTOR_NELTS (t) == 0)) + { + if (flag_delete_null_pointer_checks) + temp.var = nothing_id; + else + temp.var = nonlocal_id; + temp.type = ADDRESSOF; + temp.offset = 0; + results->safe_push (temp); + return; + } + + /* String constants are read-only, ideally we'd have a CONST_DECL + for those. */ + if (TREE_CODE (t) == STRING_CST) + { + temp.var = string_id; + temp.type = SCALAR; + temp.offset = 0; + results->safe_push (temp); + return; + } + + switch (TREE_CODE_CLASS (TREE_CODE (t))) + { + case tcc_expression: + { + switch (TREE_CODE (t)) + { + case ADDR_EXPR: + get_constraint_for_address_of (TREE_OPERAND (t, 0), results); + return; + default:; + } + break; + } + case tcc_reference: + { + switch (TREE_CODE (t)) + { + case MEM_REF: + { + struct constraint_expr cs; + varinfo_t vi, curr; + get_constraint_for_ptr_offset (TREE_OPERAND (t, 0), + TREE_OPERAND (t, 1), results); + do_deref (results); + + /* If we are not taking the address then make sure to process + all subvariables we might access. */ + if (address_p) + return; + + cs = results->last (); + if (cs.type == DEREF + && type_can_have_subvars (TREE_TYPE (t))) + { + /* For dereferences this means we have to defer it + to solving time. */ + results->last ().offset = UNKNOWN_OFFSET; + return; + } + if (cs.type != SCALAR) + return; + + vi = get_varinfo (cs.var); + curr = vi_next (vi); + if (!vi->is_full_var + && curr) + { + unsigned HOST_WIDE_INT size; + if (tree_fits_uhwi_p (TYPE_SIZE (TREE_TYPE (t)))) + size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (t))); + else + size = -1; + for (; curr; curr = vi_next (curr)) + { + if (curr->offset - vi->offset < size) + { + cs.var = curr->id; + results->safe_push (cs); + } + else + break; + } + } + return; + } + case ARRAY_REF: + case ARRAY_RANGE_REF: + case COMPONENT_REF: + case IMAGPART_EXPR: + case REALPART_EXPR: + case BIT_FIELD_REF: + get_constraint_for_component_ref (t, results, address_p, lhs_p); + return; + case VIEW_CONVERT_EXPR: + get_constraint_for_1 (TREE_OPERAND (t, 0), results, address_p, + lhs_p); + return; + /* We are missing handling for TARGET_MEM_REF here. */ + default:; + } + break; + } + case tcc_exceptional: + { + switch (TREE_CODE (t)) + { + case SSA_NAME: + { + get_constraint_for_ssa_var (t, results, address_p); + return; + } + case CONSTRUCTOR: + { + unsigned int i; + tree val; + auto_vec<ce_s> tmp; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (t), i, val) + { + struct constraint_expr *rhsp; + unsigned j; + get_constraint_for_1 (val, &tmp, address_p, lhs_p); + FOR_EACH_VEC_ELT (tmp, j, rhsp) + results->safe_push (*rhsp); + tmp.truncate (0); + } + /* We do not know whether the constructor was complete, + so technically we have to add &NOTHING or &ANYTHING + like we do for an empty constructor as well. */ + return; + } + default:; + } + break; + } + case tcc_declaration: + { + get_constraint_for_ssa_var (t, results, address_p); + return; + } + case tcc_constant: + { + /* We cannot refer to automatic variables through constants. */ + temp.type = ADDRESSOF; + temp.var = nonlocal_id; + temp.offset = 0; + results->safe_push (temp); + return; + } + default:; + } + + /* The default fallback is a constraint from anything. */ + temp.type = ADDRESSOF; + temp.var = anything_id; + temp.offset = 0; + results->safe_push (temp); +} + +/* Given a gimple tree T, return the constraint expression vector for it. */ + +static void +get_constraint_for (tree t, vec<ce_s> *results) +{ + gcc_assert (results->length () == 0); + + get_constraint_for_1 (t, results, false, true); +} + +/* Given a gimple tree T, return the constraint expression vector for it + to be used as the rhs of a constraint. */ + +static void +get_constraint_for_rhs (tree t, vec<ce_s> *results) +{ + gcc_assert (results->length () == 0); + + get_constraint_for_1 (t, results, false, false); +} + + +/* Efficiently generates constraints from all entries in *RHSC to all + entries in *LHSC. */ + +static void +process_all_all_constraints (const vec<ce_s> &lhsc, + const vec<ce_s> &rhsc) +{ + struct constraint_expr *lhsp, *rhsp; + unsigned i, j; + + if (lhsc.length () <= 1 || rhsc.length () <= 1) + { + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (*lhsp, *rhsp)); + } + else + { + struct constraint_expr tmp; + tmp = new_scalar_tmp_constraint_exp ("allalltmp", true); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (tmp, *rhsp)); + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + process_constraint (new_constraint (*lhsp, tmp)); + } +} + +/* Handle aggregate copies by expanding into copies of the respective + fields of the structures. */ + +static void +do_structure_copy (tree lhsop, tree rhsop) +{ + struct constraint_expr *lhsp, *rhsp; + auto_vec<ce_s> lhsc; + auto_vec<ce_s> rhsc; + unsigned j; + + get_constraint_for (lhsop, &lhsc); + get_constraint_for_rhs (rhsop, &rhsc); + lhsp = &lhsc[0]; + rhsp = &rhsc[0]; + if (lhsp->type == DEREF + || (lhsp->type == ADDRESSOF && lhsp->var == anything_id) + || rhsp->type == DEREF) + { + if (lhsp->type == DEREF) + { + gcc_assert (lhsc.length () == 1); + lhsp->offset = UNKNOWN_OFFSET; + } + if (rhsp->type == DEREF) + { + gcc_assert (rhsc.length () == 1); + rhsp->offset = UNKNOWN_OFFSET; + } + process_all_all_constraints (lhsc, rhsc); + } + else if (lhsp->type == SCALAR + && (rhsp->type == SCALAR + || rhsp->type == ADDRESSOF)) + { + HOST_WIDE_INT lhssize, lhsoffset; + HOST_WIDE_INT rhssize, rhsoffset; + bool reverse; + unsigned k = 0; + if (!get_ref_base_and_extent_hwi (lhsop, &lhsoffset, &lhssize, &reverse) + || !get_ref_base_and_extent_hwi (rhsop, &rhsoffset, &rhssize, + &reverse)) + { + process_all_all_constraints (lhsc, rhsc); + return; + } + for (j = 0; lhsc.iterate (j, &lhsp);) + { + varinfo_t lhsv, rhsv; + rhsp = &rhsc[k]; + lhsv = get_varinfo (lhsp->var); + rhsv = get_varinfo (rhsp->var); + if (lhsv->may_have_pointers + && (lhsv->is_full_var + || rhsv->is_full_var + || ranges_overlap_p (lhsv->offset + rhsoffset, lhsv->size, + rhsv->offset + lhsoffset, rhsv->size))) + process_constraint (new_constraint (*lhsp, *rhsp)); + if (!rhsv->is_full_var + && (lhsv->is_full_var + || (lhsv->offset + rhsoffset + lhsv->size + > rhsv->offset + lhsoffset + rhsv->size))) + { + ++k; + if (k >= rhsc.length ()) + break; + } + else + ++j; + } + } + else + gcc_unreachable (); +} + +/* Create constraints ID = { rhsc }. */ + +static void +make_constraints_to (unsigned id, const vec<ce_s> &rhsc) +{ + struct constraint_expr *c; + struct constraint_expr includes; + unsigned int j; + + includes.var = id; + includes.offset = 0; + includes.type = SCALAR; + + FOR_EACH_VEC_ELT (rhsc, j, c) + process_constraint (new_constraint (includes, *c)); +} + +/* Create a constraint ID = OP. */ + +static void +make_constraint_to (unsigned id, tree op) +{ + auto_vec<ce_s> rhsc; + get_constraint_for_rhs (op, &rhsc); + make_constraints_to (id, rhsc); +} + +/* Create a constraint ID = &FROM. */ + +static void +make_constraint_from (varinfo_t vi, int from) +{ + struct constraint_expr lhs, rhs; + + lhs.var = vi->id; + lhs.offset = 0; + lhs.type = SCALAR; + + rhs.var = from; + rhs.offset = 0; + rhs.type = ADDRESSOF; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Create a constraint ID = FROM. */ + +static void +make_copy_constraint (varinfo_t vi, int from) +{ + struct constraint_expr lhs, rhs; + + lhs.var = vi->id; + lhs.offset = 0; + lhs.type = SCALAR; + + rhs.var = from; + rhs.offset = 0; + rhs.type = SCALAR; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Make constraints necessary to make OP escape. */ + +static void +make_escape_constraint (tree op) +{ + make_constraint_to (escaped_id, op); +} + +/* Make constraint necessary to make all indirect references + from VI escape. */ + +static void +make_indirect_escape_constraint (varinfo_t vi) +{ + struct constraint_expr lhs, rhs; + /* escaped = *(VAR + UNKNOWN); */ + lhs.type = SCALAR; + lhs.var = escaped_id; + lhs.offset = 0; + rhs.type = DEREF; + rhs.var = vi->id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Add constraints to that the solution of VI is transitively closed. */ + +static void +make_transitive_closure_constraints (varinfo_t vi) +{ + struct constraint_expr lhs, rhs; + + /* VAR = *(VAR + UNKNOWN); */ + lhs.type = SCALAR; + lhs.var = vi->id; + lhs.offset = 0; + rhs.type = DEREF; + rhs.var = vi->id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Add constraints to that the solution of VI has all subvariables added. */ + +static void +make_any_offset_constraints (varinfo_t vi) +{ + struct constraint_expr lhs, rhs; + + /* VAR = VAR + UNKNOWN; */ + lhs.type = SCALAR; + lhs.var = vi->id; + lhs.offset = 0; + rhs.type = SCALAR; + rhs.var = vi->id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Temporary storage for fake var decls. */ +struct obstack fake_var_decl_obstack; + +/* Build a fake VAR_DECL acting as referrer to a DECL_UID. */ + +static tree +build_fake_var_decl (tree type) +{ + tree decl = (tree) XOBNEW (&fake_var_decl_obstack, struct tree_var_decl); + memset (decl, 0, sizeof (struct tree_var_decl)); + TREE_SET_CODE (decl, VAR_DECL); + TREE_TYPE (decl) = type; + DECL_UID (decl) = allocate_decl_uid (); + SET_DECL_PT_UID (decl, -1); + layout_decl (decl, 0); + return decl; +} + +/* Create a new artificial heap variable with NAME. + Return the created variable. */ + +static varinfo_t +make_heapvar (const char *name, bool add_id) +{ + varinfo_t vi; + tree heapvar; + + heapvar = build_fake_var_decl (ptr_type_node); + DECL_EXTERNAL (heapvar) = 1; + + vi = new_var_info (heapvar, name, add_id); + vi->is_heap_var = true; + vi->is_unknown_size_var = true; + vi->offset = 0; + vi->fullsize = ~0; + vi->size = ~0; + vi->is_full_var = true; + insert_vi_for_tree (heapvar, vi); + + return vi; +} + +/* Create a new artificial heap variable with NAME and make a + constraint from it to LHS. Set flags according to a tag used + for tracking restrict pointers. */ + +static varinfo_t +make_constraint_from_restrict (varinfo_t lhs, const char *name, bool add_id) +{ + varinfo_t vi = make_heapvar (name, add_id); + vi->is_restrict_var = 1; + vi->is_global_var = 1; + vi->may_have_pointers = 1; + make_constraint_from (lhs, vi->id); + return vi; +} + +/* Create a new artificial heap variable with NAME and make a + constraint from it to LHS. Set flags according to a tag used + for tracking restrict pointers and make the artificial heap + point to global memory. */ + +static varinfo_t +make_constraint_from_global_restrict (varinfo_t lhs, const char *name, + bool add_id) +{ + varinfo_t vi = make_constraint_from_restrict (lhs, name, add_id); + make_copy_constraint (vi, nonlocal_id); + return vi; +} + +/* In IPA mode there are varinfos for different aspects of reach + function designator. One for the points-to set of the return + value, one for the variables that are clobbered by the function, + one for its uses and one for each parameter (including a single + glob for remaining variadic arguments). */ + +enum { fi_clobbers = 1, fi_uses = 2, + fi_static_chain = 3, fi_result = 4, fi_parm_base = 5 }; + +/* Get a constraint for the requested part of a function designator FI + when operating in IPA mode. */ + +static struct constraint_expr +get_function_part_constraint (varinfo_t fi, unsigned part) +{ + struct constraint_expr c; + + gcc_assert (in_ipa_mode); + + if (fi->id == anything_id) + { + /* ??? We probably should have a ANYFN special variable. */ + c.var = anything_id; + c.offset = 0; + c.type = SCALAR; + } + else if (fi->decl && TREE_CODE (fi->decl) == FUNCTION_DECL) + { + varinfo_t ai = first_vi_for_offset (fi, part); + if (ai) + c.var = ai->id; + else + c.var = anything_id; + c.offset = 0; + c.type = SCALAR; + } + else + { + c.var = fi->id; + c.offset = part; + c.type = DEREF; + } + + return c; +} + +/* Produce constraints for argument ARG of call STMT with eaf flags + FLAGS. RESULTS is array holding constraints for return value. + CALLESCAPE_ID is variable where call loocal escapes are added. + WRITES_GLOVEL_MEMORY is true if callee may write global memory. */ + +static void +handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags, + int callescape_id, bool writes_global_memory) +{ + int relevant_indirect_flags = EAF_NO_INDIRECT_CLOBBER | EAF_NO_INDIRECT_READ + | EAF_NO_INDIRECT_ESCAPE; + int relevant_flags = relevant_indirect_flags + | EAF_NO_DIRECT_CLOBBER + | EAF_NO_DIRECT_READ + | EAF_NO_DIRECT_ESCAPE; + if (gimple_call_lhs (stmt)) + { + relevant_flags |= EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY; + relevant_indirect_flags |= EAF_NOT_RETURNED_INDIRECTLY; + + /* If value is never read from it can not be returned indirectly + (except through the escape solution). + For all flags we get these implications right except for + not_returned because we miss return functions in ipa-prop. */ + + if (flags & EAF_NO_DIRECT_READ) + flags |= EAF_NOT_RETURNED_INDIRECTLY; + } + + /* If the argument is not used we can ignore it. + Similarly argument is invisile for us if it not clobbered, does not + escape, is not read and can not be returned. */ + if ((flags & EAF_UNUSED) || ((flags & relevant_flags) == relevant_flags)) + return; + + /* Produce varinfo for direct accesses to ARG. */ + varinfo_t tem = new_var_info (NULL_TREE, "callarg", true); + tem->is_reg_var = true; + make_constraint_to (tem->id, arg); + make_any_offset_constraints (tem); + + bool callarg_transitive = false; + + /* As an compile time optimization if we make no difference between + direct and indirect accesses make arg transitively closed. + This avoids the need to build indir arg and do everything twice. */ + if (((flags & EAF_NO_INDIRECT_CLOBBER) != 0) + == ((flags & EAF_NO_DIRECT_CLOBBER) != 0) + && (((flags & EAF_NO_INDIRECT_READ) != 0) + == ((flags & EAF_NO_DIRECT_READ) != 0)) + && (((flags & EAF_NO_INDIRECT_ESCAPE) != 0) + == ((flags & EAF_NO_DIRECT_ESCAPE) != 0)) + && (((flags & EAF_NOT_RETURNED_INDIRECTLY) != 0) + == ((flags & EAF_NOT_RETURNED_DIRECTLY) != 0))) + { + make_transitive_closure_constraints (tem); + callarg_transitive = true; + gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ)); + } + + /* If necessary, produce varinfo for indirect accesses to ARG. */ + varinfo_t indir_tem = NULL; + if (!callarg_transitive + && (flags & relevant_indirect_flags) != relevant_indirect_flags) + { + struct constraint_expr lhs, rhs; + indir_tem = new_var_info (NULL_TREE, "indircallarg", true); + indir_tem->is_reg_var = true; + + /* indir_term = *tem. */ + lhs.type = SCALAR; + lhs.var = indir_tem->id; + lhs.offset = 0; + + rhs.type = DEREF; + rhs.var = tem->id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); + + make_any_offset_constraints (indir_tem); + + /* If we do not read indirectly there is no need for transitive closure. + We know there is only one level of indirection. */ + if (!(flags & EAF_NO_INDIRECT_READ)) + make_transitive_closure_constraints (indir_tem); + gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ)); + } + + if (gimple_call_lhs (stmt)) + { + if (!(flags & EAF_NOT_RETURNED_DIRECTLY)) + { + struct constraint_expr cexpr; + cexpr.var = tem->id; + cexpr.type = SCALAR; + cexpr.offset = 0; + results->safe_push (cexpr); + } + if (!callarg_transitive & !(flags & EAF_NOT_RETURNED_INDIRECTLY)) + { + struct constraint_expr cexpr; + cexpr.var = indir_tem->id; + cexpr.type = SCALAR; + cexpr.offset = 0; + results->safe_push (cexpr); + } + } + + if (!(flags & EAF_NO_DIRECT_READ)) + { + varinfo_t uses = get_call_use_vi (stmt); + make_copy_constraint (uses, tem->id); + if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_READ)) + make_copy_constraint (uses, indir_tem->id); + } + else + /* To read indirectly we need to read directly. */ + gcc_checking_assert (flags & EAF_NO_INDIRECT_READ); + + if (!(flags & EAF_NO_DIRECT_CLOBBER)) + { + struct constraint_expr lhs, rhs; + + /* *arg = callescape. */ + lhs.type = DEREF; + lhs.var = tem->id; + lhs.offset = 0; + + rhs.type = SCALAR; + rhs.var = callescape_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + + /* callclobbered = arg. */ + make_copy_constraint (get_call_clobber_vi (stmt), tem->id); + } + if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_CLOBBER)) + { + struct constraint_expr lhs, rhs; + + /* *indir_arg = callescape. */ + lhs.type = DEREF; + lhs.var = indir_tem->id; + lhs.offset = 0; + + rhs.type = SCALAR; + rhs.var = callescape_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + + /* callclobbered = indir_arg. */ + make_copy_constraint (get_call_clobber_vi (stmt), indir_tem->id); + } + + if (!(flags & (EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE))) + { + struct constraint_expr lhs, rhs; + + /* callescape = arg; */ + lhs.var = callescape_id; + lhs.offset = 0; + lhs.type = SCALAR; + + rhs.var = tem->id; + rhs.offset = 0; + rhs.type = SCALAR; + process_constraint (new_constraint (lhs, rhs)); + + if (writes_global_memory) + make_escape_constraint (arg); + } + else if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_ESCAPE)) + { + struct constraint_expr lhs, rhs; + + /* callescape = *(indir_arg + UNKNOWN); */ + lhs.var = callescape_id; + lhs.offset = 0; + lhs.type = SCALAR; + + rhs.var = indir_tem->id; + rhs.offset = 0; + rhs.type = SCALAR; + process_constraint (new_constraint (lhs, rhs)); + + if (writes_global_memory) + make_indirect_escape_constraint (tem); + } +} + +/* Determine global memory access of call STMT and update + WRITES_GLOBAL_MEMORY, READS_GLOBAL_MEMORY and USES_GLOBAL_MEMORY. */ + +static void +determine_global_memory_access (gcall *stmt, + bool *writes_global_memory, + bool *reads_global_memory, + bool *uses_global_memory) +{ + tree callee; + cgraph_node *node; + modref_summary *summary; + + /* We need to detrmine reads to set uses. */ + gcc_assert (!uses_global_memory || reads_global_memory); + + if ((callee = gimple_call_fndecl (stmt)) != NULL_TREE + && (node = cgraph_node::get (callee)) != NULL + && (summary = get_modref_function_summary (node))) + { + if (writes_global_memory && *writes_global_memory) + *writes_global_memory = summary->global_memory_written; + if (reads_global_memory && *reads_global_memory) + *reads_global_memory = summary->global_memory_read; + if (reads_global_memory && uses_global_memory + && !summary->calls_interposable + && !*reads_global_memory && node->binds_to_current_def_p ()) + *uses_global_memory = false; + } + if ((writes_global_memory && *writes_global_memory) + || (uses_global_memory && *uses_global_memory) + || (reads_global_memory && *reads_global_memory)) + { + attr_fnspec fnspec = gimple_call_fnspec (stmt); + if (fnspec.known_p ()) + { + if (writes_global_memory + && !fnspec.global_memory_written_p ()) + *writes_global_memory = false; + if (reads_global_memory && !fnspec.global_memory_read_p ()) + { + *reads_global_memory = false; + if (uses_global_memory) + *uses_global_memory = false; + } + } + } +} + +/* For non-IPA mode, generate constraints necessary for a call on the + RHS and collect return value constraint to RESULTS to be used later in + handle_lhs_call. + + IMPLICIT_EAF_FLAGS are added to each function argument. If + WRITES_GLOBAL_MEMORY is true function is assumed to possibly write to global + memory. Similar for READS_GLOBAL_MEMORY. */ + +static void +handle_rhs_call (gcall *stmt, vec<ce_s> *results, + int implicit_eaf_flags, + bool writes_global_memory, + bool reads_global_memory) +{ + determine_global_memory_access (stmt, &writes_global_memory, + &reads_global_memory, + NULL); + + varinfo_t callescape = new_var_info (NULL_TREE, "callescape", true); + + /* If function can use global memory, add it to callescape + and to possible return values. If not we can still use/return addresses + of global symbols. */ + struct constraint_expr lhs, rhs; + + lhs.type = SCALAR; + lhs.var = callescape->id; + lhs.offset = 0; + + rhs.type = reads_global_memory ? SCALAR : ADDRESSOF; + rhs.var = nonlocal_id; + rhs.offset = 0; + + process_constraint (new_constraint (lhs, rhs)); + results->safe_push (rhs); + + varinfo_t uses = get_call_use_vi (stmt); + make_copy_constraint (uses, callescape->id); + + for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i) + { + tree arg = gimple_call_arg (stmt, i); + int flags = gimple_call_arg_flags (stmt, i); + handle_call_arg (stmt, arg, results, + flags | implicit_eaf_flags, + callescape->id, writes_global_memory); + } + + /* The static chain escapes as well. */ + if (gimple_call_chain (stmt)) + handle_call_arg (stmt, gimple_call_chain (stmt), results, + implicit_eaf_flags + | gimple_call_static_chain_flags (stmt), + callescape->id, writes_global_memory); + + /* And if we applied NRV the address of the return slot escapes as well. */ + if (gimple_call_return_slot_opt_p (stmt) + && gimple_call_lhs (stmt) != NULL_TREE + && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt)))) + { + int flags = gimple_call_retslot_flags (stmt); + const int relevant_flags = EAF_NO_DIRECT_ESCAPE + | EAF_NOT_RETURNED_DIRECTLY; + + if (!(flags & EAF_UNUSED) && (flags & relevant_flags) != relevant_flags) + { + auto_vec<ce_s> tmpc; + + get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc); + + if (!(flags & EAF_NO_DIRECT_ESCAPE)) + { + make_constraints_to (callescape->id, tmpc); + if (writes_global_memory) + make_constraints_to (escaped_id, tmpc); + } + if (!(flags & EAF_NOT_RETURNED_DIRECTLY)) + { + struct constraint_expr *c; + unsigned i; + FOR_EACH_VEC_ELT (tmpc, i, c) + results->safe_push (*c); + } + } + } +} + +/* For non-IPA mode, generate constraints necessary for a call + that returns a pointer and assigns it to LHS. This simply makes + the LHS point to global and escaped variables. */ + +static void +handle_lhs_call (gcall *stmt, tree lhs, int flags, vec<ce_s> &rhsc, + tree fndecl) +{ + auto_vec<ce_s> lhsc; + + get_constraint_for (lhs, &lhsc); + /* If the store is to a global decl make sure to + add proper escape constraints. */ + lhs = get_base_address (lhs); + if (lhs + && DECL_P (lhs) + && is_global_var (lhs)) + { + struct constraint_expr tmpc; + tmpc.var = escaped_id; + tmpc.offset = 0; + tmpc.type = SCALAR; + lhsc.safe_push (tmpc); + } + + /* If the call returns an argument unmodified override the rhs + constraints. */ + if (flags & ERF_RETURNS_ARG + && (flags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (stmt)) + { + tree arg; + rhsc.create (0); + arg = gimple_call_arg (stmt, flags & ERF_RETURN_ARG_MASK); + get_constraint_for (arg, &rhsc); + process_all_all_constraints (lhsc, rhsc); + rhsc.release (); + } + else if (flags & ERF_NOALIAS) + { + varinfo_t vi; + struct constraint_expr tmpc; + rhsc.create (0); + vi = make_heapvar ("HEAP", true); + /* We are marking allocated storage local, we deal with it becoming + global by escaping and setting of vars_contains_escaped_heap. */ + DECL_EXTERNAL (vi->decl) = 0; + vi->is_global_var = 0; + /* If this is not a real malloc call assume the memory was + initialized and thus may point to global memory. All + builtin functions with the malloc attribute behave in a sane way. */ + if (!fndecl + || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) + make_constraint_from (vi, nonlocal_id); + tmpc.var = vi->id; + tmpc.offset = 0; + tmpc.type = ADDRESSOF; + rhsc.safe_push (tmpc); + process_all_all_constraints (lhsc, rhsc); + rhsc.release (); + } + else + process_all_all_constraints (lhsc, rhsc); +} + + +/* Return the varinfo for the callee of CALL. */ + +static varinfo_t +get_fi_for_callee (gcall *call) +{ + tree decl, fn = gimple_call_fn (call); + + if (fn && TREE_CODE (fn) == OBJ_TYPE_REF) + fn = OBJ_TYPE_REF_EXPR (fn); + + /* If we can directly resolve the function being called, do so. + Otherwise, it must be some sort of indirect expression that + we should still be able to handle. */ + decl = gimple_call_addr_fndecl (fn); + if (decl) + return get_vi_for_tree (decl); + + /* If the function is anything other than a SSA name pointer we have no + clue and should be getting ANYFN (well, ANYTHING for now). */ + if (!fn || TREE_CODE (fn) != SSA_NAME) + return get_varinfo (anything_id); + + if (SSA_NAME_IS_DEFAULT_DEF (fn) + && (TREE_CODE (SSA_NAME_VAR (fn)) == PARM_DECL + || TREE_CODE (SSA_NAME_VAR (fn)) == RESULT_DECL)) + fn = SSA_NAME_VAR (fn); + + return get_vi_for_tree (fn); +} + +/* Create constraints for assigning call argument ARG to the incoming parameter + INDEX of function FI. */ + +static void +find_func_aliases_for_call_arg (varinfo_t fi, unsigned index, tree arg) +{ + struct constraint_expr lhs; + lhs = get_function_part_constraint (fi, fi_parm_base + index); + + auto_vec<ce_s, 2> rhsc; + get_constraint_for_rhs (arg, &rhsc); + + unsigned j; + struct constraint_expr *rhsp; + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); +} + +/* Return true if FNDECL may be part of another lto partition. */ + +static bool +fndecl_maybe_in_other_partition (tree fndecl) +{ + cgraph_node *fn_node = cgraph_node::get (fndecl); + if (fn_node == NULL) + return true; + + return fn_node->in_other_partition; +} + +/* Create constraints for the builtin call T. Return true if the call + was handled, otherwise false. */ + +static bool +find_func_aliases_for_builtin_call (struct function *fn, gcall *t) +{ + tree fndecl = gimple_call_fndecl (t); + auto_vec<ce_s, 2> lhsc; + auto_vec<ce_s, 4> rhsc; + varinfo_t fi; + + if (gimple_call_builtin_p (t, BUILT_IN_NORMAL)) + /* ??? All builtins that are handled here need to be handled + in the alias-oracle query functions explicitly! */ + switch (DECL_FUNCTION_CODE (fndecl)) + { + /* All the following functions return a pointer to the same object + as their first argument points to. The functions do not add + to the ESCAPED solution. The functions make the first argument + pointed to memory point to what the second argument pointed to + memory points to. */ + case BUILT_IN_STRCPY: + case BUILT_IN_STRNCPY: + case BUILT_IN_BCOPY: + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMMOVE: + case BUILT_IN_MEMPCPY: + case BUILT_IN_STPCPY: + case BUILT_IN_STPNCPY: + case BUILT_IN_STRCAT: + case BUILT_IN_STRNCAT: + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STPNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_STRNCAT_CHK: + case BUILT_IN_TM_MEMCPY: + case BUILT_IN_TM_MEMMOVE: + { + tree res = gimple_call_lhs (t); + tree dest = gimple_call_arg (t, (DECL_FUNCTION_CODE (fndecl) + == BUILT_IN_BCOPY ? 1 : 0)); + tree src = gimple_call_arg (t, (DECL_FUNCTION_CODE (fndecl) + == BUILT_IN_BCOPY ? 0 : 1)); + if (res != NULL_TREE) + { + get_constraint_for (res, &lhsc); + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPCPY + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPNCPY + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY_CHK + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPCPY_CHK + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPNCPY_CHK) + get_constraint_for_ptr_offset (dest, NULL_TREE, &rhsc); + else + get_constraint_for (dest, &rhsc); + process_all_all_constraints (lhsc, rhsc); + lhsc.truncate (0); + rhsc.truncate (0); + } + get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc); + get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc); + do_deref (&lhsc); + do_deref (&rhsc); + process_all_all_constraints (lhsc, rhsc); + return true; + } + case BUILT_IN_MEMSET: + case BUILT_IN_MEMSET_CHK: + case BUILT_IN_TM_MEMSET: + { + tree res = gimple_call_lhs (t); + tree dest = gimple_call_arg (t, 0); + unsigned i; + ce_s *lhsp; + struct constraint_expr ac; + if (res != NULL_TREE) + { + get_constraint_for (res, &lhsc); + get_constraint_for (dest, &rhsc); + process_all_all_constraints (lhsc, rhsc); + lhsc.truncate (0); + } + get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc); + do_deref (&lhsc); + if (flag_delete_null_pointer_checks + && integer_zerop (gimple_call_arg (t, 1))) + { + ac.type = ADDRESSOF; + ac.var = nothing_id; + } + else + { + ac.type = SCALAR; + ac.var = integer_id; + } + ac.offset = 0; + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + process_constraint (new_constraint (*lhsp, ac)); + return true; + } + case BUILT_IN_STACK_SAVE: + case BUILT_IN_STACK_RESTORE: + /* Nothing interesting happens. */ + return true; + case BUILT_IN_ALLOCA: + case BUILT_IN_ALLOCA_WITH_ALIGN: + case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX: + { + tree ptr = gimple_call_lhs (t); + if (ptr == NULL_TREE) + return true; + get_constraint_for (ptr, &lhsc); + varinfo_t vi = make_heapvar ("HEAP", true); + /* Alloca storage is never global. To exempt it from escaped + handling make it a non-heap var. */ + DECL_EXTERNAL (vi->decl) = 0; + vi->is_global_var = 0; + vi->is_heap_var = 0; + struct constraint_expr tmpc; + tmpc.var = vi->id; + tmpc.offset = 0; + tmpc.type = ADDRESSOF; + rhsc.safe_push (tmpc); + process_all_all_constraints (lhsc, rhsc); + return true; + } + case BUILT_IN_POSIX_MEMALIGN: + { + tree ptrptr = gimple_call_arg (t, 0); + get_constraint_for (ptrptr, &lhsc); + do_deref (&lhsc); + varinfo_t vi = make_heapvar ("HEAP", true); + /* We are marking allocated storage local, we deal with it becoming + global by escaping and setting of vars_contains_escaped_heap. */ + DECL_EXTERNAL (vi->decl) = 0; + vi->is_global_var = 0; + struct constraint_expr tmpc; + tmpc.var = vi->id; + tmpc.offset = 0; + tmpc.type = ADDRESSOF; + rhsc.safe_push (tmpc); + process_all_all_constraints (lhsc, rhsc); + return true; + } + case BUILT_IN_ASSUME_ALIGNED: + { + tree res = gimple_call_lhs (t); + tree dest = gimple_call_arg (t, 0); + if (res != NULL_TREE) + { + get_constraint_for (res, &lhsc); + get_constraint_for (dest, &rhsc); + process_all_all_constraints (lhsc, rhsc); + } + return true; + } + /* All the following functions do not return pointers, do not + modify the points-to sets of memory reachable from their + arguments and do not add to the ESCAPED solution. */ + case BUILT_IN_SINCOS: + case BUILT_IN_SINCOSF: + case BUILT_IN_SINCOSL: + case BUILT_IN_FREXP: + case BUILT_IN_FREXPF: + case BUILT_IN_FREXPL: + case BUILT_IN_GAMMA_R: + case BUILT_IN_GAMMAF_R: + case BUILT_IN_GAMMAL_R: + case BUILT_IN_LGAMMA_R: + case BUILT_IN_LGAMMAF_R: + case BUILT_IN_LGAMMAL_R: + case BUILT_IN_MODF: + case BUILT_IN_MODFF: + case BUILT_IN_MODFL: + case BUILT_IN_REMQUO: + case BUILT_IN_REMQUOF: + case BUILT_IN_REMQUOL: + case BUILT_IN_FREE: + return true; + case BUILT_IN_STRDUP: + case BUILT_IN_STRNDUP: + case BUILT_IN_REALLOC: + if (gimple_call_lhs (t)) + { + auto_vec<ce_s> rhsc; + handle_lhs_call (t, gimple_call_lhs (t), + gimple_call_return_flags (t) | ERF_NOALIAS, + rhsc, fndecl); + get_constraint_for_ptr_offset (gimple_call_lhs (t), + NULL_TREE, &lhsc); + get_constraint_for_ptr_offset (gimple_call_arg (t, 0), + NULL_TREE, &rhsc); + do_deref (&lhsc); + do_deref (&rhsc); + process_all_all_constraints (lhsc, rhsc); + lhsc.truncate (0); + rhsc.truncate (0); + /* For realloc the resulting pointer can be equal to the + argument as well. But only doing this wouldn't be + correct because with ptr == 0 realloc behaves like malloc. */ + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_REALLOC) + { + get_constraint_for (gimple_call_lhs (t), &lhsc); + get_constraint_for (gimple_call_arg (t, 0), &rhsc); + process_all_all_constraints (lhsc, rhsc); + } + return true; + } + break; + /* String / character search functions return a pointer into the + source string or NULL. */ + case BUILT_IN_INDEX: + case BUILT_IN_STRCHR: + case BUILT_IN_STRRCHR: + case BUILT_IN_MEMCHR: + case BUILT_IN_STRSTR: + case BUILT_IN_STRPBRK: + if (gimple_call_lhs (t)) + { + tree src = gimple_call_arg (t, 0); + get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc); + constraint_expr nul; + nul.var = nothing_id; + nul.offset = 0; + nul.type = ADDRESSOF; + rhsc.safe_push (nul); + get_constraint_for (gimple_call_lhs (t), &lhsc); + process_all_all_constraints (lhsc, rhsc); + } + return true; + /* Pure functions that return something not based on any object and + that use the memory pointed to by their arguments (but not + transitively). */ + case BUILT_IN_STRCMP: + case BUILT_IN_STRCMP_EQ: + case BUILT_IN_STRNCMP: + case BUILT_IN_STRNCMP_EQ: + case BUILT_IN_STRCASECMP: + case BUILT_IN_STRNCASECMP: + case BUILT_IN_MEMCMP: + case BUILT_IN_BCMP: + case BUILT_IN_STRSPN: + case BUILT_IN_STRCSPN: + { + varinfo_t uses = get_call_use_vi (t); + make_any_offset_constraints (uses); + make_constraint_to (uses->id, gimple_call_arg (t, 0)); + make_constraint_to (uses->id, gimple_call_arg (t, 1)); + /* No constraints are necessary for the return value. */ + return true; + } + case BUILT_IN_STRLEN: + { + varinfo_t uses = get_call_use_vi (t); + make_any_offset_constraints (uses); + make_constraint_to (uses->id, gimple_call_arg (t, 0)); + /* No constraints are necessary for the return value. */ + return true; + } + case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_CONSTANT_P: + { + /* No constraints are necessary for the return value or the + arguments. */ + return true; + } + /* Trampolines are special - they set up passing the static + frame. */ + case BUILT_IN_INIT_TRAMPOLINE: + { + tree tramp = gimple_call_arg (t, 0); + tree nfunc = gimple_call_arg (t, 1); + tree frame = gimple_call_arg (t, 2); + unsigned i; + struct constraint_expr lhs, *rhsp; + if (in_ipa_mode) + { + varinfo_t nfi = NULL; + gcc_assert (TREE_CODE (nfunc) == ADDR_EXPR); + nfi = lookup_vi_for_tree (TREE_OPERAND (nfunc, 0)); + if (nfi) + { + lhs = get_function_part_constraint (nfi, fi_static_chain); + get_constraint_for (frame, &rhsc); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + + /* Make the frame point to the function for + the trampoline adjustment call. */ + get_constraint_for (tramp, &lhsc); + do_deref (&lhsc); + get_constraint_for (nfunc, &rhsc); + process_all_all_constraints (lhsc, rhsc); + + return true; + } + } + /* Else fallthru to generic handling which will let + the frame escape. */ + break; + } + case BUILT_IN_ADJUST_TRAMPOLINE: + { + tree tramp = gimple_call_arg (t, 0); + tree res = gimple_call_lhs (t); + if (in_ipa_mode && res) + { + get_constraint_for (res, &lhsc); + get_constraint_for (tramp, &rhsc); + do_deref (&rhsc); + process_all_all_constraints (lhsc, rhsc); + } + return true; + } + CASE_BUILT_IN_TM_STORE (1): + CASE_BUILT_IN_TM_STORE (2): + CASE_BUILT_IN_TM_STORE (4): + CASE_BUILT_IN_TM_STORE (8): + CASE_BUILT_IN_TM_STORE (FLOAT): + CASE_BUILT_IN_TM_STORE (DOUBLE): + CASE_BUILT_IN_TM_STORE (LDOUBLE): + CASE_BUILT_IN_TM_STORE (M64): + CASE_BUILT_IN_TM_STORE (M128): + CASE_BUILT_IN_TM_STORE (M256): + { + tree addr = gimple_call_arg (t, 0); + tree src = gimple_call_arg (t, 1); + + get_constraint_for (addr, &lhsc); + do_deref (&lhsc); + get_constraint_for (src, &rhsc); + process_all_all_constraints (lhsc, rhsc); + return true; + } + CASE_BUILT_IN_TM_LOAD (1): + CASE_BUILT_IN_TM_LOAD (2): + CASE_BUILT_IN_TM_LOAD (4): + CASE_BUILT_IN_TM_LOAD (8): + CASE_BUILT_IN_TM_LOAD (FLOAT): + CASE_BUILT_IN_TM_LOAD (DOUBLE): + CASE_BUILT_IN_TM_LOAD (LDOUBLE): + CASE_BUILT_IN_TM_LOAD (M64): + CASE_BUILT_IN_TM_LOAD (M128): + CASE_BUILT_IN_TM_LOAD (M256): + { + tree dest = gimple_call_lhs (t); + tree addr = gimple_call_arg (t, 0); + + get_constraint_for (dest, &lhsc); + get_constraint_for (addr, &rhsc); + do_deref (&rhsc); + process_all_all_constraints (lhsc, rhsc); + return true; + } + /* Variadic argument handling needs to be handled in IPA + mode as well. */ + case BUILT_IN_VA_START: + { + tree valist = gimple_call_arg (t, 0); + struct constraint_expr rhs, *lhsp; + unsigned i; + get_constraint_for_ptr_offset (valist, NULL_TREE, &lhsc); + do_deref (&lhsc); + /* The va_list gets access to pointers in variadic + arguments. Which we know in the case of IPA analysis + and otherwise are just all nonlocal variables. */ + if (in_ipa_mode) + { + fi = lookup_vi_for_tree (fn->decl); + rhs = get_function_part_constraint (fi, ~0); + rhs.type = ADDRESSOF; + } + else + { + rhs.var = nonlocal_id; + rhs.type = ADDRESSOF; + rhs.offset = 0; + } + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + process_constraint (new_constraint (*lhsp, rhs)); + /* va_list is clobbered. */ + make_constraint_to (get_call_clobber_vi (t)->id, valist); + return true; + } + /* va_end doesn't have any effect that matters. */ + case BUILT_IN_VA_END: + return true; + /* Alternate return. Simply give up for now. */ + case BUILT_IN_RETURN: + { + fi = NULL; + if (!in_ipa_mode + || !(fi = get_vi_for_tree (fn->decl))) + make_constraint_from (get_varinfo (escaped_id), anything_id); + else if (in_ipa_mode + && fi != NULL) + { + struct constraint_expr lhs, rhs; + lhs = get_function_part_constraint (fi, fi_result); + rhs.var = anything_id; + rhs.offset = 0; + rhs.type = SCALAR; + process_constraint (new_constraint (lhs, rhs)); + } + return true; + } + case BUILT_IN_GOMP_PARALLEL: + case BUILT_IN_GOACC_PARALLEL: + { + if (in_ipa_mode) + { + unsigned int fnpos, argpos; + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_GOMP_PARALLEL: + /* __builtin_GOMP_parallel (fn, data, num_threads, flags). */ + fnpos = 0; + argpos = 1; + break; + case BUILT_IN_GOACC_PARALLEL: + /* __builtin_GOACC_parallel (flags_m, fn, mapnum, hostaddrs, + sizes, kinds, ...). */ + fnpos = 1; + argpos = 3; + break; + default: + gcc_unreachable (); + } + + tree fnarg = gimple_call_arg (t, fnpos); + gcc_assert (TREE_CODE (fnarg) == ADDR_EXPR); + tree fndecl = TREE_OPERAND (fnarg, 0); + if (fndecl_maybe_in_other_partition (fndecl)) + /* Fallthru to general call handling. */ + break; + + tree arg = gimple_call_arg (t, argpos); + + varinfo_t fi = get_vi_for_tree (fndecl); + find_func_aliases_for_call_arg (fi, 0, arg); + return true; + } + /* Else fallthru to generic call handling. */ + break; + } + /* printf-style functions may have hooks to set pointers to + point to somewhere into the generated string. Leave them + for a later exercise... */ + default: + /* Fallthru to general call handling. */; + } + + return false; +} + +/* Create constraints for the call T. */ + +static void +find_func_aliases_for_call (struct function *fn, gcall *t) +{ + tree fndecl = gimple_call_fndecl (t); + varinfo_t fi; + + if (fndecl != NULL_TREE + && fndecl_built_in_p (fndecl) + && find_func_aliases_for_builtin_call (fn, t)) + return; + + if (gimple_call_internal_p (t, IFN_DEFERRED_INIT)) + return; + + fi = get_fi_for_callee (t); + if (!in_ipa_mode + || (fi->decl && fndecl && !fi->is_fn_info)) + { + auto_vec<ce_s, 16> rhsc; + int flags = gimple_call_flags (t); + + /* Const functions can return their arguments and addresses + of global memory but not of escaped memory. */ + if (flags & (ECF_CONST|ECF_NOVOPS)) + { + if (gimple_call_lhs (t)) + handle_rhs_call (t, &rhsc, implicit_const_eaf_flags, false, false); + } + /* Pure functions can return addresses in and of memory + reachable from their arguments, but they are not an escape + point for reachable memory of their arguments. */ + else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE)) + handle_rhs_call (t, &rhsc, implicit_pure_eaf_flags, false, true); + /* If the call is to a replaceable operator delete and results + from a delete expression as opposed to a direct call to + such operator, then the effects for PTA (in particular + the escaping of the pointer) can be ignored. */ + else if (fndecl + && DECL_IS_OPERATOR_DELETE_P (fndecl) + && gimple_call_from_new_or_delete (t)) + ; + else + handle_rhs_call (t, &rhsc, 0, true, true); + if (gimple_call_lhs (t)) + handle_lhs_call (t, gimple_call_lhs (t), + gimple_call_return_flags (t), rhsc, fndecl); + } + else + { + auto_vec<ce_s, 2> rhsc; + tree lhsop; + unsigned j; + + /* Assign all the passed arguments to the appropriate incoming + parameters of the function. */ + for (j = 0; j < gimple_call_num_args (t); j++) + { + tree arg = gimple_call_arg (t, j); + find_func_aliases_for_call_arg (fi, j, arg); + } + + /* If we are returning a value, assign it to the result. */ + lhsop = gimple_call_lhs (t); + if (lhsop) + { + auto_vec<ce_s, 2> lhsc; + struct constraint_expr rhs; + struct constraint_expr *lhsp; + bool aggr_p = aggregate_value_p (lhsop, gimple_call_fntype (t)); + + get_constraint_for (lhsop, &lhsc); + rhs = get_function_part_constraint (fi, fi_result); + if (aggr_p) + { + auto_vec<ce_s, 2> tem; + tem.quick_push (rhs); + do_deref (&tem); + gcc_checking_assert (tem.length () == 1); + rhs = tem[0]; + } + FOR_EACH_VEC_ELT (lhsc, j, lhsp) + process_constraint (new_constraint (*lhsp, rhs)); + + /* If we pass the result decl by reference, honor that. */ + if (aggr_p) + { + struct constraint_expr lhs; + struct constraint_expr *rhsp; + + get_constraint_for_address_of (lhsop, &rhsc); + lhs = get_function_part_constraint (fi, fi_result); + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + } + } + + /* If we use a static chain, pass it along. */ + if (gimple_call_chain (t)) + { + struct constraint_expr lhs; + struct constraint_expr *rhsp; + + get_constraint_for (gimple_call_chain (t), &rhsc); + lhs = get_function_part_constraint (fi, fi_static_chain); + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + } + } +} + +/* Walk statement T setting up aliasing constraints according to the + references found in T. This function is the main part of the + constraint builder. AI points to auxiliary alias information used + when building alias sets and computing alias grouping heuristics. */ + +static void +find_func_aliases (struct function *fn, gimple *origt) +{ + gimple *t = origt; + auto_vec<ce_s, 16> lhsc; + auto_vec<ce_s, 16> rhsc; + varinfo_t fi; + + /* Now build constraints expressions. */ + if (gimple_code (t) == GIMPLE_PHI) + { + /* For a phi node, assign all the arguments to + the result. */ + get_constraint_for (gimple_phi_result (t), &lhsc); + for (unsigned i = 0; i < gimple_phi_num_args (t); i++) + { + get_constraint_for_rhs (gimple_phi_arg_def (t, i), &rhsc); + process_all_all_constraints (lhsc, rhsc); + rhsc.truncate (0); + } + } + /* In IPA mode, we need to generate constraints to pass call + arguments through their calls. There are two cases, + either a GIMPLE_CALL returning a value, or just a plain + GIMPLE_CALL when we are not. + + In non-ipa mode, we need to generate constraints for each + pointer passed by address. */ + else if (is_gimple_call (t)) + find_func_aliases_for_call (fn, as_a <gcall *> (t)); + + /* Otherwise, just a regular assignment statement. Only care about + operations with pointer result, others are dealt with as escape + points if they have pointer operands. */ + else if (is_gimple_assign (t)) + { + /* Otherwise, just a regular assignment statement. */ + tree lhsop = gimple_assign_lhs (t); + tree rhsop = (gimple_num_ops (t) == 2) ? gimple_assign_rhs1 (t) : NULL; + + if (rhsop && TREE_CLOBBER_P (rhsop)) + /* Ignore clobbers, they don't actually store anything into + the LHS. */ + ; + else if (rhsop && AGGREGATE_TYPE_P (TREE_TYPE (lhsop))) + do_structure_copy (lhsop, rhsop); + else + { + enum tree_code code = gimple_assign_rhs_code (t); + + get_constraint_for (lhsop, &lhsc); + + if (code == POINTER_PLUS_EXPR) + get_constraint_for_ptr_offset (gimple_assign_rhs1 (t), + gimple_assign_rhs2 (t), &rhsc); + else if (code == POINTER_DIFF_EXPR) + /* The result is not a pointer (part). */ + ; + else if (code == BIT_AND_EXPR + && TREE_CODE (gimple_assign_rhs2 (t)) == INTEGER_CST) + { + /* Aligning a pointer via a BIT_AND_EXPR is offsetting + the pointer. Handle it by offsetting it by UNKNOWN. */ + get_constraint_for_ptr_offset (gimple_assign_rhs1 (t), + NULL_TREE, &rhsc); + } + else if (code == TRUNC_DIV_EXPR + || code == CEIL_DIV_EXPR + || code == FLOOR_DIV_EXPR + || code == ROUND_DIV_EXPR + || code == EXACT_DIV_EXPR + || code == TRUNC_MOD_EXPR + || code == CEIL_MOD_EXPR + || code == FLOOR_MOD_EXPR + || code == ROUND_MOD_EXPR) + /* Division and modulo transfer the pointer from the LHS. */ + get_constraint_for_ptr_offset (gimple_assign_rhs1 (t), + NULL_TREE, &rhsc); + else if (CONVERT_EXPR_CODE_P (code) + || gimple_assign_single_p (t)) + /* See through conversions, single RHS are handled by + get_constraint_for_rhs. */ + get_constraint_for_rhs (rhsop, &rhsc); + else if (code == COND_EXPR) + { + /* The result is a merge of both COND_EXPR arms. */ + auto_vec<ce_s, 2> tmp; + struct constraint_expr *rhsp; + unsigned i; + get_constraint_for_rhs (gimple_assign_rhs2 (t), &rhsc); + get_constraint_for_rhs (gimple_assign_rhs3 (t), &tmp); + FOR_EACH_VEC_ELT (tmp, i, rhsp) + rhsc.safe_push (*rhsp); + } + else if (truth_value_p (code)) + /* Truth value results are not pointer (parts). Or at least + very unreasonable obfuscation of a part. */ + ; + else + { + /* All other operations are possibly offsetting merges. */ + auto_vec<ce_s, 4> tmp; + struct constraint_expr *rhsp; + unsigned i, j; + get_constraint_for_ptr_offset (gimple_assign_rhs1 (t), + NULL_TREE, &rhsc); + for (i = 2; i < gimple_num_ops (t); ++i) + { + get_constraint_for_ptr_offset (gimple_op (t, i), + NULL_TREE, &tmp); + FOR_EACH_VEC_ELT (tmp, j, rhsp) + rhsc.safe_push (*rhsp); + tmp.truncate (0); + } + } + process_all_all_constraints (lhsc, rhsc); + } + /* If there is a store to a global variable the rhs escapes. */ + if ((lhsop = get_base_address (lhsop)) != NULL_TREE + && DECL_P (lhsop)) + { + varinfo_t vi = get_vi_for_tree (lhsop); + if ((! in_ipa_mode && vi->is_global_var) + || vi->is_ipa_escape_point) + make_escape_constraint (rhsop); + } + } + /* Handle escapes through return. */ + else if (gimple_code (t) == GIMPLE_RETURN + && gimple_return_retval (as_a <greturn *> (t)) != NULL_TREE) + { + greturn *return_stmt = as_a <greturn *> (t); + fi = NULL; + if (!in_ipa_mode + && SSA_VAR_P (gimple_return_retval (return_stmt))) + { + /* We handle simple returns by post-processing the solutions. */ + ; + } + if (!(fi = get_vi_for_tree (fn->decl))) + make_escape_constraint (gimple_return_retval (return_stmt)); + else if (in_ipa_mode) + { + struct constraint_expr lhs ; + struct constraint_expr *rhsp; + unsigned i; + + lhs = get_function_part_constraint (fi, fi_result); + get_constraint_for_rhs (gimple_return_retval (return_stmt), &rhsc); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + } + } + /* Handle asms conservatively by adding escape constraints to everything. */ + else if (gasm *asm_stmt = dyn_cast <gasm *> (t)) + { + unsigned i, noutputs; + const char **oconstraints; + const char *constraint; + bool allows_mem, allows_reg, is_inout; + + noutputs = gimple_asm_noutputs (asm_stmt); + oconstraints = XALLOCAVEC (const char *, noutputs); + + for (i = 0; i < noutputs; ++i) + { + tree link = gimple_asm_output_op (asm_stmt, i); + tree op = TREE_VALUE (link); + + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + oconstraints[i] = constraint; + parse_output_constraint (&constraint, i, 0, 0, &allows_mem, + &allows_reg, &is_inout); + + /* A memory constraint makes the address of the operand escape. */ + if (!allows_reg && allows_mem) + make_escape_constraint (build_fold_addr_expr (op)); + + /* The asm may read global memory, so outputs may point to + any global memory. */ + if (op) + { + auto_vec<ce_s, 2> lhsc; + struct constraint_expr rhsc, *lhsp; + unsigned j; + get_constraint_for (op, &lhsc); + rhsc.var = nonlocal_id; + rhsc.offset = 0; + rhsc.type = SCALAR; + FOR_EACH_VEC_ELT (lhsc, j, lhsp) + process_constraint (new_constraint (*lhsp, rhsc)); + } + } + for (i = 0; i < gimple_asm_ninputs (asm_stmt); ++i) + { + tree link = gimple_asm_input_op (asm_stmt, i); + tree op = TREE_VALUE (link); + + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + + parse_input_constraint (&constraint, 0, 0, noutputs, 0, oconstraints, + &allows_mem, &allows_reg); + + /* A memory constraint makes the address of the operand escape. */ + if (!allows_reg && allows_mem) + make_escape_constraint (build_fold_addr_expr (op)); + /* Strictly we'd only need the constraint to ESCAPED if + the asm clobbers memory, otherwise using something + along the lines of per-call clobbers/uses would be enough. */ + else if (op) + make_escape_constraint (op); + } + } +} + + +/* Create a constraint adding to the clobber set of FI the memory + pointed to by PTR. */ + +static void +process_ipa_clobber (varinfo_t fi, tree ptr) +{ + vec<ce_s> ptrc = vNULL; + struct constraint_expr *c, lhs; + unsigned i; + get_constraint_for_rhs (ptr, &ptrc); + lhs = get_function_part_constraint (fi, fi_clobbers); + FOR_EACH_VEC_ELT (ptrc, i, c) + process_constraint (new_constraint (lhs, *c)); + ptrc.release (); +} + +/* Walk statement T setting up clobber and use constraints according to the + references found in T. This function is a main part of the + IPA constraint builder. */ + +static void +find_func_clobbers (struct function *fn, gimple *origt) +{ + gimple *t = origt; + auto_vec<ce_s, 16> lhsc; + auto_vec<ce_s, 16> rhsc; + varinfo_t fi; + + /* Add constraints for clobbered/used in IPA mode. + We are not interested in what automatic variables are clobbered + or used as we only use the information in the caller to which + they do not escape. */ + gcc_assert (in_ipa_mode); + + /* If the stmt refers to memory in any way it better had a VUSE. */ + if (gimple_vuse (t) == NULL_TREE) + return; + + /* We'd better have function information for the current function. */ + fi = lookup_vi_for_tree (fn->decl); + gcc_assert (fi != NULL); + + /* Account for stores in assignments and calls. */ + if (gimple_vdef (t) != NULL_TREE + && gimple_has_lhs (t)) + { + tree lhs = gimple_get_lhs (t); + tree tem = lhs; + while (handled_component_p (tem)) + tem = TREE_OPERAND (tem, 0); + if ((DECL_P (tem) + && !auto_var_in_fn_p (tem, fn->decl)) + || INDIRECT_REF_P (tem) + || (TREE_CODE (tem) == MEM_REF + && !(TREE_CODE (TREE_OPERAND (tem, 0)) == ADDR_EXPR + && auto_var_in_fn_p + (TREE_OPERAND (TREE_OPERAND (tem, 0), 0), fn->decl)))) + { + struct constraint_expr lhsc, *rhsp; + unsigned i; + lhsc = get_function_part_constraint (fi, fi_clobbers); + get_constraint_for_address_of (lhs, &rhsc); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhsc, *rhsp)); + rhsc.truncate (0); + } + } + + /* Account for uses in assigments and returns. */ + if (gimple_assign_single_p (t) + || (gimple_code (t) == GIMPLE_RETURN + && gimple_return_retval (as_a <greturn *> (t)) != NULL_TREE)) + { + tree rhs = (gimple_assign_single_p (t) + ? gimple_assign_rhs1 (t) + : gimple_return_retval (as_a <greturn *> (t))); + tree tem = rhs; + while (handled_component_p (tem)) + tem = TREE_OPERAND (tem, 0); + if ((DECL_P (tem) + && !auto_var_in_fn_p (tem, fn->decl)) + || INDIRECT_REF_P (tem) + || (TREE_CODE (tem) == MEM_REF + && !(TREE_CODE (TREE_OPERAND (tem, 0)) == ADDR_EXPR + && auto_var_in_fn_p + (TREE_OPERAND (TREE_OPERAND (tem, 0), 0), fn->decl)))) + { + struct constraint_expr lhs, *rhsp; + unsigned i; + lhs = get_function_part_constraint (fi, fi_uses); + get_constraint_for_address_of (rhs, &rhsc); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + } + } + + if (gcall *call_stmt = dyn_cast <gcall *> (t)) + { + varinfo_t cfi = NULL; + tree decl = gimple_call_fndecl (t); + struct constraint_expr lhs, rhs; + unsigned i, j; + + /* For builtins we do not have separate function info. For those + we do not generate escapes for we have to generate clobbers/uses. */ + if (gimple_call_builtin_p (t, BUILT_IN_NORMAL)) + switch (DECL_FUNCTION_CODE (decl)) + { + /* The following functions use and clobber memory pointed to + by their arguments. */ + case BUILT_IN_STRCPY: + case BUILT_IN_STRNCPY: + case BUILT_IN_BCOPY: + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMMOVE: + case BUILT_IN_MEMPCPY: + case BUILT_IN_STPCPY: + case BUILT_IN_STPNCPY: + case BUILT_IN_STRCAT: + case BUILT_IN_STRNCAT: + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STPNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_STRNCAT_CHK: + { + tree dest = gimple_call_arg (t, (DECL_FUNCTION_CODE (decl) + == BUILT_IN_BCOPY ? 1 : 0)); + tree src = gimple_call_arg (t, (DECL_FUNCTION_CODE (decl) + == BUILT_IN_BCOPY ? 0 : 1)); + unsigned i; + struct constraint_expr *rhsp, *lhsp; + get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc); + lhs = get_function_part_constraint (fi, fi_clobbers); + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + process_constraint (new_constraint (lhs, *lhsp)); + get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc); + lhs = get_function_part_constraint (fi, fi_uses); + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + return; + } + /* The following function clobbers memory pointed to by + its argument. */ + case BUILT_IN_MEMSET: + case BUILT_IN_MEMSET_CHK: + case BUILT_IN_POSIX_MEMALIGN: + { + tree dest = gimple_call_arg (t, 0); + unsigned i; + ce_s *lhsp; + get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc); + lhs = get_function_part_constraint (fi, fi_clobbers); + FOR_EACH_VEC_ELT (lhsc, i, lhsp) + process_constraint (new_constraint (lhs, *lhsp)); + return; + } + /* The following functions clobber their second and third + arguments. */ + case BUILT_IN_SINCOS: + case BUILT_IN_SINCOSF: + case BUILT_IN_SINCOSL: + { + process_ipa_clobber (fi, gimple_call_arg (t, 1)); + process_ipa_clobber (fi, gimple_call_arg (t, 2)); + return; + } + /* The following functions clobber their second argument. */ + case BUILT_IN_FREXP: + case BUILT_IN_FREXPF: + case BUILT_IN_FREXPL: + case BUILT_IN_LGAMMA_R: + case BUILT_IN_LGAMMAF_R: + case BUILT_IN_LGAMMAL_R: + case BUILT_IN_GAMMA_R: + case BUILT_IN_GAMMAF_R: + case BUILT_IN_GAMMAL_R: + case BUILT_IN_MODF: + case BUILT_IN_MODFF: + case BUILT_IN_MODFL: + { + process_ipa_clobber (fi, gimple_call_arg (t, 1)); + return; + } + /* The following functions clobber their third argument. */ + case BUILT_IN_REMQUO: + case BUILT_IN_REMQUOF: + case BUILT_IN_REMQUOL: + { + process_ipa_clobber (fi, gimple_call_arg (t, 2)); + return; + } + /* The following functions neither read nor clobber memory. */ + case BUILT_IN_ASSUME_ALIGNED: + case BUILT_IN_FREE: + return; + /* Trampolines are of no interest to us. */ + case BUILT_IN_INIT_TRAMPOLINE: + case BUILT_IN_ADJUST_TRAMPOLINE: + return; + case BUILT_IN_VA_START: + case BUILT_IN_VA_END: + return; + case BUILT_IN_GOMP_PARALLEL: + case BUILT_IN_GOACC_PARALLEL: + { + unsigned int fnpos, argpos; + unsigned int implicit_use_args[2]; + unsigned int num_implicit_use_args = 0; + switch (DECL_FUNCTION_CODE (decl)) + { + case BUILT_IN_GOMP_PARALLEL: + /* __builtin_GOMP_parallel (fn, data, num_threads, flags). */ + fnpos = 0; + argpos = 1; + break; + case BUILT_IN_GOACC_PARALLEL: + /* __builtin_GOACC_parallel (flags_m, fn, mapnum, hostaddrs, + sizes, kinds, ...). */ + fnpos = 1; + argpos = 3; + implicit_use_args[num_implicit_use_args++] = 4; + implicit_use_args[num_implicit_use_args++] = 5; + break; + default: + gcc_unreachable (); + } + + tree fnarg = gimple_call_arg (t, fnpos); + gcc_assert (TREE_CODE (fnarg) == ADDR_EXPR); + tree fndecl = TREE_OPERAND (fnarg, 0); + if (fndecl_maybe_in_other_partition (fndecl)) + /* Fallthru to general call handling. */ + break; + + varinfo_t cfi = get_vi_for_tree (fndecl); + + tree arg = gimple_call_arg (t, argpos); + + /* Parameter passed by value is used. */ + lhs = get_function_part_constraint (fi, fi_uses); + struct constraint_expr *rhsp; + get_constraint_for (arg, &rhsc); + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + + /* Handle parameters used by the call, but not used in cfi, as + implicitly used by cfi. */ + lhs = get_function_part_constraint (cfi, fi_uses); + for (unsigned i = 0; i < num_implicit_use_args; ++i) + { + tree arg = gimple_call_arg (t, implicit_use_args[i]); + get_constraint_for (arg, &rhsc); + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + } + + /* The caller clobbers what the callee does. */ + lhs = get_function_part_constraint (fi, fi_clobbers); + rhs = get_function_part_constraint (cfi, fi_clobbers); + process_constraint (new_constraint (lhs, rhs)); + + /* The caller uses what the callee does. */ + lhs = get_function_part_constraint (fi, fi_uses); + rhs = get_function_part_constraint (cfi, fi_uses); + process_constraint (new_constraint (lhs, rhs)); + + return; + } + /* printf-style functions may have hooks to set pointers to + point to somewhere into the generated string. Leave them + for a later exercise... */ + default: + /* Fallthru to general call handling. */; + } + + /* Parameters passed by value are used. */ + lhs = get_function_part_constraint (fi, fi_uses); + for (i = 0; i < gimple_call_num_args (t); i++) + { + struct constraint_expr *rhsp; + tree arg = gimple_call_arg (t, i); + + if (TREE_CODE (arg) == SSA_NAME + || is_gimple_min_invariant (arg)) + continue; + + get_constraint_for_address_of (arg, &rhsc); + FOR_EACH_VEC_ELT (rhsc, j, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + rhsc.truncate (0); + } + + /* Build constraints for propagating clobbers/uses along the + callgraph edges. */ + cfi = get_fi_for_callee (call_stmt); + if (cfi->id == anything_id) + { + if (gimple_vdef (t)) + make_constraint_from (first_vi_for_offset (fi, fi_clobbers), + anything_id); + make_constraint_from (first_vi_for_offset (fi, fi_uses), + anything_id); + return; + } + + /* For callees without function info (that's external functions), + ESCAPED is clobbered and used. */ + if (cfi->decl + && TREE_CODE (cfi->decl) == FUNCTION_DECL + && !cfi->is_fn_info) + { + varinfo_t vi; + + if (gimple_vdef (t)) + make_copy_constraint (first_vi_for_offset (fi, fi_clobbers), + escaped_id); + make_copy_constraint (first_vi_for_offset (fi, fi_uses), escaped_id); + + /* Also honor the call statement use/clobber info. */ + if ((vi = lookup_call_clobber_vi (call_stmt)) != NULL) + make_copy_constraint (first_vi_for_offset (fi, fi_clobbers), + vi->id); + if ((vi = lookup_call_use_vi (call_stmt)) != NULL) + make_copy_constraint (first_vi_for_offset (fi, fi_uses), + vi->id); + return; + } + + /* Otherwise the caller clobbers and uses what the callee does. + ??? This should use a new complex constraint that filters + local variables of the callee. */ + if (gimple_vdef (t)) + { + lhs = get_function_part_constraint (fi, fi_clobbers); + rhs = get_function_part_constraint (cfi, fi_clobbers); + process_constraint (new_constraint (lhs, rhs)); + } + lhs = get_function_part_constraint (fi, fi_uses); + rhs = get_function_part_constraint (cfi, fi_uses); + process_constraint (new_constraint (lhs, rhs)); + } + else if (gimple_code (t) == GIMPLE_ASM) + { + /* ??? Ick. We can do better. */ + if (gimple_vdef (t)) + make_constraint_from (first_vi_for_offset (fi, fi_clobbers), + anything_id); + make_constraint_from (first_vi_for_offset (fi, fi_uses), + anything_id); + } +} + + +/* Find the first varinfo in the same variable as START that overlaps with + OFFSET. Return NULL if we can't find one. */ + +static varinfo_t +first_vi_for_offset (varinfo_t start, unsigned HOST_WIDE_INT offset) +{ + /* If the offset is outside of the variable, bail out. */ + if (offset >= start->fullsize) + return NULL; + + /* If we cannot reach offset from start, lookup the first field + and start from there. */ + if (start->offset > offset) + start = get_varinfo (start->head); + + while (start) + { + /* We may not find a variable in the field list with the actual + offset when we have glommed a structure to a variable. + In that case, however, offset should still be within the size + of the variable. */ + if (offset >= start->offset + && (offset - start->offset) < start->size) + return start; + + start = vi_next (start); + } + + return NULL; +} + +/* Find the first varinfo in the same variable as START that overlaps with + OFFSET. If there is no such varinfo the varinfo directly preceding + OFFSET is returned. */ + +static varinfo_t +first_or_preceding_vi_for_offset (varinfo_t start, + unsigned HOST_WIDE_INT offset) +{ + /* If we cannot reach offset from start, lookup the first field + and start from there. */ + if (start->offset > offset) + start = get_varinfo (start->head); + + /* We may not find a variable in the field list with the actual + offset when we have glommed a structure to a variable. + In that case, however, offset should still be within the size + of the variable. + If we got beyond the offset we look for return the field + directly preceding offset which may be the last field. */ + while (start->next + && offset >= start->offset + && !((offset - start->offset) < start->size)) + start = vi_next (start); + + return start; +} + + +/* This structure is used during pushing fields onto the fieldstack + to track the offset of the field, since bitpos_of_field gives it + relative to its immediate containing type, and we want it relative + to the ultimate containing object. */ + +struct fieldoff +{ + /* Offset from the base of the base containing object to this field. */ + HOST_WIDE_INT offset; + + /* Size, in bits, of the field. */ + unsigned HOST_WIDE_INT size; + + unsigned has_unknown_size : 1; + + unsigned must_have_pointers : 1; + + unsigned may_have_pointers : 1; + + unsigned only_restrict_pointers : 1; + + tree restrict_pointed_type; +}; +typedef struct fieldoff fieldoff_s; + + +/* qsort comparison function for two fieldoff's PA and PB */ + +static int +fieldoff_compare (const void *pa, const void *pb) +{ + const fieldoff_s *foa = (const fieldoff_s *)pa; + const fieldoff_s *fob = (const fieldoff_s *)pb; + unsigned HOST_WIDE_INT foasize, fobsize; + + if (foa->offset < fob->offset) + return -1; + else if (foa->offset > fob->offset) + return 1; + + foasize = foa->size; + fobsize = fob->size; + if (foasize < fobsize) + return -1; + else if (foasize > fobsize) + return 1; + return 0; +} + +/* Sort a fieldstack according to the field offset and sizes. */ +static void +sort_fieldstack (vec<fieldoff_s> &fieldstack) +{ + fieldstack.qsort (fieldoff_compare); +} + +/* Return true if T is a type that can have subvars. */ + +static inline bool +type_can_have_subvars (const_tree t) +{ + /* Aggregates without overlapping fields can have subvars. */ + return TREE_CODE (t) == RECORD_TYPE; +} + +/* Return true if V is a tree that we can have subvars for. + Normally, this is any aggregate type. Also complex + types which are not gimple registers can have subvars. */ + +static inline bool +var_can_have_subvars (const_tree v) +{ + /* Volatile variables should never have subvars. */ + if (TREE_THIS_VOLATILE (v)) + return false; + + /* Non decls or memory tags can never have subvars. */ + if (!DECL_P (v)) + return false; + + return type_can_have_subvars (TREE_TYPE (v)); +} + +/* Return true if T is a type that does contain pointers. */ + +static bool +type_must_have_pointers (tree type) +{ + if (POINTER_TYPE_P (type)) + return true; + + if (TREE_CODE (type) == ARRAY_TYPE) + return type_must_have_pointers (TREE_TYPE (type)); + + /* A function or method can have pointers as arguments, so track + those separately. */ + if (TREE_CODE (type) == FUNCTION_TYPE + || TREE_CODE (type) == METHOD_TYPE) + return true; + + return false; +} + +static bool +field_must_have_pointers (tree t) +{ + return type_must_have_pointers (TREE_TYPE (t)); +} + +/* Given a TYPE, and a vector of field offsets FIELDSTACK, push all + the fields of TYPE onto fieldstack, recording their offsets along + the way. + + OFFSET is used to keep track of the offset in this entire + structure, rather than just the immediately containing structure. + Returns false if the caller is supposed to handle the field we + recursed for. */ + +static bool +push_fields_onto_fieldstack (tree type, vec<fieldoff_s> *fieldstack, + HOST_WIDE_INT offset) +{ + tree field; + bool empty_p = true; + + if (TREE_CODE (type) != RECORD_TYPE) + return false; + + /* If the vector of fields is growing too big, bail out early. + Callers check for vec::length <= param_max_fields_for_field_sensitive, make + sure this fails. */ + if (fieldstack->length () > (unsigned)param_max_fields_for_field_sensitive) + return false; + + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + { + bool push = false; + HOST_WIDE_INT foff = bitpos_of_field (field); + tree field_type = TREE_TYPE (field); + + if (!var_can_have_subvars (field) + || TREE_CODE (field_type) == QUAL_UNION_TYPE + || TREE_CODE (field_type) == UNION_TYPE) + push = true; + else if (!push_fields_onto_fieldstack + (field_type, fieldstack, offset + foff) + && (DECL_SIZE (field) + && !integer_zerop (DECL_SIZE (field)))) + /* Empty structures may have actual size, like in C++. So + see if we didn't push any subfields and the size is + nonzero, push the field onto the stack. */ + push = true; + + if (push) + { + fieldoff_s *pair = NULL; + bool has_unknown_size = false; + bool must_have_pointers_p; + + if (!fieldstack->is_empty ()) + pair = &fieldstack->last (); + + /* If there isn't anything at offset zero, create sth. */ + if (!pair + && offset + foff != 0) + { + fieldoff_s e + = {0, offset + foff, false, false, true, false, NULL_TREE}; + pair = fieldstack->safe_push (e); + } + + if (!DECL_SIZE (field) + || !tree_fits_uhwi_p (DECL_SIZE (field))) + has_unknown_size = true; + + /* If adjacent fields do not contain pointers merge them. */ + must_have_pointers_p = field_must_have_pointers (field); + if (pair + && !has_unknown_size + && !must_have_pointers_p + && !pair->must_have_pointers + && !pair->has_unknown_size + && pair->offset + (HOST_WIDE_INT)pair->size == offset + foff) + { + pair->size += tree_to_uhwi (DECL_SIZE (field)); + } + else + { + fieldoff_s e; + e.offset = offset + foff; + e.has_unknown_size = has_unknown_size; + if (!has_unknown_size) + e.size = tree_to_uhwi (DECL_SIZE (field)); + else + e.size = -1; + e.must_have_pointers = must_have_pointers_p; + e.may_have_pointers = true; + e.only_restrict_pointers + = (!has_unknown_size + && POINTER_TYPE_P (field_type) + && TYPE_RESTRICT (field_type)); + if (e.only_restrict_pointers) + e.restrict_pointed_type = TREE_TYPE (field_type); + fieldstack->safe_push (e); + } + } + + empty_p = false; + } + + return !empty_p; +} + +/* Count the number of arguments DECL has, and set IS_VARARGS to true + if it is a varargs function. */ + +static unsigned int +count_num_arguments (tree decl, bool *is_varargs) +{ + unsigned int num = 0; + tree t; + + /* Capture named arguments for K&R functions. They do not + have a prototype and thus no TYPE_ARG_TYPES. */ + for (t = DECL_ARGUMENTS (decl); t; t = DECL_CHAIN (t)) + ++num; + + /* Check if the function has variadic arguments. */ + for (t = TYPE_ARG_TYPES (TREE_TYPE (decl)); t; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) == void_type_node) + break; + if (!t) + *is_varargs = true; + + return num; +} + +/* Creation function node for DECL, using NAME, and return the index + of the variable we've created for the function. If NONLOCAL_p, create + initial constraints. */ + +static varinfo_t +create_function_info_for (tree decl, const char *name, bool add_id, + bool nonlocal_p) +{ + struct function *fn = DECL_STRUCT_FUNCTION (decl); + varinfo_t vi, prev_vi; + tree arg; + unsigned int i; + bool is_varargs = false; + unsigned int num_args = count_num_arguments (decl, &is_varargs); + + /* Create the variable info. */ + + vi = new_var_info (decl, name, add_id); + vi->offset = 0; + vi->size = 1; + vi->fullsize = fi_parm_base + num_args; + vi->is_fn_info = 1; + vi->may_have_pointers = false; + if (is_varargs) + vi->fullsize = ~0; + insert_vi_for_tree (vi->decl, vi); + + prev_vi = vi; + + /* Create a variable for things the function clobbers and one for + things the function uses. */ + { + varinfo_t clobbervi, usevi; + const char *newname; + char *tempname; + + tempname = xasprintf ("%s.clobber", name); + newname = ggc_strdup (tempname); + free (tempname); + + clobbervi = new_var_info (NULL, newname, false); + clobbervi->offset = fi_clobbers; + clobbervi->size = 1; + clobbervi->fullsize = vi->fullsize; + clobbervi->is_full_var = true; + clobbervi->is_global_var = false; + clobbervi->is_reg_var = true; + + gcc_assert (prev_vi->offset < clobbervi->offset); + prev_vi->next = clobbervi->id; + prev_vi = clobbervi; + + tempname = xasprintf ("%s.use", name); + newname = ggc_strdup (tempname); + free (tempname); + + usevi = new_var_info (NULL, newname, false); + usevi->offset = fi_uses; + usevi->size = 1; + usevi->fullsize = vi->fullsize; + usevi->is_full_var = true; + usevi->is_global_var = false; + usevi->is_reg_var = true; + + gcc_assert (prev_vi->offset < usevi->offset); + prev_vi->next = usevi->id; + prev_vi = usevi; + } + + /* And one for the static chain. */ + if (fn->static_chain_decl != NULL_TREE) + { + varinfo_t chainvi; + const char *newname; + char *tempname; + + tempname = xasprintf ("%s.chain", name); + newname = ggc_strdup (tempname); + free (tempname); + + chainvi = new_var_info (fn->static_chain_decl, newname, false); + chainvi->offset = fi_static_chain; + chainvi->size = 1; + chainvi->fullsize = vi->fullsize; + chainvi->is_full_var = true; + chainvi->is_global_var = false; + + insert_vi_for_tree (fn->static_chain_decl, chainvi); + + if (nonlocal_p + && chainvi->may_have_pointers) + make_constraint_from (chainvi, nonlocal_id); + + gcc_assert (prev_vi->offset < chainvi->offset); + prev_vi->next = chainvi->id; + prev_vi = chainvi; + } + + /* Create a variable for the return var. */ + if (DECL_RESULT (decl) != NULL + || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl)))) + { + varinfo_t resultvi; + const char *newname; + char *tempname; + tree resultdecl = decl; + + if (DECL_RESULT (decl)) + resultdecl = DECL_RESULT (decl); + + tempname = xasprintf ("%s.result", name); + newname = ggc_strdup (tempname); + free (tempname); + + resultvi = new_var_info (resultdecl, newname, false); + resultvi->offset = fi_result; + resultvi->size = 1; + resultvi->fullsize = vi->fullsize; + resultvi->is_full_var = true; + if (DECL_RESULT (decl)) + resultvi->may_have_pointers = true; + + if (DECL_RESULT (decl)) + insert_vi_for_tree (DECL_RESULT (decl), resultvi); + + if (nonlocal_p + && DECL_RESULT (decl) + && DECL_BY_REFERENCE (DECL_RESULT (decl))) + make_constraint_from (resultvi, nonlocal_id); + + gcc_assert (prev_vi->offset < resultvi->offset); + prev_vi->next = resultvi->id; + prev_vi = resultvi; + } + + /* We also need to make function return values escape. Nothing + escapes by returning from main though. */ + if (nonlocal_p + && !MAIN_NAME_P (DECL_NAME (decl))) + { + varinfo_t fi, rvi; + fi = lookup_vi_for_tree (decl); + rvi = first_vi_for_offset (fi, fi_result); + if (rvi && rvi->offset == fi_result) + make_copy_constraint (get_varinfo (escaped_id), rvi->id); + } + + /* Set up variables for each argument. */ + arg = DECL_ARGUMENTS (decl); + for (i = 0; i < num_args; i++) + { + varinfo_t argvi; + const char *newname; + char *tempname; + tree argdecl = decl; + + if (arg) + argdecl = arg; + + tempname = xasprintf ("%s.arg%d", name, i); + newname = ggc_strdup (tempname); + free (tempname); + + argvi = new_var_info (argdecl, newname, false); + argvi->offset = fi_parm_base + i; + argvi->size = 1; + argvi->is_full_var = true; + argvi->fullsize = vi->fullsize; + if (arg) + argvi->may_have_pointers = true; + + if (arg) + insert_vi_for_tree (arg, argvi); + + if (nonlocal_p + && argvi->may_have_pointers) + make_constraint_from (argvi, nonlocal_id); + + gcc_assert (prev_vi->offset < argvi->offset); + prev_vi->next = argvi->id; + prev_vi = argvi; + if (arg) + arg = DECL_CHAIN (arg); + } + + /* Add one representative for all further args. */ + if (is_varargs) + { + varinfo_t argvi; + const char *newname; + char *tempname; + tree decl; + + tempname = xasprintf ("%s.varargs", name); + newname = ggc_strdup (tempname); + free (tempname); + + /* We need sth that can be pointed to for va_start. */ + decl = build_fake_var_decl (ptr_type_node); + + argvi = new_var_info (decl, newname, false); + argvi->offset = fi_parm_base + num_args; + argvi->size = ~0; + argvi->is_full_var = true; + argvi->is_heap_var = true; + argvi->fullsize = vi->fullsize; + + if (nonlocal_p + && argvi->may_have_pointers) + make_constraint_from (argvi, nonlocal_id); + + gcc_assert (prev_vi->offset < argvi->offset); + prev_vi->next = argvi->id; + } + + return vi; +} + + +/* Return true if FIELDSTACK contains fields that overlap. + FIELDSTACK is assumed to be sorted by offset. */ + +static bool +check_for_overlaps (const vec<fieldoff_s> &fieldstack) +{ + fieldoff_s *fo = NULL; + unsigned int i; + HOST_WIDE_INT lastoffset = -1; + + FOR_EACH_VEC_ELT (fieldstack, i, fo) + { + if (fo->offset == lastoffset) + return true; + lastoffset = fo->offset; + } + return false; +} + +/* Create a varinfo structure for NAME and DECL, and add it to VARMAP. + This will also create any varinfo structures necessary for fields + of DECL. DECL is a function parameter if HANDLE_PARAM is set. + HANDLED_STRUCT_TYPE is used to register struct types reached by following + restrict pointers. This is needed to prevent infinite recursion. + If ADD_RESTRICT, pretend that the pointer NAME is restrict even if DECL + does not advertise it. */ + +static varinfo_t +create_variable_info_for_1 (tree decl, const char *name, bool add_id, + bool handle_param, bitmap handled_struct_type, + bool add_restrict = false) +{ + varinfo_t vi, newvi; + tree decl_type = TREE_TYPE (decl); + tree declsize = DECL_P (decl) ? DECL_SIZE (decl) : TYPE_SIZE (decl_type); + auto_vec<fieldoff_s> fieldstack; + fieldoff_s *fo; + unsigned int i; + + if (!declsize + || !tree_fits_uhwi_p (declsize)) + { + vi = new_var_info (decl, name, add_id); + vi->offset = 0; + vi->size = ~0; + vi->fullsize = ~0; + vi->is_unknown_size_var = true; + vi->is_full_var = true; + vi->may_have_pointers = true; + return vi; + } + + /* Collect field information. */ + if (use_field_sensitive + && var_can_have_subvars (decl) + /* ??? Force us to not use subfields for globals in IPA mode. + Else we'd have to parse arbitrary initializers. */ + && !(in_ipa_mode + && is_global_var (decl))) + { + fieldoff_s *fo = NULL; + bool notokay = false; + unsigned int i; + + push_fields_onto_fieldstack (decl_type, &fieldstack, 0); + + for (i = 0; !notokay && fieldstack.iterate (i, &fo); i++) + if (fo->has_unknown_size + || fo->offset < 0) + { + notokay = true; + break; + } + + /* We can't sort them if we have a field with a variable sized type, + which will make notokay = true. In that case, we are going to return + without creating varinfos for the fields anyway, so sorting them is a + waste to boot. */ + if (!notokay) + { + sort_fieldstack (fieldstack); + /* Due to some C++ FE issues, like PR 22488, we might end up + what appear to be overlapping fields even though they, + in reality, do not overlap. Until the C++ FE is fixed, + we will simply disable field-sensitivity for these cases. */ + notokay = check_for_overlaps (fieldstack); + } + + if (notokay) + fieldstack.release (); + } + + /* If we didn't end up collecting sub-variables create a full + variable for the decl. */ + if (fieldstack.length () == 0 + || fieldstack.length () > (unsigned)param_max_fields_for_field_sensitive) + { + vi = new_var_info (decl, name, add_id); + vi->offset = 0; + vi->may_have_pointers = true; + vi->fullsize = tree_to_uhwi (declsize); + vi->size = vi->fullsize; + vi->is_full_var = true; + if (POINTER_TYPE_P (decl_type) + && (TYPE_RESTRICT (decl_type) || add_restrict)) + vi->only_restrict_pointers = 1; + if (vi->only_restrict_pointers + && !type_contains_placeholder_p (TREE_TYPE (decl_type)) + && handle_param + && !bitmap_bit_p (handled_struct_type, + TYPE_UID (TREE_TYPE (decl_type)))) + { + varinfo_t rvi; + tree heapvar = build_fake_var_decl (TREE_TYPE (decl_type)); + DECL_EXTERNAL (heapvar) = 1; + if (var_can_have_subvars (heapvar)) + bitmap_set_bit (handled_struct_type, + TYPE_UID (TREE_TYPE (decl_type))); + rvi = create_variable_info_for_1 (heapvar, "PARM_NOALIAS", true, + true, handled_struct_type); + if (var_can_have_subvars (heapvar)) + bitmap_clear_bit (handled_struct_type, + TYPE_UID (TREE_TYPE (decl_type))); + rvi->is_restrict_var = 1; + insert_vi_for_tree (heapvar, rvi); + make_constraint_from (vi, rvi->id); + make_param_constraints (rvi); + } + fieldstack.release (); + return vi; + } + + vi = new_var_info (decl, name, add_id); + vi->fullsize = tree_to_uhwi (declsize); + if (fieldstack.length () == 1) + vi->is_full_var = true; + for (i = 0, newvi = vi; + fieldstack.iterate (i, &fo); + ++i, newvi = vi_next (newvi)) + { + const char *newname = NULL; + char *tempname; + + if (dump_file) + { + if (fieldstack.length () != 1) + { + tempname + = xasprintf ("%s." HOST_WIDE_INT_PRINT_DEC + "+" HOST_WIDE_INT_PRINT_DEC, name, + fo->offset, fo->size); + newname = ggc_strdup (tempname); + free (tempname); + } + } + else + newname = "NULL"; + + if (newname) + newvi->name = newname; + newvi->offset = fo->offset; + newvi->size = fo->size; + newvi->fullsize = vi->fullsize; + newvi->may_have_pointers = fo->may_have_pointers; + newvi->only_restrict_pointers = fo->only_restrict_pointers; + if (handle_param + && newvi->only_restrict_pointers + && !type_contains_placeholder_p (fo->restrict_pointed_type) + && !bitmap_bit_p (handled_struct_type, + TYPE_UID (fo->restrict_pointed_type))) + { + varinfo_t rvi; + tree heapvar = build_fake_var_decl (fo->restrict_pointed_type); + DECL_EXTERNAL (heapvar) = 1; + if (var_can_have_subvars (heapvar)) + bitmap_set_bit (handled_struct_type, + TYPE_UID (fo->restrict_pointed_type)); + rvi = create_variable_info_for_1 (heapvar, "PARM_NOALIAS", true, + true, handled_struct_type); + if (var_can_have_subvars (heapvar)) + bitmap_clear_bit (handled_struct_type, + TYPE_UID (fo->restrict_pointed_type)); + rvi->is_restrict_var = 1; + insert_vi_for_tree (heapvar, rvi); + make_constraint_from (newvi, rvi->id); + make_param_constraints (rvi); + } + if (i + 1 < fieldstack.length ()) + { + varinfo_t tem = new_var_info (decl, name, false); + newvi->next = tem->id; + tem->head = vi->id; + } + } + + return vi; +} + +static unsigned int +create_variable_info_for (tree decl, const char *name, bool add_id) +{ + /* First see if we are dealing with an ifunc resolver call and + assiociate that with a call to the resolver function result. */ + cgraph_node *node; + if (in_ipa_mode + && TREE_CODE (decl) == FUNCTION_DECL + && (node = cgraph_node::get (decl)) + && node->ifunc_resolver) + { + varinfo_t fi = get_vi_for_tree (node->get_alias_target ()->decl); + constraint_expr rhs + = get_function_part_constraint (fi, fi_result); + fi = new_var_info (NULL_TREE, "ifuncres", true); + fi->is_reg_var = true; + constraint_expr lhs; + lhs.type = SCALAR; + lhs.var = fi->id; + lhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + insert_vi_for_tree (decl, fi); + return fi->id; + } + + varinfo_t vi = create_variable_info_for_1 (decl, name, add_id, false, NULL); + unsigned int id = vi->id; + + insert_vi_for_tree (decl, vi); + + if (!VAR_P (decl)) + return id; + + /* Create initial constraints for globals. */ + for (; vi; vi = vi_next (vi)) + { + if (!vi->may_have_pointers + || !vi->is_global_var) + continue; + + /* Mark global restrict qualified pointers. */ + if ((POINTER_TYPE_P (TREE_TYPE (decl)) + && TYPE_RESTRICT (TREE_TYPE (decl))) + || vi->only_restrict_pointers) + { + varinfo_t rvi + = make_constraint_from_global_restrict (vi, "GLOBAL_RESTRICT", + true); + /* ??? For now exclude reads from globals as restrict sources + if those are not (indirectly) from incoming parameters. */ + rvi->is_restrict_var = false; + continue; + } + + /* In non-IPA mode the initializer from nonlocal is all we need. */ + if (!in_ipa_mode + || DECL_HARD_REGISTER (decl)) + make_copy_constraint (vi, nonlocal_id); + + /* In IPA mode parse the initializer and generate proper constraints + for it. */ + else + { + varpool_node *vnode = varpool_node::get (decl); + + /* For escaped variables initialize them from nonlocal. */ + if (!vnode->all_refs_explicit_p ()) + make_copy_constraint (vi, nonlocal_id); + + /* If this is a global variable with an initializer and we are in + IPA mode generate constraints for it. */ + ipa_ref *ref; + for (unsigned idx = 0; vnode->iterate_reference (idx, ref); ++idx) + { + auto_vec<ce_s> rhsc; + struct constraint_expr lhs, *rhsp; + unsigned i; + get_constraint_for_address_of (ref->referred->decl, &rhsc); + lhs.var = vi->id; + lhs.offset = 0; + lhs.type = SCALAR; + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + /* If this is a variable that escapes from the unit + the initializer escapes as well. */ + if (!vnode->all_refs_explicit_p ()) + { + lhs.var = escaped_id; + lhs.offset = 0; + lhs.type = SCALAR; + FOR_EACH_VEC_ELT (rhsc, i, rhsp) + process_constraint (new_constraint (lhs, *rhsp)); + } + } + } + } + + return id; +} + +/* Print out the points-to solution for VAR to FILE. */ + +static void +dump_solution_for_var (FILE *file, unsigned int var) +{ + varinfo_t vi = get_varinfo (var); + unsigned int i; + bitmap_iterator bi; + + /* Dump the solution for unified vars anyway, this avoids difficulties + in scanning dumps in the testsuite. */ + fprintf (file, "%s = { ", vi->name); + vi = get_varinfo (find (var)); + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi) + fprintf (file, "%s ", get_varinfo (i)->name); + fprintf (file, "}"); + + /* But note when the variable was unified. */ + if (vi->id != var) + fprintf (file, " same as %s", vi->name); + + fprintf (file, "\n"); +} + +/* Print the points-to solution for VAR to stderr. */ + +DEBUG_FUNCTION void +debug_solution_for_var (unsigned int var) +{ + dump_solution_for_var (stderr, var); +} + +/* Register the constraints for function parameter related VI. */ + +static void +make_param_constraints (varinfo_t vi) +{ + for (; vi; vi = vi_next (vi)) + { + if (vi->only_restrict_pointers) + ; + else if (vi->may_have_pointers) + make_constraint_from (vi, nonlocal_id); + + if (vi->is_full_var) + break; + } +} + +/* Create varinfo structures for all of the variables in the + function for intraprocedural mode. */ + +static void +intra_create_variable_infos (struct function *fn) +{ + tree t; + bitmap handled_struct_type = NULL; + bool this_parm_in_ctor = DECL_CXX_CONSTRUCTOR_P (fn->decl); + + /* For each incoming pointer argument arg, create the constraint ARG + = NONLOCAL or a dummy variable if it is a restrict qualified + passed-by-reference argument. */ + for (t = DECL_ARGUMENTS (fn->decl); t; t = DECL_CHAIN (t)) + { + if (handled_struct_type == NULL) + handled_struct_type = BITMAP_ALLOC (NULL); + + varinfo_t p + = create_variable_info_for_1 (t, alias_get_name (t), false, true, + handled_struct_type, this_parm_in_ctor); + insert_vi_for_tree (t, p); + + make_param_constraints (p); + + this_parm_in_ctor = false; + } + + if (handled_struct_type != NULL) + BITMAP_FREE (handled_struct_type); + + /* Add a constraint for a result decl that is passed by reference. */ + if (DECL_RESULT (fn->decl) + && DECL_BY_REFERENCE (DECL_RESULT (fn->decl))) + { + varinfo_t p, result_vi = get_vi_for_tree (DECL_RESULT (fn->decl)); + + for (p = result_vi; p; p = vi_next (p)) + make_constraint_from (p, nonlocal_id); + } + + /* Add a constraint for the incoming static chain parameter. */ + if (fn->static_chain_decl != NULL_TREE) + { + varinfo_t p, chain_vi = get_vi_for_tree (fn->static_chain_decl); + + for (p = chain_vi; p; p = vi_next (p)) + make_constraint_from (p, nonlocal_id); + } +} + +/* Structure used to put solution bitmaps in a hashtable so they can + be shared among variables with the same points-to set. */ + +typedef struct shared_bitmap_info +{ + bitmap pt_vars; + hashval_t hashcode; +} *shared_bitmap_info_t; +typedef const struct shared_bitmap_info *const_shared_bitmap_info_t; + +/* Shared_bitmap hashtable helpers. */ + +struct shared_bitmap_hasher : free_ptr_hash <shared_bitmap_info> +{ + static inline hashval_t hash (const shared_bitmap_info *); + static inline bool equal (const shared_bitmap_info *, + const shared_bitmap_info *); +}; + +/* Hash function for a shared_bitmap_info_t */ + +inline hashval_t +shared_bitmap_hasher::hash (const shared_bitmap_info *bi) +{ + return bi->hashcode; +} + +/* Equality function for two shared_bitmap_info_t's. */ + +inline bool +shared_bitmap_hasher::equal (const shared_bitmap_info *sbi1, + const shared_bitmap_info *sbi2) +{ + return bitmap_equal_p (sbi1->pt_vars, sbi2->pt_vars); +} + +/* Shared_bitmap hashtable. */ + +static hash_table<shared_bitmap_hasher> *shared_bitmap_table; + +/* Lookup a bitmap in the shared bitmap hashtable, and return an already + existing instance if there is one, NULL otherwise. */ + +static bitmap +shared_bitmap_lookup (bitmap pt_vars) +{ + shared_bitmap_info **slot; + struct shared_bitmap_info sbi; + + sbi.pt_vars = pt_vars; + sbi.hashcode = bitmap_hash (pt_vars); + + slot = shared_bitmap_table->find_slot (&sbi, NO_INSERT); + if (!slot) + return NULL; + else + return (*slot)->pt_vars; +} + + +/* Add a bitmap to the shared bitmap hashtable. */ + +static void +shared_bitmap_add (bitmap pt_vars) +{ + shared_bitmap_info **slot; + shared_bitmap_info_t sbi = XNEW (struct shared_bitmap_info); + + sbi->pt_vars = pt_vars; + sbi->hashcode = bitmap_hash (pt_vars); + + slot = shared_bitmap_table->find_slot (sbi, INSERT); + gcc_assert (!*slot); + *slot = sbi; +} + + +/* Set bits in INTO corresponding to the variable uids in solution set FROM. */ + +static void +set_uids_in_ptset (bitmap into, bitmap from, struct pt_solution *pt, + tree fndecl) +{ + unsigned int i; + bitmap_iterator bi; + varinfo_t escaped_vi = get_varinfo (find (escaped_id)); + bool everything_escaped + = escaped_vi->solution && bitmap_bit_p (escaped_vi->solution, anything_id); + + EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi) + { + varinfo_t vi = get_varinfo (i); + + if (vi->is_artificial_var) + continue; + + if (everything_escaped + || (escaped_vi->solution + && bitmap_bit_p (escaped_vi->solution, i))) + { + pt->vars_contains_escaped = true; + pt->vars_contains_escaped_heap |= vi->is_heap_var; + } + + if (vi->is_restrict_var) + pt->vars_contains_restrict = true; + + if (VAR_P (vi->decl) + || TREE_CODE (vi->decl) == PARM_DECL + || TREE_CODE (vi->decl) == RESULT_DECL) + { + /* If we are in IPA mode we will not recompute points-to + sets after inlining so make sure they stay valid. */ + if (in_ipa_mode + && !DECL_PT_UID_SET_P (vi->decl)) + SET_DECL_PT_UID (vi->decl, DECL_UID (vi->decl)); + + /* Add the decl to the points-to set. Note that the points-to + set contains global variables. */ + bitmap_set_bit (into, DECL_PT_UID (vi->decl)); + if (vi->is_global_var + /* In IPA mode the escaped_heap trick doesn't work as + ESCAPED is escaped from the unit but + pt_solution_includes_global needs to answer true for + all variables not automatic within a function. + For the same reason is_global_var is not the + correct flag to track - local variables from other + functions also need to be considered global. + Conveniently all HEAP vars are not put in function + scope. */ + || (in_ipa_mode + && fndecl + && ! auto_var_in_fn_p (vi->decl, fndecl))) + pt->vars_contains_nonlocal = true; + + /* If we have a variable that is interposable record that fact + for pointer comparison simplification. */ + if (VAR_P (vi->decl) + && (TREE_STATIC (vi->decl) || DECL_EXTERNAL (vi->decl)) + && ! decl_binds_to_current_def_p (vi->decl)) + pt->vars_contains_interposable = true; + + /* If this is a local variable we can have overlapping lifetime + of different function invocations through recursion duplicate + it with its shadow variable. */ + if (in_ipa_mode + && vi->shadow_var_uid != 0) + { + bitmap_set_bit (into, vi->shadow_var_uid); + pt->vars_contains_nonlocal = true; + } + } + + else if (TREE_CODE (vi->decl) == FUNCTION_DECL + || TREE_CODE (vi->decl) == LABEL_DECL) + { + /* Nothing should read/write from/to code so we can + save bits by not including them in the points-to bitmaps. + Still mark the points-to set as containing global memory + to make code-patching possible - see PR70128. */ + pt->vars_contains_nonlocal = true; + } + } +} + + +/* Compute the points-to solution *PT for the variable VI. */ + +static struct pt_solution +find_what_var_points_to (tree fndecl, varinfo_t orig_vi) +{ + unsigned int i; + bitmap_iterator bi; + bitmap finished_solution; + bitmap result; + varinfo_t vi; + struct pt_solution *pt; + + /* This variable may have been collapsed, let's get the real + variable. */ + vi = get_varinfo (find (orig_vi->id)); + + /* See if we have already computed the solution and return it. */ + pt_solution **slot = &final_solutions->get_or_insert (vi); + if (*slot != NULL) + return **slot; + + *slot = pt = XOBNEW (&final_solutions_obstack, struct pt_solution); + memset (pt, 0, sizeof (struct pt_solution)); + + /* Translate artificial variables into SSA_NAME_PTR_INFO + attributes. */ + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi) + { + varinfo_t vi = get_varinfo (i); + + if (vi->is_artificial_var) + { + if (vi->id == nothing_id) + pt->null = 1; + else if (vi->id == escaped_id) + { + if (in_ipa_mode) + pt->ipa_escaped = 1; + else + pt->escaped = 1; + /* Expand some special vars of ESCAPED in-place here. */ + varinfo_t evi = get_varinfo (find (escaped_id)); + if (bitmap_bit_p (evi->solution, nonlocal_id)) + pt->nonlocal = 1; + } + else if (vi->id == nonlocal_id) + pt->nonlocal = 1; + else if (vi->id == string_id) + /* Nobody cares - STRING_CSTs are read-only entities. */ + ; + else if (vi->id == anything_id + || vi->id == integer_id) + pt->anything = 1; + } + } + + /* Instead of doing extra work, simply do not create + elaborate points-to information for pt_anything pointers. */ + if (pt->anything) + return *pt; + + /* Share the final set of variables when possible. */ + finished_solution = BITMAP_GGC_ALLOC (); + stats.points_to_sets_created++; + + set_uids_in_ptset (finished_solution, vi->solution, pt, fndecl); + result = shared_bitmap_lookup (finished_solution); + if (!result) + { + shared_bitmap_add (finished_solution); + pt->vars = finished_solution; + } + else + { + pt->vars = result; + bitmap_clear (finished_solution); + } + + return *pt; +} + +/* Given a pointer variable P, fill in its points-to set. */ + +static void +find_what_p_points_to (tree fndecl, tree p) +{ + struct ptr_info_def *pi; + tree lookup_p = p; + varinfo_t vi; + value_range vr; + get_range_query (DECL_STRUCT_FUNCTION (fndecl))->range_of_expr (vr, p); + bool nonnull = vr.nonzero_p (); + + /* For parameters, get at the points-to set for the actual parm + decl. */ + if (TREE_CODE (p) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (p) + && (TREE_CODE (SSA_NAME_VAR (p)) == PARM_DECL + || TREE_CODE (SSA_NAME_VAR (p)) == RESULT_DECL)) + lookup_p = SSA_NAME_VAR (p); + + vi = lookup_vi_for_tree (lookup_p); + if (!vi) + return; + + pi = get_ptr_info (p); + pi->pt = find_what_var_points_to (fndecl, vi); + /* Conservatively set to NULL from PTA (to true). */ + pi->pt.null = 1; + /* Preserve pointer nonnull globally computed. */ + if (nonnull) + set_ptr_nonnull (p); +} + + +/* Query statistics for points-to solutions. */ + +static struct { + unsigned HOST_WIDE_INT pt_solution_includes_may_alias; + unsigned HOST_WIDE_INT pt_solution_includes_no_alias; + unsigned HOST_WIDE_INT pt_solutions_intersect_may_alias; + unsigned HOST_WIDE_INT pt_solutions_intersect_no_alias; +} pta_stats; + +void +dump_pta_stats (FILE *s) +{ + fprintf (s, "\nPTA query stats:\n"); + fprintf (s, " pt_solution_includes: " + HOST_WIDE_INT_PRINT_DEC" disambiguations, " + HOST_WIDE_INT_PRINT_DEC" queries\n", + pta_stats.pt_solution_includes_no_alias, + pta_stats.pt_solution_includes_no_alias + + pta_stats.pt_solution_includes_may_alias); + fprintf (s, " pt_solutions_intersect: " + HOST_WIDE_INT_PRINT_DEC" disambiguations, " + HOST_WIDE_INT_PRINT_DEC" queries\n", + pta_stats.pt_solutions_intersect_no_alias, + pta_stats.pt_solutions_intersect_no_alias + + pta_stats.pt_solutions_intersect_may_alias); +} + + +/* Reset the points-to solution *PT to a conservative default + (point to anything). */ + +void +pt_solution_reset (struct pt_solution *pt) +{ + memset (pt, 0, sizeof (struct pt_solution)); + pt->anything = true; + pt->null = true; +} + +/* Set the points-to solution *PT to point only to the variables + in VARS. VARS_CONTAINS_GLOBAL specifies whether that contains + global variables and VARS_CONTAINS_RESTRICT specifies whether + it contains restrict tag variables. */ + +void +pt_solution_set (struct pt_solution *pt, bitmap vars, + bool vars_contains_nonlocal) +{ + memset (pt, 0, sizeof (struct pt_solution)); + pt->vars = vars; + pt->vars_contains_nonlocal = vars_contains_nonlocal; + pt->vars_contains_escaped + = (cfun->gimple_df->escaped.anything + || bitmap_intersect_p (cfun->gimple_df->escaped.vars, vars)); +} + +/* Set the points-to solution *PT to point only to the variable VAR. */ + +void +pt_solution_set_var (struct pt_solution *pt, tree var) +{ + memset (pt, 0, sizeof (struct pt_solution)); + pt->vars = BITMAP_GGC_ALLOC (); + bitmap_set_bit (pt->vars, DECL_PT_UID (var)); + pt->vars_contains_nonlocal = is_global_var (var); + pt->vars_contains_escaped + = (cfun->gimple_df->escaped.anything + || bitmap_bit_p (cfun->gimple_df->escaped.vars, DECL_PT_UID (var))); +} + +/* Computes the union of the points-to solutions *DEST and *SRC and + stores the result in *DEST. This changes the points-to bitmap + of *DEST and thus may not be used if that might be shared. + The points-to bitmap of *SRC and *DEST will not be shared after + this function if they were not before. */ + +static void +pt_solution_ior_into (struct pt_solution *dest, struct pt_solution *src) +{ + dest->anything |= src->anything; + if (dest->anything) + { + pt_solution_reset (dest); + return; + } + + dest->nonlocal |= src->nonlocal; + dest->escaped |= src->escaped; + dest->ipa_escaped |= src->ipa_escaped; + dest->null |= src->null; + dest->vars_contains_nonlocal |= src->vars_contains_nonlocal; + dest->vars_contains_escaped |= src->vars_contains_escaped; + dest->vars_contains_escaped_heap |= src->vars_contains_escaped_heap; + if (!src->vars) + return; + + if (!dest->vars) + dest->vars = BITMAP_GGC_ALLOC (); + bitmap_ior_into (dest->vars, src->vars); +} + +/* Return true if the points-to solution *PT is empty. */ + +bool +pt_solution_empty_p (const pt_solution *pt) +{ + if (pt->anything + || pt->nonlocal) + return false; + + if (pt->vars + && !bitmap_empty_p (pt->vars)) + return false; + + /* If the solution includes ESCAPED, check if that is empty. */ + if (pt->escaped + && !pt_solution_empty_p (&cfun->gimple_df->escaped)) + return false; + + /* If the solution includes ESCAPED, check if that is empty. */ + if (pt->ipa_escaped + && !pt_solution_empty_p (&ipa_escaped_pt)) + return false; + + return true; +} + +/* Return true if the points-to solution *PT only point to a single var, and + return the var uid in *UID. */ + +bool +pt_solution_singleton_or_null_p (struct pt_solution *pt, unsigned *uid) +{ + if (pt->anything || pt->nonlocal || pt->escaped || pt->ipa_escaped + || pt->vars == NULL + || !bitmap_single_bit_set_p (pt->vars)) + return false; + + *uid = bitmap_first_set_bit (pt->vars); + return true; +} + +/* Return true if the points-to solution *PT includes global memory. */ + +bool +pt_solution_includes_global (struct pt_solution *pt) +{ + if (pt->anything + || pt->nonlocal + || pt->vars_contains_nonlocal + /* The following is a hack to make the malloc escape hack work. + In reality we'd need different sets for escaped-through-return + and escaped-to-callees and passes would need to be updated. */ + || pt->vars_contains_escaped_heap) + return true; + + /* 'escaped' is also a placeholder so we have to look into it. */ + if (pt->escaped) + return pt_solution_includes_global (&cfun->gimple_df->escaped); + + if (pt->ipa_escaped) + return pt_solution_includes_global (&ipa_escaped_pt); + + return false; +} + +/* Return true if the points-to solution *PT includes the variable + declaration DECL. */ + +static bool +pt_solution_includes_1 (struct pt_solution *pt, const_tree decl) +{ + if (pt->anything) + return true; + + if (pt->nonlocal + && is_global_var (decl)) + return true; + + if (pt->vars + && bitmap_bit_p (pt->vars, DECL_PT_UID (decl))) + return true; + + /* If the solution includes ESCAPED, check it. */ + if (pt->escaped + && pt_solution_includes_1 (&cfun->gimple_df->escaped, decl)) + return true; + + /* If the solution includes ESCAPED, check it. */ + if (pt->ipa_escaped + && pt_solution_includes_1 (&ipa_escaped_pt, decl)) + return true; + + return false; +} + +bool +pt_solution_includes (struct pt_solution *pt, const_tree decl) +{ + bool res = pt_solution_includes_1 (pt, decl); + if (res) + ++pta_stats.pt_solution_includes_may_alias; + else + ++pta_stats.pt_solution_includes_no_alias; + return res; +} + +/* Return true if both points-to solutions PT1 and PT2 have a non-empty + intersection. */ + +static bool +pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2) +{ + if (pt1->anything || pt2->anything) + return true; + + /* If either points to unknown global memory and the other points to + any global memory they alias. */ + if ((pt1->nonlocal + && (pt2->nonlocal + || pt2->vars_contains_nonlocal)) + || (pt2->nonlocal + && pt1->vars_contains_nonlocal)) + return true; + + /* If either points to all escaped memory and the other points to + any escaped memory they alias. */ + if ((pt1->escaped + && (pt2->escaped + || pt2->vars_contains_escaped)) + || (pt2->escaped + && pt1->vars_contains_escaped)) + return true; + + /* Check the escaped solution if required. + ??? Do we need to check the local against the IPA escaped sets? */ + if ((pt1->ipa_escaped || pt2->ipa_escaped) + && !pt_solution_empty_p (&ipa_escaped_pt)) + { + /* If both point to escaped memory and that solution + is not empty they alias. */ + if (pt1->ipa_escaped && pt2->ipa_escaped) + return true; + + /* If either points to escaped memory see if the escaped solution + intersects with the other. */ + if ((pt1->ipa_escaped + && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2)) + || (pt2->ipa_escaped + && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1))) + return true; + } + + /* Now both pointers alias if their points-to solution intersects. */ + return (pt1->vars + && pt2->vars + && bitmap_intersect_p (pt1->vars, pt2->vars)); +} + +bool +pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2) +{ + bool res = pt_solutions_intersect_1 (pt1, pt2); + if (res) + ++pta_stats.pt_solutions_intersect_may_alias; + else + ++pta_stats.pt_solutions_intersect_no_alias; + return res; +} + + +/* Dump points-to information to OUTFILE. */ + +static void +dump_sa_points_to_info (FILE *outfile) +{ + unsigned int i; + + fprintf (outfile, "\nPoints-to sets\n\n"); + + if (dump_flags & TDF_STATS) + { + fprintf (outfile, "Stats:\n"); + fprintf (outfile, "Total vars: %d\n", stats.total_vars); + fprintf (outfile, "Non-pointer vars: %d\n", + stats.nonpointer_vars); + fprintf (outfile, "Statically unified vars: %d\n", + stats.unified_vars_static); + fprintf (outfile, "Dynamically unified vars: %d\n", + stats.unified_vars_dynamic); + fprintf (outfile, "Iterations: %d\n", stats.iterations); + fprintf (outfile, "Number of edges: %d\n", stats.num_edges); + fprintf (outfile, "Number of implicit edges: %d\n", + stats.num_implicit_edges); + } + + for (i = 1; i < varmap.length (); i++) + { + varinfo_t vi = get_varinfo (i); + if (!vi->may_have_pointers) + continue; + dump_solution_for_var (outfile, i); + } +} + + +/* Debug points-to information to stderr. */ + +DEBUG_FUNCTION void +debug_sa_points_to_info (void) +{ + dump_sa_points_to_info (stderr); +} + + +/* Initialize the always-existing constraint variables for NULL + ANYTHING, READONLY, and INTEGER */ + +static void +init_base_vars (void) +{ + struct constraint_expr lhs, rhs; + varinfo_t var_anything; + varinfo_t var_nothing; + varinfo_t var_string; + varinfo_t var_escaped; + varinfo_t var_nonlocal; + varinfo_t var_storedanything; + varinfo_t var_integer; + + /* Variable ID zero is reserved and should be NULL. */ + varmap.safe_push (NULL); + + /* Create the NULL variable, used to represent that a variable points + to NULL. */ + var_nothing = new_var_info (NULL_TREE, "NULL", false); + gcc_assert (var_nothing->id == nothing_id); + var_nothing->is_artificial_var = 1; + var_nothing->offset = 0; + var_nothing->size = ~0; + var_nothing->fullsize = ~0; + var_nothing->is_special_var = 1; + var_nothing->may_have_pointers = 0; + var_nothing->is_global_var = 0; + + /* Create the ANYTHING variable, used to represent that a variable + points to some unknown piece of memory. */ + var_anything = new_var_info (NULL_TREE, "ANYTHING", false); + gcc_assert (var_anything->id == anything_id); + var_anything->is_artificial_var = 1; + var_anything->size = ~0; + var_anything->offset = 0; + var_anything->fullsize = ~0; + var_anything->is_special_var = 1; + + /* Anything points to anything. This makes deref constraints just + work in the presence of linked list and other p = *p type loops, + by saying that *ANYTHING = ANYTHING. */ + lhs.type = SCALAR; + lhs.var = anything_id; + lhs.offset = 0; + rhs.type = ADDRESSOF; + rhs.var = anything_id; + rhs.offset = 0; + + /* This specifically does not use process_constraint because + process_constraint ignores all anything = anything constraints, since all + but this one are redundant. */ + constraints.safe_push (new_constraint (lhs, rhs)); + + /* Create the STRING variable, used to represent that a variable + points to a string literal. String literals don't contain + pointers so STRING doesn't point to anything. */ + var_string = new_var_info (NULL_TREE, "STRING", false); + gcc_assert (var_string->id == string_id); + var_string->is_artificial_var = 1; + var_string->offset = 0; + var_string->size = ~0; + var_string->fullsize = ~0; + var_string->is_special_var = 1; + var_string->may_have_pointers = 0; + + /* Create the ESCAPED variable, used to represent the set of escaped + memory. */ + var_escaped = new_var_info (NULL_TREE, "ESCAPED", false); + gcc_assert (var_escaped->id == escaped_id); + var_escaped->is_artificial_var = 1; + var_escaped->offset = 0; + var_escaped->size = ~0; + var_escaped->fullsize = ~0; + var_escaped->is_special_var = 0; + + /* Create the NONLOCAL variable, used to represent the set of nonlocal + memory. */ + var_nonlocal = new_var_info (NULL_TREE, "NONLOCAL", false); + gcc_assert (var_nonlocal->id == nonlocal_id); + var_nonlocal->is_artificial_var = 1; + var_nonlocal->offset = 0; + var_nonlocal->size = ~0; + var_nonlocal->fullsize = ~0; + var_nonlocal->is_special_var = 1; + + /* ESCAPED = *ESCAPED, because escaped is may-deref'd at calls, etc. */ + lhs.type = SCALAR; + lhs.var = escaped_id; + lhs.offset = 0; + rhs.type = DEREF; + rhs.var = escaped_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + + /* ESCAPED = ESCAPED + UNKNOWN_OFFSET, because if a sub-field escapes the + whole variable escapes. */ + lhs.type = SCALAR; + lhs.var = escaped_id; + lhs.offset = 0; + rhs.type = SCALAR; + rhs.var = escaped_id; + rhs.offset = UNKNOWN_OFFSET; + process_constraint (new_constraint (lhs, rhs)); + + /* *ESCAPED = NONLOCAL. This is true because we have to assume + everything pointed to by escaped points to what global memory can + point to. */ + lhs.type = DEREF; + lhs.var = escaped_id; + lhs.offset = 0; + rhs.type = SCALAR; + rhs.var = nonlocal_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + + /* NONLOCAL = &NONLOCAL, NONLOCAL = &ESCAPED. This is true because + global memory may point to global memory and escaped memory. */ + lhs.type = SCALAR; + lhs.var = nonlocal_id; + lhs.offset = 0; + rhs.type = ADDRESSOF; + rhs.var = nonlocal_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + rhs.type = ADDRESSOF; + rhs.var = escaped_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); + + /* Create the STOREDANYTHING variable, used to represent the set of + variables stored to *ANYTHING. */ + var_storedanything = new_var_info (NULL_TREE, "STOREDANYTHING", false); + gcc_assert (var_storedanything->id == storedanything_id); + var_storedanything->is_artificial_var = 1; + var_storedanything->offset = 0; + var_storedanything->size = ~0; + var_storedanything->fullsize = ~0; + var_storedanything->is_special_var = 0; + + /* Create the INTEGER variable, used to represent that a variable points + to what an INTEGER "points to". */ + var_integer = new_var_info (NULL_TREE, "INTEGER", false); + gcc_assert (var_integer->id == integer_id); + var_integer->is_artificial_var = 1; + var_integer->size = ~0; + var_integer->fullsize = ~0; + var_integer->offset = 0; + var_integer->is_special_var = 1; + + /* INTEGER = ANYTHING, because we don't know where a dereference of + a random integer will point to. */ + lhs.type = SCALAR; + lhs.var = integer_id; + lhs.offset = 0; + rhs.type = ADDRESSOF; + rhs.var = anything_id; + rhs.offset = 0; + process_constraint (new_constraint (lhs, rhs)); +} + +/* Initialize things necessary to perform PTA */ + +static void +init_alias_vars (void) +{ + use_field_sensitive = (param_max_fields_for_field_sensitive > 1); + + bitmap_obstack_initialize (&pta_obstack); + bitmap_obstack_initialize (&oldpta_obstack); + bitmap_obstack_initialize (&predbitmap_obstack); + + constraints.create (8); + varmap.create (8); + vi_for_tree = new hash_map<tree, varinfo_t>; + call_stmt_vars = new hash_map<gimple *, varinfo_t>; + + memset (&stats, 0, sizeof (stats)); + shared_bitmap_table = new hash_table<shared_bitmap_hasher> (511); + init_base_vars (); + + gcc_obstack_init (&fake_var_decl_obstack); + + final_solutions = new hash_map<varinfo_t, pt_solution *>; + gcc_obstack_init (&final_solutions_obstack); +} + +/* Remove the REF and ADDRESS edges from GRAPH, as well as all the + predecessor edges. */ + +static void +remove_preds_and_fake_succs (constraint_graph_t graph) +{ + unsigned int i; + + /* Clear the implicit ref and address nodes from the successor + lists. */ + for (i = 1; i < FIRST_REF_NODE; i++) + { + if (graph->succs[i]) + bitmap_clear_range (graph->succs[i], FIRST_REF_NODE, + FIRST_REF_NODE * 2); + } + + /* Free the successor list for the non-ref nodes. */ + for (i = FIRST_REF_NODE + 1; i < graph->size; i++) + { + if (graph->succs[i]) + BITMAP_FREE (graph->succs[i]); + } + + /* Now reallocate the size of the successor list as, and blow away + the predecessor bitmaps. */ + graph->size = varmap.length (); + graph->succs = XRESIZEVEC (bitmap, graph->succs, graph->size); + + free (graph->implicit_preds); + graph->implicit_preds = NULL; + free (graph->preds); + graph->preds = NULL; + bitmap_obstack_release (&predbitmap_obstack); +} + +/* Solve the constraint set. */ + +static void +solve_constraints (void) +{ + class scc_info *si; + + /* Sort varinfos so that ones that cannot be pointed to are last. + This makes bitmaps more efficient. */ + unsigned int *map = XNEWVEC (unsigned int, varmap.length ()); + for (unsigned i = 0; i < integer_id + 1; ++i) + map[i] = i; + /* Start with address-taken vars, followed by not address-taken vars + to move vars never appearing in the points-to solution bitmaps last. */ + unsigned j = integer_id + 1; + for (unsigned i = integer_id + 1; i < varmap.length (); ++i) + if (varmap[varmap[i]->head]->address_taken) + map[i] = j++; + for (unsigned i = integer_id + 1; i < varmap.length (); ++i) + if (! varmap[varmap[i]->head]->address_taken) + map[i] = j++; + /* Shuffle varmap according to map. */ + for (unsigned i = integer_id + 1; i < varmap.length (); ++i) + { + while (map[varmap[i]->id] != i) + std::swap (varmap[i], varmap[map[varmap[i]->id]]); + gcc_assert (bitmap_empty_p (varmap[i]->solution)); + varmap[i]->id = i; + varmap[i]->next = map[varmap[i]->next]; + varmap[i]->head = map[varmap[i]->head]; + } + /* Finally rewrite constraints. */ + for (unsigned i = 0; i < constraints.length (); ++i) + { + constraints[i]->lhs.var = map[constraints[i]->lhs.var]; + constraints[i]->rhs.var = map[constraints[i]->rhs.var]; + } + free (map); + + if (dump_file) + fprintf (dump_file, + "\nCollapsing static cycles and doing variable " + "substitution\n"); + + init_graph (varmap.length () * 2); + + if (dump_file) + fprintf (dump_file, "Building predecessor graph\n"); + build_pred_graph (); + + if (dump_file) + fprintf (dump_file, "Detecting pointer and location " + "equivalences\n"); + si = perform_var_substitution (graph); + + if (dump_file) + fprintf (dump_file, "Rewriting constraints and unifying " + "variables\n"); + rewrite_constraints (graph, si); + + build_succ_graph (); + + free_var_substitution_info (si); + + /* Attach complex constraints to graph nodes. */ + move_complex_constraints (graph); + + if (dump_file) + fprintf (dump_file, "Uniting pointer but not location equivalent " + "variables\n"); + unite_pointer_equivalences (graph); + + if (dump_file) + fprintf (dump_file, "Finding indirect cycles\n"); + find_indirect_cycles (graph); + + /* Implicit nodes and predecessors are no longer necessary at this + point. */ + remove_preds_and_fake_succs (graph); + + if (dump_file && (dump_flags & TDF_GRAPH)) + { + fprintf (dump_file, "\n\n// The constraint graph before solve-graph " + "in dot format:\n"); + dump_constraint_graph (dump_file); + fprintf (dump_file, "\n\n"); + } + + if (dump_file) + fprintf (dump_file, "Solving graph\n"); + + solve_graph (graph); + + if (dump_file && (dump_flags & TDF_GRAPH)) + { + fprintf (dump_file, "\n\n// The constraint graph after solve-graph " + "in dot format:\n"); + dump_constraint_graph (dump_file); + fprintf (dump_file, "\n\n"); + } +} + +/* Create points-to sets for the current function. See the comments + at the start of the file for an algorithmic overview. */ + +static void +compute_points_to_sets (void) +{ + basic_block bb; + varinfo_t vi; + + timevar_push (TV_TREE_PTA); + + init_alias_vars (); + + intra_create_variable_infos (cfun); + + /* Now walk all statements and build the constraint set. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gphi *phi = gsi.phi (); + + if (! virtual_operand_p (gimple_phi_result (phi))) + find_func_aliases (cfun, phi); + } + + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + find_func_aliases (cfun, stmt); + } + } + + if (dump_file) + { + fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n"); + dump_constraints (dump_file, 0); + } + + /* From the constraints compute the points-to sets. */ + solve_constraints (); + + /* Post-process solutions for escapes through returns. */ + edge_iterator ei; + edge e; + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + if (greturn *ret = safe_dyn_cast <greturn *> (last_stmt (e->src))) + { + tree val = gimple_return_retval (ret); + /* ??? Easy to handle simple indirections with some work. + Arbitrary references like foo.bar.baz are more difficult + (but conservatively easy enough with just looking at the base). + Mind to fixup find_func_aliases as well. */ + if (!val || !SSA_VAR_P (val)) + continue; + /* returns happen last in non-IPA so they only influence + the ESCAPED solution and we can filter local variables. */ + varinfo_t escaped_vi = get_varinfo (find (escaped_id)); + varinfo_t vi = lookup_vi_for_tree (val); + bitmap delta = BITMAP_ALLOC (&pta_obstack); + bitmap_iterator bi; + unsigned i; + for (; vi; vi = vi_next (vi)) + { + varinfo_t part_vi = get_varinfo (find (vi->id)); + EXECUTE_IF_AND_COMPL_IN_BITMAP (part_vi->solution, + escaped_vi->solution, 0, i, bi) + { + varinfo_t pointed_to_vi = get_varinfo (i); + if (pointed_to_vi->is_global_var + /* We delay marking of heap memory as global. */ + || pointed_to_vi->is_heap_var) + bitmap_set_bit (delta, i); + } + } + + /* Now compute the transitive closure. */ + bitmap_ior_into (escaped_vi->solution, delta); + bitmap new_delta = BITMAP_ALLOC (&pta_obstack); + while (!bitmap_empty_p (delta)) + { + EXECUTE_IF_SET_IN_BITMAP (delta, 0, i, bi) + { + varinfo_t pointed_to_vi = get_varinfo (i); + pointed_to_vi = get_varinfo (find (pointed_to_vi->id)); + unsigned j; + bitmap_iterator bi2; + EXECUTE_IF_AND_COMPL_IN_BITMAP (pointed_to_vi->solution, + escaped_vi->solution, + 0, j, bi2) + { + varinfo_t pointed_to_vi2 = get_varinfo (j); + if (pointed_to_vi2->is_global_var + /* We delay marking of heap memory as global. */ + || pointed_to_vi2->is_heap_var) + bitmap_set_bit (new_delta, j); + } + } + bitmap_ior_into (escaped_vi->solution, new_delta); + bitmap_clear (delta); + std::swap (delta, new_delta); + } + BITMAP_FREE (delta); + BITMAP_FREE (new_delta); + } + + if (dump_file) + dump_sa_points_to_info (dump_file); + + /* Compute the points-to set for ESCAPED used for call-clobber analysis. */ + cfun->gimple_df->escaped = find_what_var_points_to (cfun->decl, + get_varinfo (escaped_id)); + + /* Make sure the ESCAPED solution (which is used as placeholder in + other solutions) does not reference itself. This simplifies + points-to solution queries. */ + cfun->gimple_df->escaped.escaped = 0; + + /* Compute the points-to sets for pointer SSA_NAMEs. */ + unsigned i; + tree ptr; + + FOR_EACH_SSA_NAME (i, ptr, cfun) + { + if (POINTER_TYPE_P (TREE_TYPE (ptr))) + find_what_p_points_to (cfun->decl, ptr); + } + + /* Compute the call-used/clobbered sets. */ + FOR_EACH_BB_FN (bb, cfun) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gcall *stmt; + struct pt_solution *pt; + + stmt = dyn_cast <gcall *> (gsi_stmt (gsi)); + if (!stmt) + continue; + + pt = gimple_call_use_set (stmt); + if (gimple_call_flags (stmt) & ECF_CONST) + memset (pt, 0, sizeof (struct pt_solution)); + else + { + bool uses_global_memory = true; + bool reads_global_memory = true; + + determine_global_memory_access (stmt, NULL, + &reads_global_memory, + &uses_global_memory); + if ((vi = lookup_call_use_vi (stmt)) != NULL) + { + *pt = find_what_var_points_to (cfun->decl, vi); + /* Escaped (and thus nonlocal) variables are always + implicitly used by calls. */ + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + if (uses_global_memory) + { + pt->nonlocal = 1; + pt->escaped = 1; + } + } + else if (uses_global_memory) + { + /* If there is nothing special about this call then + we have made everything that is used also escape. */ + *pt = cfun->gimple_df->escaped; + pt->nonlocal = 1; + } + else + memset (pt, 0, sizeof (struct pt_solution)); + } + + pt = gimple_call_clobber_set (stmt); + if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS)) + memset (pt, 0, sizeof (struct pt_solution)); + else + { + bool writes_global_memory = true; + + determine_global_memory_access (stmt, &writes_global_memory, + NULL, NULL); + + if ((vi = lookup_call_clobber_vi (stmt)) != NULL) + { + *pt = find_what_var_points_to (cfun->decl, vi); + /* Escaped (and thus nonlocal) variables are always + implicitly clobbered by calls. */ + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + if (writes_global_memory) + { + pt->nonlocal = 1; + pt->escaped = 1; + } + } + else if (writes_global_memory) + { + /* If there is nothing special about this call then + we have made everything that is used also escape. */ + *pt = cfun->gimple_df->escaped; + pt->nonlocal = 1; + } + else + memset (pt, 0, sizeof (struct pt_solution)); + } + } + } + + timevar_pop (TV_TREE_PTA); +} + + +/* Delete created points-to sets. */ + +static void +delete_points_to_sets (void) +{ + unsigned int i; + + delete shared_bitmap_table; + shared_bitmap_table = NULL; + if (dump_file && (dump_flags & TDF_STATS)) + fprintf (dump_file, "Points to sets created:%d\n", + stats.points_to_sets_created); + + delete vi_for_tree; + delete call_stmt_vars; + bitmap_obstack_release (&pta_obstack); + constraints.release (); + + for (i = 0; i < graph->size; i++) + graph->complex[i].release (); + free (graph->complex); + + free (graph->rep); + free (graph->succs); + free (graph->pe); + free (graph->pe_rep); + free (graph->indirect_cycles); + free (graph); + + varmap.release (); + variable_info_pool.release (); + constraint_pool.release (); + + obstack_free (&fake_var_decl_obstack, NULL); + + delete final_solutions; + obstack_free (&final_solutions_obstack, NULL); +} + +struct vls_data +{ + unsigned short clique; + bool escaped_p; + bitmap rvars; +}; + +/* Mark "other" loads and stores as belonging to CLIQUE and with + base zero. */ + +static bool +visit_loadstore (gimple *, tree base, tree ref, void *data) +{ + unsigned short clique = ((vls_data *) data)->clique; + bitmap rvars = ((vls_data *) data)->rvars; + bool escaped_p = ((vls_data *) data)->escaped_p; + if (TREE_CODE (base) == MEM_REF + || TREE_CODE (base) == TARGET_MEM_REF) + { + tree ptr = TREE_OPERAND (base, 0); + if (TREE_CODE (ptr) == SSA_NAME) + { + /* For parameters, get at the points-to set for the actual parm + decl. */ + if (SSA_NAME_IS_DEFAULT_DEF (ptr) + && (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL + || TREE_CODE (SSA_NAME_VAR (ptr)) == RESULT_DECL)) + ptr = SSA_NAME_VAR (ptr); + + /* We need to make sure 'ptr' doesn't include any of + the restrict tags we added bases for in its points-to set. */ + varinfo_t vi = lookup_vi_for_tree (ptr); + if (! vi) + return false; + + vi = get_varinfo (find (vi->id)); + if (bitmap_intersect_p (rvars, vi->solution) + || (escaped_p && bitmap_bit_p (vi->solution, escaped_id))) + return false; + } + + /* Do not overwrite existing cliques (that includes clique, base + pairs we just set). */ + if (MR_DEPENDENCE_CLIQUE (base) == 0) + { + MR_DEPENDENCE_CLIQUE (base) = clique; + MR_DEPENDENCE_BASE (base) = 0; + } + } + + /* For plain decl accesses see whether they are accesses to globals + and rewrite them to MEM_REFs with { clique, 0 }. */ + if (VAR_P (base) + && is_global_var (base) + /* ??? We can't rewrite a plain decl with the walk_stmt_load_store + ops callback. */ + && base != ref) + { + tree *basep = &ref; + while (handled_component_p (*basep)) + basep = &TREE_OPERAND (*basep, 0); + gcc_assert (VAR_P (*basep)); + tree ptr = build_fold_addr_expr (*basep); + tree zero = build_int_cst (TREE_TYPE (ptr), 0); + *basep = build2 (MEM_REF, TREE_TYPE (*basep), ptr, zero); + MR_DEPENDENCE_CLIQUE (*basep) = clique; + MR_DEPENDENCE_BASE (*basep) = 0; + } + + return false; +} + +struct msdi_data { + tree ptr; + unsigned short *clique; + unsigned short *last_ruid; + varinfo_t restrict_var; +}; + +/* If BASE is a MEM_REF then assign a clique, base pair to it, updating + CLIQUE, *RESTRICT_VAR and LAST_RUID as passed via DATA. + Return whether dependence info was assigned to BASE. */ + +static bool +maybe_set_dependence_info (gimple *, tree base, tree, void *data) +{ + tree ptr = ((msdi_data *)data)->ptr; + unsigned short &clique = *((msdi_data *)data)->clique; + unsigned short &last_ruid = *((msdi_data *)data)->last_ruid; + varinfo_t restrict_var = ((msdi_data *)data)->restrict_var; + if ((TREE_CODE (base) == MEM_REF + || TREE_CODE (base) == TARGET_MEM_REF) + && TREE_OPERAND (base, 0) == ptr) + { + /* Do not overwrite existing cliques. This avoids overwriting dependence + info inlined from a function with restrict parameters inlined + into a function with restrict parameters. This usually means we + prefer to be precise in innermost loops. */ + if (MR_DEPENDENCE_CLIQUE (base) == 0) + { + if (clique == 0) + { + if (cfun->last_clique == 0) + cfun->last_clique = 1; + clique = 1; + } + if (restrict_var->ruid == 0) + restrict_var->ruid = ++last_ruid; + MR_DEPENDENCE_CLIQUE (base) = clique; + MR_DEPENDENCE_BASE (base) = restrict_var->ruid; + return true; + } + } + return false; +} + +/* Clear dependence info for the clique DATA. */ + +static bool +clear_dependence_clique (gimple *, tree base, tree, void *data) +{ + unsigned short clique = (uintptr_t)data; + if ((TREE_CODE (base) == MEM_REF + || TREE_CODE (base) == TARGET_MEM_REF) + && MR_DEPENDENCE_CLIQUE (base) == clique) + { + MR_DEPENDENCE_CLIQUE (base) = 0; + MR_DEPENDENCE_BASE (base) = 0; + } + + return false; +} + +/* Compute the set of independend memory references based on restrict + tags and their conservative propagation to the points-to sets. */ + +static void +compute_dependence_clique (void) +{ + /* First clear the special "local" clique. */ + basic_block bb; + if (cfun->last_clique != 0) + FOR_EACH_BB_FN (bb, cfun) + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + walk_stmt_load_store_ops (stmt, (void *)(uintptr_t) 1, + clear_dependence_clique, + clear_dependence_clique); + } + + unsigned short clique = 0; + unsigned short last_ruid = 0; + bitmap rvars = BITMAP_ALLOC (NULL); + bool escaped_p = false; + for (unsigned i = 0; i < num_ssa_names; ++i) + { + tree ptr = ssa_name (i); + if (!ptr || !POINTER_TYPE_P (TREE_TYPE (ptr))) + continue; + + /* Avoid all this when ptr is not dereferenced? */ + tree p = ptr; + if (SSA_NAME_IS_DEFAULT_DEF (ptr) + && (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL + || TREE_CODE (SSA_NAME_VAR (ptr)) == RESULT_DECL)) + p = SSA_NAME_VAR (ptr); + varinfo_t vi = lookup_vi_for_tree (p); + if (!vi) + continue; + vi = get_varinfo (find (vi->id)); + bitmap_iterator bi; + unsigned j; + varinfo_t restrict_var = NULL; + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, j, bi) + { + varinfo_t oi = get_varinfo (j); + if (oi->head != j) + oi = get_varinfo (oi->head); + if (oi->is_restrict_var) + { + if (restrict_var + && restrict_var != oi) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "found restrict pointed-to " + "for "); + print_generic_expr (dump_file, ptr); + fprintf (dump_file, " but not exclusively\n"); + } + restrict_var = NULL; + break; + } + restrict_var = oi; + } + /* NULL is the only other valid points-to entry. */ + else if (oi->id != nothing_id) + { + restrict_var = NULL; + break; + } + } + /* Ok, found that ptr must(!) point to a single(!) restrict + variable. */ + /* ??? PTA isn't really a proper propagation engine to compute + this property. + ??? We could handle merging of two restricts by unifying them. */ + if (restrict_var) + { + /* Now look at possible dereferences of ptr. */ + imm_use_iterator ui; + gimple *use_stmt; + bool used = false; + msdi_data data = { ptr, &clique, &last_ruid, restrict_var }; + FOR_EACH_IMM_USE_STMT (use_stmt, ui, ptr) + used |= walk_stmt_load_store_ops (use_stmt, &data, + maybe_set_dependence_info, + maybe_set_dependence_info); + if (used) + { + /* Add all subvars to the set of restrict pointed-to set. */ + for (unsigned sv = restrict_var->head; sv != 0; + sv = get_varinfo (sv)->next) + bitmap_set_bit (rvars, sv); + varinfo_t escaped = get_varinfo (find (escaped_id)); + if (bitmap_bit_p (escaped->solution, restrict_var->id)) + escaped_p = true; + } + } + } + + if (clique != 0) + { + /* Assign the BASE id zero to all accesses not based on a restrict + pointer. That way they get disambiguated against restrict + accesses but not against each other. */ + /* ??? For restricts derived from globals (thus not incoming + parameters) we can't restrict scoping properly thus the following + is too aggressive there. For now we have excluded those globals from + getting into the MR_DEPENDENCE machinery. */ + vls_data data = { clique, escaped_p, rvars }; + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + walk_stmt_load_store_ops (stmt, &data, + visit_loadstore, visit_loadstore); + } + } + + BITMAP_FREE (rvars); +} + +/* Compute points-to information for every SSA_NAME pointer in the + current function and compute the transitive closure of escaped + variables to re-initialize the call-clobber states of local variables. */ + +unsigned int +compute_may_aliases (void) +{ + if (cfun->gimple_df->ipa_pta) + { + if (dump_file) + { + fprintf (dump_file, "\nNot re-computing points-to information " + "because IPA points-to information is available.\n\n"); + + /* But still dump what we have remaining it. */ + dump_alias_info (dump_file); + } + + return 0; + } + + /* For each pointer P_i, determine the sets of variables that P_i may + point-to. Compute the reachability set of escaped and call-used + variables. */ + compute_points_to_sets (); + + /* Debugging dumps. */ + if (dump_file) + dump_alias_info (dump_file); + + /* Compute restrict-based memory disambiguations. */ + compute_dependence_clique (); + + /* Deallocate memory used by aliasing data structures and the internal + points-to solution. */ + delete_points_to_sets (); + + gcc_assert (!need_ssa_update_p (cfun)); + + return 0; +} + +/* A dummy pass to cause points-to information to be computed via + TODO_rebuild_alias. */ + +namespace { + +const pass_data pass_data_build_alias = +{ + GIMPLE_PASS, /* type */ + "alias", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_rebuild_alias, /* todo_flags_finish */ +}; + +class pass_build_alias : public gimple_opt_pass +{ +public: + pass_build_alias (gcc::context *ctxt) + : gimple_opt_pass (pass_data_build_alias, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return flag_tree_pta; } + +}; // class pass_build_alias + +} // anon namespace + +gimple_opt_pass * +make_pass_build_alias (gcc::context *ctxt) +{ + return new pass_build_alias (ctxt); +} + +/* A dummy pass to cause points-to information to be computed via + TODO_rebuild_alias. */ + +namespace { + +const pass_data pass_data_build_ealias = +{ + GIMPLE_PASS, /* type */ + "ealias", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_rebuild_alias, /* todo_flags_finish */ +}; + +class pass_build_ealias : public gimple_opt_pass +{ +public: + pass_build_ealias (gcc::context *ctxt) + : gimple_opt_pass (pass_data_build_ealias, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return flag_tree_pta; } + +}; // class pass_build_ealias + +} // anon namespace + +gimple_opt_pass * +make_pass_build_ealias (gcc::context *ctxt) +{ + return new pass_build_ealias (ctxt); +} + + +/* IPA PTA solutions for ESCAPED. */ +struct pt_solution ipa_escaped_pt + = { true, false, false, false, false, + false, false, false, false, false, NULL }; + +/* Associate node with varinfo DATA. Worker for + cgraph_for_symbol_thunks_and_aliases. */ +static bool +associate_varinfo_to_alias (struct cgraph_node *node, void *data) +{ + if ((node->alias + || (node->thunk + && ! node->inlined_to)) + && node->analyzed + && !node->ifunc_resolver) + insert_vi_for_tree (node->decl, (varinfo_t)data); + return false; +} + +/* Dump varinfo VI to FILE. */ + +static void +dump_varinfo (FILE *file, varinfo_t vi) +{ + if (vi == NULL) + return; + + fprintf (file, "%u: %s\n", vi->id, vi->name); + + const char *sep = " "; + if (vi->is_artificial_var) + fprintf (file, "%sartificial", sep); + if (vi->is_special_var) + fprintf (file, "%sspecial", sep); + if (vi->is_unknown_size_var) + fprintf (file, "%sunknown-size", sep); + if (vi->is_full_var) + fprintf (file, "%sfull", sep); + if (vi->is_heap_var) + fprintf (file, "%sheap", sep); + if (vi->may_have_pointers) + fprintf (file, "%smay-have-pointers", sep); + if (vi->only_restrict_pointers) + fprintf (file, "%sonly-restrict-pointers", sep); + if (vi->is_restrict_var) + fprintf (file, "%sis-restrict-var", sep); + if (vi->is_global_var) + fprintf (file, "%sglobal", sep); + if (vi->is_ipa_escape_point) + fprintf (file, "%sipa-escape-point", sep); + if (vi->is_fn_info) + fprintf (file, "%sfn-info", sep); + if (vi->ruid) + fprintf (file, "%srestrict-uid:%u", sep, vi->ruid); + if (vi->next) + fprintf (file, "%snext:%u", sep, vi->next); + if (vi->head != vi->id) + fprintf (file, "%shead:%u", sep, vi->head); + if (vi->offset) + fprintf (file, "%soffset:" HOST_WIDE_INT_PRINT_DEC, sep, vi->offset); + if (vi->size != ~(unsigned HOST_WIDE_INT)0) + fprintf (file, "%ssize:" HOST_WIDE_INT_PRINT_DEC, sep, vi->size); + if (vi->fullsize != ~(unsigned HOST_WIDE_INT)0 + && vi->fullsize != vi->size) + fprintf (file, "%sfullsize:" HOST_WIDE_INT_PRINT_DEC, sep, + vi->fullsize); + fprintf (file, "\n"); + + if (vi->solution && !bitmap_empty_p (vi->solution)) + { + bitmap_iterator bi; + unsigned i; + fprintf (file, " solution: {"); + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi) + fprintf (file, " %u", i); + fprintf (file, " }\n"); + } + + if (vi->oldsolution && !bitmap_empty_p (vi->oldsolution) + && !bitmap_equal_p (vi->solution, vi->oldsolution)) + { + bitmap_iterator bi; + unsigned i; + fprintf (file, " oldsolution: {"); + EXECUTE_IF_SET_IN_BITMAP (vi->oldsolution, 0, i, bi) + fprintf (file, " %u", i); + fprintf (file, " }\n"); + } +} + +/* Dump varinfo VI to stderr. */ + +DEBUG_FUNCTION void +debug_varinfo (varinfo_t vi) +{ + dump_varinfo (stderr, vi); +} + +/* Dump varmap to FILE. */ + +static void +dump_varmap (FILE *file) +{ + if (varmap.length () == 0) + return; + + fprintf (file, "variables:\n"); + + for (unsigned int i = 0; i < varmap.length (); ++i) + { + varinfo_t vi = get_varinfo (i); + dump_varinfo (file, vi); + } + + fprintf (file, "\n"); +} + +/* Dump varmap to stderr. */ + +DEBUG_FUNCTION void +debug_varmap (void) +{ + dump_varmap (stderr); +} + +/* Compute whether node is refered to non-locally. Worker for + cgraph_for_symbol_thunks_and_aliases. */ +static bool +refered_from_nonlocal_fn (struct cgraph_node *node, void *data) +{ + bool *nonlocal_p = (bool *)data; + *nonlocal_p |= (node->used_from_other_partition + || DECL_EXTERNAL (node->decl) + || TREE_PUBLIC (node->decl) + || node->force_output + || lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))); + return false; +} + +/* Same for varpool nodes. */ +static bool +refered_from_nonlocal_var (struct varpool_node *node, void *data) +{ + bool *nonlocal_p = (bool *)data; + *nonlocal_p |= (node->used_from_other_partition + || DECL_EXTERNAL (node->decl) + || TREE_PUBLIC (node->decl) + || node->force_output); + return false; +} + +/* Execute the driver for IPA PTA. */ +static unsigned int +ipa_pta_execute (void) +{ + struct cgraph_node *node; + varpool_node *var; + unsigned int from = 0; + + in_ipa_mode = 1; + + init_alias_vars (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + symtab->dump (dump_file); + fprintf (dump_file, "\n"); + } + + if (dump_file) + { + fprintf (dump_file, "Generating generic constraints\n\n"); + dump_constraints (dump_file, from); + fprintf (dump_file, "\n"); + from = constraints.length (); + } + + /* Build the constraints. */ + FOR_EACH_DEFINED_FUNCTION (node) + { + varinfo_t vi; + /* Nodes without a body in this partition are not interesting. + Especially do not visit clones at this point for now - we + get duplicate decls there for inline clones at least. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->inlined_to) + continue; + node->get_body (); + + gcc_assert (!node->clone_of); + + /* For externally visible or attribute used annotated functions use + local constraints for their arguments. + For local functions we see all callers and thus do not need initial + constraints for parameters. */ + bool nonlocal_p = (node->used_from_other_partition + || DECL_EXTERNAL (node->decl) + || TREE_PUBLIC (node->decl) + || node->force_output + || lookup_attribute ("noipa", + DECL_ATTRIBUTES (node->decl))); + node->call_for_symbol_thunks_and_aliases (refered_from_nonlocal_fn, + &nonlocal_p, true); + + vi = create_function_info_for (node->decl, + alias_get_name (node->decl), false, + nonlocal_p); + if (dump_file + && from != constraints.length ()) + { + fprintf (dump_file, + "Generating initial constraints for %s", + node->dump_name ()); + if (DECL_ASSEMBLER_NAME_SET_P (node->decl)) + fprintf (dump_file, " (%s)", + IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (node->decl))); + fprintf (dump_file, "\n\n"); + dump_constraints (dump_file, from); + fprintf (dump_file, "\n"); + + from = constraints.length (); + } + + node->call_for_symbol_thunks_and_aliases + (associate_varinfo_to_alias, vi, true); + } + + /* Create constraints for global variables and their initializers. */ + FOR_EACH_VARIABLE (var) + { + if (var->alias && var->analyzed) + continue; + + varinfo_t vi = get_vi_for_tree (var->decl); + + /* For the purpose of IPA PTA unit-local globals are not + escape points. */ + bool nonlocal_p = (DECL_EXTERNAL (var->decl) + || TREE_PUBLIC (var->decl) + || var->used_from_other_partition + || var->force_output); + var->call_for_symbol_and_aliases (refered_from_nonlocal_var, + &nonlocal_p, true); + if (nonlocal_p) + vi->is_ipa_escape_point = true; + } + + if (dump_file + && from != constraints.length ()) + { + fprintf (dump_file, + "Generating constraints for global initializers\n\n"); + dump_constraints (dump_file, from); + fprintf (dump_file, "\n"); + from = constraints.length (); + } + + FOR_EACH_DEFINED_FUNCTION (node) + { + struct function *func; + basic_block bb; + + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) + continue; + + if (dump_file) + { + fprintf (dump_file, + "Generating constraints for %s", node->dump_name ()); + if (DECL_ASSEMBLER_NAME_SET_P (node->decl)) + fprintf (dump_file, " (%s)", + IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (node->decl))); + fprintf (dump_file, "\n"); + } + + func = DECL_STRUCT_FUNCTION (node->decl); + gcc_assert (cfun == NULL); + + /* Build constriants for the function body. */ + FOR_EACH_BB_FN (bb, func) + { + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gphi *phi = gsi.phi (); + + if (! virtual_operand_p (gimple_phi_result (phi))) + find_func_aliases (func, phi); + } + + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + find_func_aliases (func, stmt); + find_func_clobbers (func, stmt); + } + } + + if (dump_file) + { + fprintf (dump_file, "\n"); + dump_constraints (dump_file, from); + fprintf (dump_file, "\n"); + from = constraints.length (); + } + } + + /* From the constraints compute the points-to sets. */ + solve_constraints (); + + if (dump_file) + dump_sa_points_to_info (dump_file); + + /* Now post-process solutions to handle locals from different + runtime instantiations coming in through recursive invocations. */ + unsigned shadow_var_cnt = 0; + for (unsigned i = 1; i < varmap.length (); ++i) + { + varinfo_t fi = get_varinfo (i); + if (fi->is_fn_info + && fi->decl) + /* Automatic variables pointed to by their containing functions + parameters need this treatment. */ + for (varinfo_t ai = first_vi_for_offset (fi, fi_parm_base); + ai; ai = vi_next (ai)) + { + varinfo_t vi = get_varinfo (find (ai->id)); + bitmap_iterator bi; + unsigned j; + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, j, bi) + { + varinfo_t pt = get_varinfo (j); + if (pt->shadow_var_uid == 0 + && pt->decl + && auto_var_in_fn_p (pt->decl, fi->decl)) + { + pt->shadow_var_uid = allocate_decl_uid (); + shadow_var_cnt++; + } + } + } + /* As well as global variables which are another way of passing + arguments to recursive invocations. */ + else if (fi->is_global_var) + { + for (varinfo_t ai = fi; ai; ai = vi_next (ai)) + { + varinfo_t vi = get_varinfo (find (ai->id)); + bitmap_iterator bi; + unsigned j; + EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, j, bi) + { + varinfo_t pt = get_varinfo (j); + if (pt->shadow_var_uid == 0 + && pt->decl + && auto_var_p (pt->decl)) + { + pt->shadow_var_uid = allocate_decl_uid (); + shadow_var_cnt++; + } + } + } + } + } + if (shadow_var_cnt && dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Allocated %u shadow variables for locals " + "maybe leaking into recursive invocations of their containing " + "functions\n", shadow_var_cnt); + + /* Compute the global points-to sets for ESCAPED. + ??? Note that the computed escape set is not correct + for the whole unit as we fail to consider graph edges to + externally visible functions. */ + ipa_escaped_pt = find_what_var_points_to (NULL, get_varinfo (escaped_id)); + + /* Make sure the ESCAPED solution (which is used as placeholder in + other solutions) does not reference itself. This simplifies + points-to solution queries. */ + ipa_escaped_pt.ipa_escaped = 0; + + /* Assign the points-to sets to the SSA names in the unit. */ + FOR_EACH_DEFINED_FUNCTION (node) + { + tree ptr; + struct function *fn; + unsigned i; + basic_block bb; + + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) + continue; + + fn = DECL_STRUCT_FUNCTION (node->decl); + + /* Compute the points-to sets for pointer SSA_NAMEs. */ + FOR_EACH_VEC_ELT (*fn->gimple_df->ssa_names, i, ptr) + { + if (ptr + && POINTER_TYPE_P (TREE_TYPE (ptr))) + find_what_p_points_to (node->decl, ptr); + } + + /* Compute the call-use and call-clobber sets for indirect calls + and calls to external functions. */ + FOR_EACH_BB_FN (bb, fn) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gcall *stmt; + struct pt_solution *pt; + varinfo_t vi, fi; + tree decl; + + stmt = dyn_cast <gcall *> (gsi_stmt (gsi)); + if (!stmt) + continue; + + /* Handle direct calls to functions with body. */ + decl = gimple_call_fndecl (stmt); + + { + tree called_decl = NULL_TREE; + if (gimple_call_builtin_p (stmt, BUILT_IN_GOMP_PARALLEL)) + called_decl = TREE_OPERAND (gimple_call_arg (stmt, 0), 0); + else if (gimple_call_builtin_p (stmt, BUILT_IN_GOACC_PARALLEL)) + called_decl = TREE_OPERAND (gimple_call_arg (stmt, 1), 0); + + if (called_decl != NULL_TREE + && !fndecl_maybe_in_other_partition (called_decl)) + decl = called_decl; + } + + if (decl + && (fi = lookup_vi_for_tree (decl)) + && fi->is_fn_info) + { + *gimple_call_clobber_set (stmt) + = find_what_var_points_to + (node->decl, first_vi_for_offset (fi, fi_clobbers)); + *gimple_call_use_set (stmt) + = find_what_var_points_to + (node->decl, first_vi_for_offset (fi, fi_uses)); + } + /* Handle direct calls to external functions. */ + else if (decl && (!fi || fi->decl)) + { + pt = gimple_call_use_set (stmt); + if (gimple_call_flags (stmt) & ECF_CONST) + memset (pt, 0, sizeof (struct pt_solution)); + else if ((vi = lookup_call_use_vi (stmt)) != NULL) + { + *pt = find_what_var_points_to (node->decl, vi); + /* Escaped (and thus nonlocal) variables are always + implicitly used by calls. */ + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + pt->nonlocal = 1; + pt->ipa_escaped = 1; + } + else + { + /* If there is nothing special about this call then + we have made everything that is used also escape. */ + *pt = ipa_escaped_pt; + pt->nonlocal = 1; + } + + pt = gimple_call_clobber_set (stmt); + if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS)) + memset (pt, 0, sizeof (struct pt_solution)); + else if ((vi = lookup_call_clobber_vi (stmt)) != NULL) + { + *pt = find_what_var_points_to (node->decl, vi); + /* Escaped (and thus nonlocal) variables are always + implicitly clobbered by calls. */ + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + pt->nonlocal = 1; + pt->ipa_escaped = 1; + } + else + { + /* If there is nothing special about this call then + we have made everything that is used also escape. */ + *pt = ipa_escaped_pt; + pt->nonlocal = 1; + } + } + /* Handle indirect calls. */ + else if ((fi = get_fi_for_callee (stmt))) + { + /* We need to accumulate all clobbers/uses of all possible + callees. */ + fi = get_varinfo (find (fi->id)); + /* If we cannot constrain the set of functions we'll end up + calling we end up using/clobbering everything. */ + if (bitmap_bit_p (fi->solution, anything_id) + || bitmap_bit_p (fi->solution, nonlocal_id) + || bitmap_bit_p (fi->solution, escaped_id)) + { + pt_solution_reset (gimple_call_clobber_set (stmt)); + pt_solution_reset (gimple_call_use_set (stmt)); + } + else + { + bitmap_iterator bi; + unsigned i; + struct pt_solution *uses, *clobbers; + + uses = gimple_call_use_set (stmt); + clobbers = gimple_call_clobber_set (stmt); + memset (uses, 0, sizeof (struct pt_solution)); + memset (clobbers, 0, sizeof (struct pt_solution)); + EXECUTE_IF_SET_IN_BITMAP (fi->solution, 0, i, bi) + { + struct pt_solution sol; + + vi = get_varinfo (i); + if (!vi->is_fn_info) + { + /* ??? We could be more precise here? */ + uses->nonlocal = 1; + uses->ipa_escaped = 1; + clobbers->nonlocal = 1; + clobbers->ipa_escaped = 1; + continue; + } + + if (!uses->anything) + { + sol = find_what_var_points_to + (node->decl, + first_vi_for_offset (vi, fi_uses)); + pt_solution_ior_into (uses, &sol); + } + if (!clobbers->anything) + { + sol = find_what_var_points_to + (node->decl, + first_vi_for_offset (vi, fi_clobbers)); + pt_solution_ior_into (clobbers, &sol); + } + } + } + } + else + gcc_unreachable (); + } + } + + fn->gimple_df->ipa_pta = true; + + /* We have to re-set the final-solution cache after each function + because what is a "global" is dependent on function context. */ + final_solutions->empty (); + obstack_free (&final_solutions_obstack, NULL); + gcc_obstack_init (&final_solutions_obstack); + } + + delete_points_to_sets (); + + in_ipa_mode = 0; + + return 0; +} + +namespace { + +const pass_data pass_data_ipa_pta = +{ + SIMPLE_IPA_PASS, /* type */ + "pta", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_IPA_PTA, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_ipa_pta : public simple_ipa_opt_pass +{ +public: + pass_ipa_pta (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_pta, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return (optimize + && flag_ipa_pta + /* Don't bother doing anything if the program has errors. */ + && !seen_error ()); + } + + opt_pass * clone () { return new pass_ipa_pta (m_ctxt); } + + virtual unsigned int execute (function *) { return ipa_pta_execute (); } + +}; // class pass_ipa_pta + +} // anon namespace + +simple_ipa_opt_pass * +make_pass_ipa_pta (gcc::context *ctxt) +{ + return new pass_ipa_pta (ctxt); +} |