This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] [RFC] loop index promotion pass


On Thu, Apr 23, 2009 at 5:17 PM, Nathan Froyd <froydnj@codesourcery.com> wrote:
> The patch below implements a pass that promotes shorter-than-word-size
> loop indices to word size indices. ?This pass enables improvements of
> 30% or more on particular EEMBC benchmarks and a >2% overall gain on the
> EEMBC suite.

Is this related to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36905 ?

Ramana


>
> When doing benchmarking for our customers, we noticed GCC doing a poor
> job of optimizing code like:
>
> void
> frob (unsigned char *in, unsigned char *out)
> {
> ?unsigned short h = 640, w = 480;
> ?unsigned short i, j;
>
> ?for (i = 1; i < (h - 1); i++)
> ? ?for (j = 1; j < (w - 1); j++)
> ? ? ?{
> ? ? ? ?unsigned int c = (i * w) + j;
> ? ? ? ?unsigned short b = (in[c - w -1]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c - w]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c - w + 1]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c - 1]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c + 1]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c + w + 1]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c + w]
> ? ? ? ? ? ? ? ? ? ? ? ? ? + in[c + w - 1]);
> ? ? ? ?*out++ = b;
> ? ? ?}
> }
>
> relative to other compilers. ?For reference, the -O2 x86 assembly
> generated for the innermost loop above looks like:
>
> .L3:
> ? ? ? ?addl ? ?-24(%ebp), %edx
> ? ? ? ?addl ? ?$1, %ecx
> ? ? ? ?movzbl ?-480(%eax,%edx), %edi
> ? ? ? ?leal ? ?-480(%edx), %esi
> ? ? ? ?leal ? ?480(%edx), %ebx
> ? ? ? ?movw ? ?%di, -14(%ebp)
> ? ? ? ?movzbl ?-1(%eax,%edx), %edi
> ? ? ? ?addw ? ?-14(%ebp), %di
> ? ? ? ?movw ? ?%di, -14(%ebp)
> ? ? ? ?movzbl ?(%eax,%edx), %edi
> ? ? ? ?addw ? ?-14(%ebp), %di
> ? ? ? ?movw ? ?%di, -14(%ebp)
> ? ? ? ?movzbl ?1(%eax,%edx), %edi
> ? ? ? ?movzbl ?480(%eax,%edx), %edx
> ? ? ? ?addw ? ?-14(%ebp), %di
> ? ? ? ?addl ? ?%edx, %edi
> ? ? ? ?movzbl ?-1(%eax,%esi), %edx
> ? ? ? ?addl ? ?%edx, %edi
> ? ? ? ?movzbl ?1(%eax,%esi), %edx
> ? ? ? ?movzbl ?1(%eax,%ebx), %esi
> ? ? ? ?addl ? ?%edx, %edi
> ? ? ? ?leal ? ?(%edi,%esi), %esi
> ? ? ? ?movzbl ?-1(%eax,%ebx), %edi
> ? ? ? ?movl ? ?-20(%ebp), %ebx
> ? ? ? ?leal ? ?(%esi,%edi), %edx
> ? ? ? ?movb ? ?%dl, (%ebx)
> ? ? ? ?addl ? ?$1, %ebx
> ? ? ? ?cmpw ? ?$479, %cx
> ? ? ? ?movl ? ?%ebx, -20(%ebp)
> ? ? ? ?movzwl ?%cx, %edx
> ? ? ? ?jne ? ? .L3
>
> Notice that we have three indices into %eax (%edx, %esi, and %ebx, setup
> just after .L3) where we should only have one. ?This is, as far as we
> could tell, a failure of IV recognition to see through the casts
> involved in the loop indexing because the indices are shorts and they
> need to be promoted to ints for arithmetic.
>
> Compare the assembly above with the -O2 -fpromote-loop-indices x86
> assembly:
>
> .L3:
> ? ? ? ?movzbl ?-480(%eax), %ecx
> ? ? ? ?movzbl ?-479(%eax), %ebx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?-478(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?1(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?2(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?482(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?481(%eax), %ecx
> ? ? ? ?addl ? ?%ecx, %ebx
> ? ? ? ?movzbl ?480(%eax), %ecx
> ? ? ? ?addl ? ?$1, %eax
> ? ? ? ?leal ? ?(%ebx,%ecx), %ecx
> ? ? ? ?movb ? ?%cl, (%esi,%edx)
> ? ? ? ?addl ? ?$1, %edx
> ? ? ? ?cmpl ? ?$478, %edx
> ? ? ? ?jne ? ? .L3
>
> Here the compiler is properly using a single base register (%eax) and
> the immediate offset. ?We have also dropped a sign extension, since our
> index no longer requires promotion to int for several operations, we
> have lower register pressure, etc. etc.
>
> The pass is placed as early as possible in the pipeline so that as many
> optimizations as possible see the promoted indices.
>
> Bootstrapped on x86_64-unknown-linux-gnu. ?Comments welcome.
>
> -Nathan
>
> ./
> ? ? ? ?* tree-ssa-loop-promote.c: New file.
> ? ? ? ?* common.opt (fpromote-loop-indices): New option.
> ? ? ? ?* timevar.def (TV_TREE_LOOP_PROMOTE): New timevar.
> ? ? ? ?* Makefile.in (tree-ssa-loop-promote.o): New rule.
> ? ? ? ?(OBJS-common): Include it.
> ? ? ? ?* tree-pass.h (pass_promote_indices): Declare.
> ? ? ? ?* passes.c (init_optimization_passes): Add it.
> ? ? ? ?* pointer-set.h (pointer_set_n_elements, pointer_set_clear)
> ? ? ? ?(pointer_map_n_elements, pointer_map_clear): Declare.
> ? ? ? ?* pointer-set.c (pointer_set_n_elements, pointer_set_clear,
> ? ? ? ?pointer_map_n_elements, pointer_map_clear): Define.
> ? ? ? ?* doc/invoke.texi (-fpromote-loop-indices): New entry.
>
> testsuite/
> ? ? ? ?* gcc.dg/promote-short-1.c: New file.
> ? ? ? ?* gcc.dg/promote-short-2.c: New file.
> ? ? ? ?* gcc.dg/promote-short-3.c: New file.
> ? ? ? ?* gcc.dg/promote-short-4.c: New file.
> ? ? ? ?* gcc.dg/promote-short-5.c: New file.
> ? ? ? ?* gcc.dg/promote-short-6.c: New file.
> ? ? ? ?* gcc.dg/promote-short-7.c: New file.
> ? ? ? ?* gcc.dg/promote-short-8.c: New file.
> ? ? ? ?* gcc.dg/promote-short-9.c: New file.
> ? ? ? ?* gcc.dg/promote-short-10.c: New file.
>
> Index: doc/invoke.texi
> ===================================================================
> --- doc/invoke.texi ? ? (revision 146526)
> +++ doc/invoke.texi ? ? (working copy)
> @@ -7256,6 +7256,14 @@ int foo (void)
>
> ?Not all targets support this option.
>
> +@item -fpromote-loop-indices
> +@opindex fpromote-loop-indices
> +Converts loop indices that have a type shorter than the word size to
> +word-sized quantities. ?This transformation can reduce the overhead
> +associated with sign/zero-extension and truncation of such variables.
> +Using @option{-funsafe-loop-optimizations} with this option may result
> +in more effective optimization.
> +
> ?@item --param @var{name}=@var{value}
> ?@opindex param
> ?In some places, GCC uses various constants to control the amount of
> Index: tree-pass.h
> ===================================================================
> --- tree-pass.h (revision 146526)
> +++ tree-pass.h (working copy)
> @@ -331,6 +331,7 @@ extern struct gimple_opt_pass pass_scev_
> ?extern struct gimple_opt_pass pass_empty_loop;
> ?extern struct gimple_opt_pass pass_record_bounds;
> ?extern struct gimple_opt_pass pass_graphite_transforms;
> +extern struct gimple_opt_pass pass_promote_indices;
> ?extern struct gimple_opt_pass pass_if_conversion;
> ?extern struct gimple_opt_pass pass_loop_distribution;
> ?extern struct gimple_opt_pass pass_vectorize;
> Index: pointer-set.c
> ===================================================================
> --- pointer-set.c ? ? ? (revision 146526)
> +++ pointer-set.c ? ? ? (working copy)
> @@ -181,6 +181,23 @@ void pointer_set_traverse (const struct
> ? ? ? break;
> ?}
>
> +/* Return the number of elements in PSET. ?*/
> +
> +size_t
> +pointer_set_n_elements (struct pointer_set_t *pset)
> +{
> + ?return pset->n_elements;
> +}
> +
> +/* Remove all entries from PSET. ?*/
> +
> +void
> +pointer_set_clear (struct pointer_set_t *pset)
> +{
> + ?pset->n_elements = 0;
> + ?memset (pset->slots, 0, sizeof (pset->slots[0]) * pset->n_slots);
> +}
> +
>
> ?/* A pointer map is represented the same way as a pointer_set, so
> ? ?the hash code is based on the address of the key, rather than
> @@ -301,3 +318,20 @@ void pointer_map_traverse (const struct
> ? ? if (pmap->keys[i] && !fn (pmap->keys[i], &pmap->values[i], data))
> ? ? ? break;
> ?}
> +
> +/* Return the number of elements in PMAP. ?*/
> +
> +size_t
> +pointer_map_n_elements (struct pointer_map_t *pmap)
> +{
> + ?return pmap->n_elements;
> +}
> +
> +/* Remove all entries from PMAP. ?*/
> +
> +void pointer_map_clear (struct pointer_map_t *pmap)
> +{
> + ?pmap->n_elements = 0;
> + ?memset (pmap->keys, 0, sizeof (pmap->keys[0]) * pmap->n_slots);
> + ?memset (pmap->values, 0, sizeof (pmap->values[0]) * pmap->n_slots);
> +}
> Index: pointer-set.h
> ===================================================================
> --- pointer-set.h ? ? ? (revision 146526)
> +++ pointer-set.h ? ? ? (working copy)
> @@ -29,6 +29,8 @@ int pointer_set_insert (struct pointer_s
> ?void pointer_set_traverse (const struct pointer_set_t *,
> ? ? ? ? ? ? ? ? ? ? ? ? ? bool (*) (const void *, void *),
> ? ? ? ? ? ? ? ? ? ? ? ? ? void *);
> +size_t pointer_set_n_elements (struct pointer_set_t *);
> +void pointer_set_clear (struct pointer_set_t *);
>
> ?struct pointer_map_t;
> ?struct pointer_map_t *pointer_map_create (void);
> @@ -38,5 +40,7 @@ void **pointer_map_contains (const struc
> ?void **pointer_map_insert (struct pointer_map_t *pmap, const void *p);
> ?void pointer_map_traverse (const struct pointer_map_t *,
> ? ? ? ? ? ? ? ? ? ? ? ? ? bool (*) (const void *, void **, void *), void *);
> +size_t pointer_map_n_elements (struct pointer_map_t *);
> +void pointer_map_clear (struct pointer_map_t *);
>
> ?#endif ?/* POINTER_SET_H ?*/
> Index: ChangeLog
> ===================================================================
> Index: testsuite/gcc.target/powerpc/ppc-spe64-1.c
> ===================================================================
> --- testsuite/gcc.target/powerpc/ppc-spe64-1.c ?(revision 146526)
> +++ testsuite/gcc.target/powerpc/ppc-spe64-1.c ?(working copy)
> @@ -4,4 +4,4 @@
> ?/* { dg-options "-m64" } */
>
> ?/* { dg-error "-m64 not supported in this configuration" "SPE not 64-bit" { target *-*-* } 0 } */
> -/* { dg-error "64-bit E500 not supported" "64-bit E500" { target *-*-* } 0 } */
> +/* { dg-error "64-bit E500 not supported" "64-bit E500" { target powerpc-*-eabi* } 0 } */
> Index: testsuite/gcc.dg/promote-short-5.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-5.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-5.c ?(revision 0)
> @@ -0,0 +1,18 @@
> +/* Verify that we do not promote a short loop index variable when it has
> + ? a non-unit-increment. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 0 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (short n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i+=2)
> + ? ?{
> + ? ? ?x[i] = 0;
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-9.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-9.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-9.c ?(revision 0)
> @@ -0,0 +1,15 @@
> +/* -fpromote-loop-indices used to ICE on this. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices" } */
> +
> +char
> +lookup (char *haystack, char *needle)
> +{
> + ?char x;
> +
> + ?for (x = haystack[-2]; x < *needle; x++)
> + ? ?haystack[x] = needle[x];
> +
> + ?return 1;
> +}
> Index: testsuite/gcc.dg/promote-short-2.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-2.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-2.c ?(revision 0)
> @@ -0,0 +1,16 @@
> +/* Verify that we do not promote a short loop index variable when it is
> + ? being stored to memory. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 0 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (short n, short *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?x[i] = i;
> +}
> Index: testsuite/gcc.dg/promote-short-6.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-6.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-6.c ?(revision 0)
> @@ -0,0 +1,18 @@
> +/* Verify that we do promote a short loop index variable when it has
> + ? a non-unit-increment and -funsafe-loop-optimizations is in effect. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -funsafe-loop-optimizations -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 1 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (short n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i+=2)
> + ? ?{
> + ? ? ?x[i] = 0;
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-10.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-10.c (revision 0)
> +++ testsuite/gcc.dg/promote-short-10.c (revision 0)
> @@ -0,0 +1,20 @@
> +/* Verify that we do not promote a short loop index variable when its
> + ? address is taken. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Found 0 candidates" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +extern void outside (short *);
> +
> +void
> +test1 (int n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?{
> + ? ? ?outside (&i);
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-3.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-3.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-3.c ?(revision 0)
> @@ -0,0 +1,18 @@
> +/* Verify that we do not promote a short loop index variable when it is
> + ? being passed as a function parameter. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 0 variables" 1 "promoteshort" { xfail m68k*-*-* fido*-*-* i?86-*-* x86_64-*-* mips*-*-* sh*-*-* } } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +extern void outside (short);
> +
> +void
> +test1 (short n)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?outside (i);
> +}
> Index: testsuite/gcc.dg/promote-short-7.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-7.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-7.c ?(revision 0)
> @@ -0,0 +1,18 @@
> +/* Verify that we do not promote a short loop index variable when the
> + ? loop in which it is used has a bound of wider type. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 0 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (int n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?{
> + ? ? ?x[i] = 0;
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-4.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-4.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-4.c ?(revision 0)
> @@ -0,0 +1,19 @@
> +/* Verify that we do not promote a short loop index variable when it is
> + ? modified within the loop. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 0 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (short n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?{
> + ? ? ?i++;
> + ? ? ?x[i] = 0;
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-8.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-8.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-8.c ?(revision 0)
> @@ -0,0 +1,19 @@
> +/* Verify that we do promote a short loop index variable when the loop
> + ? in which it is used has a bound of wider type and
> + ? -funsafe-loop-optimizations is in effect. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -funsafe-loop-optimizations -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 1 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (int n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?{
> + ? ? ?x[i] = 0;
> + ? ?}
> +}
> Index: testsuite/gcc.dg/promote-short-1.c
> ===================================================================
> --- testsuite/gcc.dg/promote-short-1.c ?(revision 0)
> +++ testsuite/gcc.dg/promote-short-1.c ?(revision 0)
> @@ -0,0 +1,15 @@
> +/* Verify that we promote a short loop index variable. ?*/
> +
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fpromote-loop-indices -fdump-tree-promoteshort" } */
> +/* { dg-final { scan-tree-dump-times "Promoting 1 variables" 1 "promoteshort" } } */
> +/* { dg-final { cleanup-tree-dump "promoteshort" } } */
> +
> +void
> +test1 (short n, int *x)
> +{
> + ?short i;
> +
> + ?for (i = 0; i < n; i++)
> + ? ?x[i] = 0;
> +}
> Index: testsuite/ChangeLog
> ===================================================================
> Index: timevar.def
> ===================================================================
> --- timevar.def (revision 146526)
> +++ timevar.def (working copy)
> @@ -130,6 +130,7 @@ DEFTIMEVAR (TV_TREE_LOOP_IVOPTS ? ? ? ? ?, "
> ?DEFTIMEVAR (TV_PREDCOM ? ? ? ? ? ? ?, "predictive commoning")
> ?DEFTIMEVAR (TV_TREE_LOOP_INIT ? ? ? , "tree loop init")
> ?DEFTIMEVAR (TV_TREE_LOOP_FINI ? ? ? , "tree loop fini")
> +DEFTIMEVAR (TV_TREE_LOOP_PROMOTE ? ? , "tree loop index promotion")
> ?DEFTIMEVAR (TV_TREE_CH ? ? ? ? ? ? ?, "tree copy headers")
> ?DEFTIMEVAR (TV_TREE_SSA_UNCPROP ? ? ? ? ? ? , "tree SSA uncprop")
> ?DEFTIMEVAR (TV_TREE_SSA_TO_NORMAL ? ?, "tree SSA to normal")
> Index: tree-ssa-loop-promote.c
> ===================================================================
> --- tree-ssa-loop-promote.c ? ? (revision 0)
> +++ tree-ssa-loop-promote.c ? ? (revision 0)
> @@ -0,0 +1,1594 @@
> +/* Promotion of shorter-than-word-size loop indices.
> + ? Copyright (C) 2009 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it
> +under the terms of the GNU General Public License as published by the
> +Free Software Foundation; either version 3, or (at your option) any
> +later version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT
> +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE. ?See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3. ?If not see
> +<http://www.gnu.org/licenses/>. ?*/
> +
> +/* This pass finds loop indices that are declared as
> + ? shorter-than-word-size and replaces them with word-sized loop
> + ? indices. ?(It assumes that word-sized quantities are the most
> + ? efficient type on which to do arithmetic.) ?The loop optimization
> + ? machinery has a difficult time seeing through the casts required to
> + ? promote such indices to word-sized quantities for memory addressing
> + ? and/or preserving the semantics of the source language (such as C).
> + ? The transformation also helps eliminate unnecessary
> + ? {sign,zero}-extensions required for the same.
> +
> + ? Although this is most naturally expressed as a loop optimization
> + ? pass, we choose to place this pass some ways before the loop
> + ? optimization passes proper, so that other scalar optimizations will
> + ? run on our "cleaned-up" code. ?This decision has the negative of
> + ? requiring us to build and destroy all the loop optimization
> + ? infrastructure.
> +
> + ? The algorithm is relatively simple. ?For each single-exit loop, we
> + ? identify the loop index variable. ?If the loop index variable is
> + ? shorter than the word size, then we have a candidate for promotion.
> + ? We determine whether the scalar evolution of the loop index fits a
> + ? particular pattern (incremented by 1, compared against a
> + ? similarly-typed loop bound, and only modified by a single increment
> + ? within the loop), as well as examining the uses of the loop index to
> + ? ensure we are able to safely promote those uses (e.g. the loop index
> + ? must not be stored to memory or passed to function calls). ?If these
> + ? conditions are satisfied, we create an appropriate word-sized type
> + ? and replace all uses and defs of the loop index variable with the new
> + ? variable. ?*/
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"
> +
> +#include "toplev.h"
> +#include "rtl.h"
> +#include "tm_p.h"
> +#include "hard-reg-set.h"
> +#include "obstack.h"
> +#include "basic-block.h"
> +#include "pointer-set.h"
> +#include "intl.h"
> +
> +#include "tree.h"
> +#include "gimple.h"
> +#include "hashtab.h"
> +#include "diagnostic.h"
> +#include "tree-flow.h"
> +#include "tree-dump.h"
> +#include "cfgloop.h"
> +#include "flags.h"
> +#include "timevar.h"
> +#include "tree-pass.h"
> +
> +struct promote_info {
> + ?/* The loop being analyzed. ?*/
> + ?struct loop *loop;
> +
> + ?/* The GIMPLE_COND controlling exit from the loop. ?*/
> + ?gimple exit_expr;
> +
> + ?/* The loop index variable's SSA_NAME that is defined in a phi node in
> + ? ? LOOP->HEADER. ?Note that this SSA_NAME may be different than the
> + ? ? one appearing in EXIT_EXPR. ?*/
> + ?tree loop_index_name;
> +
> + ?/* The bound of the loop. ?*/
> + ?tree loop_limit;
> +
> + ?/* Whether we've warned about things with
> + ? ? warn_unsafe_loop_optimizations. ?*/
> + ?bool warned;
> +
> + ?/* LOOP_INDEX_NAME's underlying VAR_DECL. ?*/
> + ?tree var_decl;
> +
> + ?/* The types to which defs/uses of LOOP_INDEX_NAME are cast via
> + ? ? NOP_EXPRs. ?*/
> + ?VEC(tree, heap) *cast_types;
> +
> + ?/* The number of times we have seen a cast to the corresponding type
> + ? ? (as determined by types_compatible_p) in CAST_TYPES. ?*/
> + ?VEC(int, heap) *cast_counts;
> +
> + ?/* Whether LOOP_INDEX_NAME is suitable for promotion. ?*/
> + ?bool can_be_promoted_p;
> +
> + ?/* If CAN_BE_PROMOTED_P, the promoted type. ?*/
> + ?tree promoted_type;
> +
> + ?/* If CAN_BE_PROMOTED_P, the promoted VAR_DECL. ?*/
> + ?tree promoted_var;
> +};
> +
> +/* A set of `struct promote_info'. ?*/
> +
> +static struct pointer_set_t *promotion_info;
> +
> +/* A set of all potentially promotable SSA_NAMEs, used for quick
> +decision-making during analysis. ?*/
> +
> +static struct pointer_set_t *promotable_names;
> +
> +/* A map from SSA_NAMEs to the VAR_DECL to which they will be
> + ? promoted. ?*/
> +
> +static struct pointer_map_t *variable_map;
> +
> +/* A set of the stmts that we have already rebuilt with promoted variables. ?*/
> +
> +static struct pointer_set_t *promoted_stmts;
> +
> +
> +/* Add CASTED to PI->CAST_TYPES if we haven't seen CASTED before. ?*/
> +
> +static void
> +add_casted_type (struct promote_info *pi, tree casted)
> +{
> + ?int i;
> + ?tree type;
> +
> + ?/* For this information to be useful later, CASTED must be wider than
> + ? ? the type of the variable. ?*/
> + ?if (TYPE_PRECISION (casted) <= TYPE_PRECISION (TREE_TYPE (pi->var_decl)))
> + ? ?return;
> +
> + ?for (i = 0; VEC_iterate (tree, pi->cast_types, i, type); i++)
> + ? ?if (types_compatible_p (casted, type))
> + ? ? ?{
> + ? ? ? int c = VEC_index(int, pi->cast_counts, i);
> + ? ? ? VEC_replace(int, pi->cast_counts, i, ++c);
> + ? ? ? return;
> + ? ? ?}
> +
> + ?/* Haven't see the type before. ?*/
> + ?VEC_safe_push (tree, heap, pi->cast_types, casted);
> + ?VEC_safe_push (int, heap, pi->cast_counts, 1);
> +}
> +
> +/* Return the most-casted-to type in PI->CAST_TYPES. ?Return an
> + ? appropriately signed variant of size_type_node if the variable wasn't
> + ? cast in some fashion. ?*/
> +
> +static tree
> +choose_profitable_promoted_type (struct promote_info *pi)
> +{
> + ?int i;
> + ?int count;
> + ?tree type = NULL_TREE;
> + ?int maxuse = -1;
> +
> + ?for (i = 0; VEC_iterate (int, pi->cast_counts, i, count); i++)
> + ? ?if (count > maxuse)
> + ? ? ?{
> + ? ? ? maxuse = count;
> + ? ? ? type = VEC_index (tree, pi->cast_types, i);
> + ? ? ?}
> +
> + ?if (type == NULL_TREE)
> + ? ?{
> + ? ? ?if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "Warning, failed to find upcast type for ");
> + ? ? ? ? print_generic_expr (dump_file, pi->loop_index_name, 0);
> + ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? }
> + ? ? ?return (TYPE_UNSIGNED (TREE_TYPE (pi->var_decl))
> + ? ? ? ? ? ? ? size_type_node
> + ? ? ? ? ? ? : signed_type_for (size_type_node));
> + ? ?}
> + ?else
> + ? ?return signed_type_for (type);
> +}
> +
> +/* Intuit the loop index for LOOP from PHI. ?There must be a path that
> + ? only goes through NOP_EXPRs or CONVERT_EXPRs from the result of PHI
> + ? to one of the operands of COND. ?If such a path cannot be found,
> + ? return NULL_TREE. ?If LIMIT is not NULL and a path can be found,
> + ? store the other operand of COND into LIMIT. ?*/
> +
> +static tree
> +find_promotion_candidate_from_phi (struct loop *loop, gimple cond,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?gimple phi, tree *limit)
> +{
> + ?tree op0, op1;
> + ?tree result, candidate;
> +
> + ?result = candidate = PHI_RESULT (phi);
> + ?/* Must be an integer variable. ?*/
> + ?if (TREE_CODE (TREE_TYPE (candidate)) != INTEGER_TYPE)
> + ? ?return NULL_TREE;
> +
> + ?op0 = gimple_cond_lhs (cond);
> + ?op1 = gimple_cond_rhs (cond);
> +
> + ?/* See if there's a path from CANDIDATE to an operand of COND. ?*/
> + ?while (true)
> + ? ?{
> + ? ? ?use_operand_p use;
> + ? ? ?imm_use_iterator iui;
> + ? ? ?gimple use_stmt = NULL;
> +
> + ? ? ?if (candidate == op0)
> + ? ? ? {
> + ? ? ? ? if (limit) *limit = op1;
> + ? ? ? ? break;
> + ? ? ? }
> + ? ? ?if (candidate == op1)
> + ? ? ? {
> + ? ? ? ? if (limit) *limit = op0;
> + ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ?/* Find a single use in the loop header. ?Give up if there's
> + ? ? ? ?multiple ones. ?*/
> + ? ? ?FOR_EACH_IMM_USE_FAST (use, iui, candidate)
> + ? ? ? {
> + ? ? ? ? gimple stmt = USE_STMT (use);
> +
> + ? ? ? ? if (gimple_bb (stmt) == loop->header)
> + ? ? ? ? ? {
> + ? ? ? ? ? ? if (use_stmt)
> + ? ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? ? if (dump_file)
> + ? ? ? ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? ? ? ? fprintf (dump_file, "Rejecting ");
> + ? ? ? ? ? ? ? ? ? ? print_generic_expr (dump_file, candidate, 0);
> + ? ? ? ? ? ? ? ? ? ? fprintf (dump_file, " because it has multiple uses in the loop header (bb #%d).\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?loop->header->index);
> + ? ? ? ? ? ? ? ? ? ? fprintf (dump_file, "first use: ");
> + ? ? ? ? ? ? ? ? ? ? print_gimple_stmt (dump_file, use_stmt, 0, 0);
> + ? ? ? ? ? ? ? ? ? ? fprintf (dump_file, "\nsecond use: ");
> + ? ? ? ? ? ? ? ? ? ? print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ? ? ? ? ? ? ? ? fprintf (dump_file, "\n(possibly more, but unanalyzed)\n");
> + ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? return NULL_TREE;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? use_stmt = stmt;
> + ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ?/* No uses in the loop header, bail. ?*/
> + ? ? ?if (use_stmt == NULL)
> + ? ? ? return NULL_TREE;
> +
> + ? ? ?if (gimple_code (use_stmt) != GIMPLE_ASSIGN
> + ? ? ? ? || TREE_CODE (gimple_assign_lhs (use_stmt)) != SSA_NAME
> + ? ? ? ? || (gimple_assign_rhs_code (use_stmt) != NOP_EXPR
> + ? ? ? ? ? ? && gimple_assign_rhs_code (use_stmt) != CONVERT_EXPR))
> + ? ? ? {
> + ? ? ? ? if (dump_file)
> + ? ? ? ? ? {
> + ? ? ? ? ? ? fprintf (dump_file, "Rejecting ");
> + ? ? ? ? ? ? print_generic_expr (dump_file, candidate, 0);
> + ? ? ? ? ? ? fprintf (dump_file, " because of use in ");
> + ? ? ? ? ? ? print_gimple_stmt (dump_file, use_stmt, 0, 0);
> + ? ? ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? ? ? }
> + ? ? ? ? return NULL_TREE;
> + ? ? ? }
> +
> + ? ? ?candidate = gimple_assign_lhs (use_stmt);
> + ? ?}
> +
> + ?/* CANDIDATE is now what we believe to be the loop index variable. ?There
> + ? ? are two possibilities:
> +
> + ? ? - CANDIDATE is not the "true" loop index variable, but rather is a
> + ? ? ? promoted version of RESULT, done for purposes of satisfying a
> + ? ? ? language's semantics;
> +
> + ? ? - CANDIDATE is the "true" loop index variable. ?*/
> + ?if (!types_compatible_p (TREE_TYPE (result), TREE_TYPE (candidate)))
> + ? ?candidate = result;
> +
> + ?/* The type of candidate must be "short" to consider promoting it. ?*/
> + ?if (TREE_CODE (TREE_TYPE (candidate)) != INTEGER_TYPE
> + ? ? ?|| TYPE_PRECISION (TREE_TYPE (candidate)) >= TYPE_PRECISION (size_type_node))
> + ? ?return NULL_TREE;
> +
> + ?return candidate;
> +}
> +
> +/* Find the loop index variable of LOOP. ?LOOP's exit is controlled by
> + ? the COND_EXPR EXPR. ?IF we can't determine what the loop index
> + ? variable is, or EXPR does not appear to be analyzable, then return
> + ? NULL_TREE. ?*/
> +
> +static tree
> +find_promotion_candidate (struct loop *loop, gimple cond, tree *limit)
> +{
> + ?tree candidate = NULL_TREE;
> + ?gimple_stmt_iterator gsi;
> +
> + ?switch (gimple_cond_code (cond))
> + ? ?{
> + ? ?case GT_EXPR:
> + ? ?case GE_EXPR:
> + ? ?case NE_EXPR:
> + ? ?case LT_EXPR:
> + ? ?case LE_EXPR:
> + ? ? ?break;
> +
> + ? ?default:
> + ? ? ?return NULL_TREE;
> + ? ?}
> +
> + ?/* We'd like to examine COND and intuit the loop index variable from
> + ? ? there. ?Instead, we're going to start from the phi nodes in BB and
> + ? ? attempt to work our way forwards to one of the operands of COND,
> + ? ? since starting from COND might yield an upcast loop index. ?If we
> + ? ? find multiple phi nodes whose results reach COND, then give up. ?*/
> + ?for (gsi = gsi_start_phis (loop->header); !gsi_end_p (gsi); gsi_next (&gsi))
> + ? ?{
> + ? ? ?gimple phi = gsi_stmt (gsi);
> + ? ? ?tree t = find_promotion_candidate_from_phi (loop, cond, phi, limit);
> +
> + ? ? ?if (t == NULL_TREE)
> + ? ? ? continue;
> + ? ? ?else if (candidate == NULL_TREE)
> + ? ? ? candidate = t;
> + ? ? ?else
> + ? ? ? {
> + ? ? ? ? if (dump_file)
> + ? ? ? ? ? {
> + ? ? ? ? ? ? fprintf (dump_file, "Can't find a candidate from ");
> + ? ? ? ? ? ? print_gimple_stmt (dump_file, cond, 0, 0);
> + ? ? ? ? ? ? fprintf (dump_file, "\n ?because too many phi node results reach the condition.\n");
> + ? ? ? ? ? }
> + ? ? ? ? return NULL_TREE;
> + ? ? ? }
> + ? ?}
> +
> + ?return candidate;
> +}
> +
> +/* Return true if X is something that could be promoted. ?*/
> +
> +static bool
> +could_be_promoted (tree x)
> +{
> + ?return (TREE_CODE (x) == INTEGER_CST
> + ? ? ? ? || (TREE_CODE (x) == SSA_NAME
> + ? ? ? ? ? ? && pointer_set_contains (promotable_names, x)));
> +}
> +
> +/* Examine the RHS of STMT's suitability with respect to being able to
> + ? promote VAR. ?*/
> +
> +static bool
> +check_rhs_for_promotability (struct promote_info *pi, tree var, gimple stmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?bool is_assign)
> +{
> + ?enum tree_code subcode = gimple_assign_rhs_code (stmt);
> +
> + ?bool ok = true;
> +
> + ?switch (subcode)
> + ? ?{
> + ? ?case PLUS_EXPR:
> + ? ?case MINUS_EXPR:
> + ? ?case MULT_EXPR:
> + ? ?case EQ_EXPR:
> + ? ?case NE_EXPR:
> + ? ?case LT_EXPR:
> + ? ?case LE_EXPR:
> + ? ?case GT_EXPR:
> + ? ?case GE_EXPR:
> + ? ? ?{
> + ? ? ? tree op0 = gimple_assign_rhs1 (stmt);
> + ? ? ? tree op1 = gimple_assign_rhs2 (stmt);
> +
> + ? ? ? ok = ((op0 == var && could_be_promoted (op1))
> + ? ? ? ? ? ? || (op1 == var && could_be_promoted (op0)));
> + ? ? ? break;
> + ? ? ?}
> + ? ?case COND_EXPR:
> + ? ? ?if (gimple_expr_type (stmt) == NULL
> + ? ? ? ? || gimple_expr_type (stmt) == void_type_node)
> + ? ? ? ok = true;
> + ? ? ?else
> + ? ? ? /* This is conservative; it's possible that these sorts of nodes
> + ? ? ? ? ?could be promoted, but we'd have to be very careful about
> + ? ? ? ? ?checking in which parts of the COND_EXPR the promotable
> + ? ? ? ? ?variable(s) are. ?*/
> + ? ? ? ok = false;
> + ? ? ?break;
> + ? ?case SSA_NAME:
> + ? ? ?{
> + ? ? ? tree expr = gimple_assign_rhs1 (stmt);
> + ? ? ? ok = (expr == var || could_be_promoted (expr));
> + ? ? ?}
> + ? ? ?break;
> + ? ?case INTEGER_CST:
> + ? ? ?break;
> + ? ?case NOP_EXPR:
> + ? ?case CONVERT_EXPR:
> + ? ? ?if (!is_assign)
> + ? ? ? {
> + ? ? ? ? add_casted_type (pi, gimple_expr_type (stmt));
> + ? ? ? ? break;
> + ? ? ? }
> + ? ? ?/* Fallthrough. ?*/
> + ? ?default:
> + ? ? ?ok = false;
> + ? ? ?break;
> + ? ?}
> +
> + ?return ok;
> +}
> +
> +/* Analyze the loop index VAR for promotability. ?The rules for
> + ? promotability are:
> +
> + ? For uses:
> +
> + ? - The underlying variable may be used in NOP_EXPRs.
> +
> + ? - The underlying variable may be used in simple arithmmetic
> + ? ? expressions so long as the other parts are potentially promotable
> + ? ? variables or constants (so we don't go willy-nilly on promoting
> + ? ? things).
> +
> + ? - The underlying variable may not be stored to memory.
> +
> + ? - All uses must occur inside the loop.
> +
> + ? For defs:
> +
> + ? - The underlying variable may not be loaded from memory; and
> +
> + ? - The underlying variable may only be formed from expressions
> + ? ? involving potentially promotable varibles or constants.
> +
> + ? Note that defs may occur outside of the loop; we do this to handle
> + ? initial conditions before entering the loop. ?*/
> +
> +static void
> +analyze_loop_index_uses (tree var, struct promote_info *pi)
> +{
> + ?imm_use_iterator iui;
> + ?use_operand_p use;
> + ?gimple bad_stmt = NULL;
> + ?const char *reason = NULL;
> +
> + ?FOR_EACH_IMM_USE_FAST (use, iui, var)
> + ? ?{
> + ? ? ?basic_block bb;
> + ? ? ?gimple use_stmt = USE_STMT (use);
> +
> + ? ? ?/* Uses must exist only within the loop. ?*/
> + ? ? ?bb = gimple_bb (use_stmt);
> +
> + ? ? ?if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "Checking ");
> + ? ? ? ? print_gimple_stmt (dump_file, use_stmt, 0, 0);
> + ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? }
> +
> + ? ? ?if (!flow_bb_inside_loop_p (pi->loop, bb))
> + ? ? ? {
> + ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? reason = " is involved in stmt outside loop ";
> + ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ?/* We cannot store the index to memory. ?*/
> + ? ? ?if (gimple_references_memory_p (use_stmt))
> + ? ? ? {
> + ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? reason = " is stored to memory in ";
> + ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ?if (gimple_code (use_stmt) == GIMPLE_CALL)
> + ? ? ? {
> + ? ? ? ? /* We cannot pass the variable to a function. ?*/
> + ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? reason = " is passed to function in ";
> + ? ? ? ? break;
> + ? ? ? }
> + ? ? ?else if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
> + ? ? ? {
> + ? ? ? ? tree lhs = gimple_assign_lhs (use_stmt);
> +
> + ? ? ? ? if (!check_rhs_for_promotability (pi, var, use_stmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*is_assign=*/false))
> + ? ? ? ? ? {
> + ? ? ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? ? ? reason = " is involved in non-promotable expression ";
> + ? ? ? ? ? ? break;
> + ? ? ? ? ? }
> + ? ? ? ? else if ((TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt)) == tcc_binary
> + ? ? ? ? ? ? ? ? ? || gimple_assign_rhs_code (use_stmt) == SSA_NAME)
> + ? ? ? ? ? ? ? ? ?&& !could_be_promoted (lhs))
> + ? ? ? ? ? {
> + ? ? ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? ? ? reason = " is being assigned to non-promotable variable ";
> + ? ? ? ? ? ? break;
> + ? ? ? ? ? }
> + ? ? ? }
> + ? ? ?else if (gimple_code (use_stmt) != GIMPLE_COND
> + ? ? ? ? ? ? ?&& gimple_code (use_stmt) != GIMPLE_PHI)
> + ? ? ? {
> + ? ? ? ? /* Use of the variable in some statement we don't know how to
> + ? ? ? ? ? ?analyze. ?*/
> + ? ? ? ? bad_stmt = use_stmt;
> + ? ? ? ? reason = " is used in unanalyzable expression in ";
> + ? ? ? ? break;
> + ? ? ? }
> + ? ?}
> +
> + ?if (bad_stmt && reason)
> + ? ?{
> + ? ? ?if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "Loop index ");
> + ? ? ? ? print_generic_expr (dump_file, var, 0);
> + ? ? ? ? fprintf (dump_file, "%s", reason);
> + ? ? ? ? print_gimple_stmt (dump_file, bad_stmt, 0, 0);
> + ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? }
> + ? ? ?pi->can_be_promoted_p = false;
> + ? ?}
> +}
> +
> +/* Check that the uses and def of VAR, defined in STMT, conform to the
> + ? rules given above. ?*/
> +
> +static bool
> +analyze_loop_index (tree var, gimple stmt, void *data)
> +{
> + ?struct promote_info *pi = (struct promote_info *) data;
> +
> + ?if (dump_file)
> + ? ?{
> + ? ? ?fprintf (dump_file, "Analyzing loop index ");
> + ? ? ?print_generic_expr (dump_file, var, 0);
> + ? ? ?fprintf (dump_file, " defined in ");
> + ? ? ?print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ?fprintf (dump_file, "\n");
> + ? ?}
> +
> + ?/* Check the definition. ?*/
> + ?switch (gimple_code (stmt))
> + ? ?{
> + ? ?case GIMPLE_PHI:
> + ? ? ?/* Phi nodes are OK. ?*/
> + ? ? ?break;
> +
> + ? ?case GIMPLE_ASSIGN:
> + ? ? ?if (!check_rhs_for_promotability (pi, var, stmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*is_assign=*/true))
> + ? ? ? break;
> + ? ? ?/* Fallthrough. ?*/
> +
> + ? ?default:
> + ? ? ?/* Something we can't handle or the variable is being loaded from
> + ? ? ? ?memory. ?*/
> + ? ? ?pi->can_be_promoted_p = false;
> + ? ? ?goto done;
> + ? ?}
> +
> + ?if (gimple_code (stmt) == GIMPLE_PHI)
> + ? ?{
> + ? ? ?unsigned int i;
> +
> + ? ? ?for (i = 0; i < gimple_phi_num_args (stmt); i++)
> + ? ? ? {
> + ? ? ? ? tree arg = PHI_ARG_DEF (stmt, i);
> +
> + ? ? ? ? if (TREE_CODE (arg) == SSA_NAME)
> + ? ? ? ? ? pointer_set_insert (promotable_names, arg);
> + ? ? ? }
> +
> + ? ? ?analyze_loop_index_uses (PHI_RESULT (stmt), pi);
> + ? ?}
> + ?else
> + ? ?analyze_loop_index_uses (var, pi);
> +
> + ?/* Only worth continuing if we think the loop index can be
> + ? ? promoted. ?*/
> + done:
> + ?if (dump_file)
> + ? ?{
> + ? ? ?fprintf (dump_file, "Done analyzing ");
> + ? ? ?print_generic_expr (dump_file, var, 0);
> + ? ? ?fprintf (dump_file, " defined in ");
> + ? ? ?print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ?fprintf (dump_file, "...%s to analyze\n\n",
> + ? ? ? ? ? ? ?pi->can_be_promoted_p ? "continuing" : "not continuing");
> + ? ?}
> + ?return !pi->can_be_promoted_p;
> +}
> +
> +/* Determine whether T is an INTEGER_CST or a single-use SSA_NAME
> + ? defined as the result of a NOP_EXPR or CONVERT_EXPR. ?Return the
> + ? operand of the NOP_EXPR or CONVERT_EXPR if so. ?*/
> +
> +static tree
> +upcast_operand_p (tree t)
> +{
> + ?gimple def;
> +
> + ?if (TREE_CODE (t) == INTEGER_CST)
> + ? ?return t;
> +
> + ?if (TREE_CODE (t) != SSA_NAME
> + ? ? ?|| !has_single_use (t))
> + ? ?return NULL_TREE;
> +
> + ?def = SSA_NAME_DEF_STMT (t);
> + ?if (gimple_code (def) != GIMPLE_ASSIGN)
> + ? ?return NULL_TREE;
> +
> + ?if (gimple_assign_rhs_code (def) != CONVERT_EXPR
> + ? ? ?&& gimple_assign_rhs_code (def) != NOP_EXPR)
> + ? ?return NULL_TREE;
> +
> + ?return gimple_assign_rhs1 (def);
> +}
> +
> +/* Check for the idiom:
> +
> + ? ? short x, y;
> + ? ? unsigned short x.2, y.2, tmp;
> + ? ? ...
> + ? ? x.2 = (unsigned short) x;
> + ? ? y.2 = (unsigned short) y;
> + ? ? tmp = x.2 + y.2;
> + ? ? x = (short) tmp;
> +
> + ? which is generated by convert for avoiding signed arithmetic
> + ? overflow. ?RHS is TMP in the above statement. ?If RHS is
> + ? defined via such an idiom, store x and y into *OP0 and *OP1,
> + ? respectively. ?We permit y.2 to be a constant if necessary. ?*/
> +
> +static bool
> +signed_arithmetic_overflow_idiom_p (tree rhs, tree *op0, tree *op1)
> +{
> + ?gimple op_stmt = SSA_NAME_DEF_STMT (rhs);
> + ?tree x2, y2;
> + ?bool yes = false;
> + ?enum tree_code code;
> +
> + ?if (!has_single_use (rhs)
> + ? ? ?|| gimple_code (op_stmt) != GIMPLE_ASSIGN)
> + ? ?goto done;
> +
> + ?/* This could probably profitably be expanded to consider
> + ? ? MINUS_EXPR, MULT_EXPR, etc. ?*/
> + ?code = gimple_assign_rhs_code (op_stmt);
> + ?if (code != PLUS_EXPR)
> + ? ?goto done;
> + ?x2 = gimple_assign_rhs1 (op_stmt);
> + ?y2 = gimple_assign_rhs2 (op_stmt);
> +
> + ?x2 = upcast_operand_p (x2);
> + ?if (x2 == NULL_TREE)
> + ? ?goto done;
> + ?y2 = upcast_operand_p (y2);
> + ?if (y2 == NULL_TREE)
> + ? ?goto done;
> +
> + ?*op0 = x2;
> + ?*op1 = y2;
> + ?yes = true;
> +
> + done:
> + ?return yes;
> +}
> +
> +/* Simple wrapper around flow_bb_inside_loop_p that handles NULL
> + ? statements and initial definitions of variables. ?*/
> +
> +static bool
> +stmt_in_loop_p (gimple t, struct loop *loop)
> +{
> + ?basic_block bb;
> +
> + ?if (t == NULL)
> + ? ?return false;
> +
> + ?bb = gimple_bb (t);
> + ?if (bb == NULL)
> + ? ?return false;
> +
> + ?return flow_bb_inside_loop_p (loop, bb);
> +}
> +
> +/* The loop index should have a specific usage pattern:
> +
> + ? - It should be defined in a phi node with two incoming values:
> +
> + ? ? LI_phi = PHI (LI_out, LI_in)
> +
> + ? - One incoming value, LI_out, should be from outside the loop.
> +
> + ? - The other incoming value, LI_in, should be defined thusly:
> +
> + ? ? LI_in = LI_phi + increment
> +
> + ? - increment should be 1. ?We permit other increments with
> + ? ? -funsafe-loop-optimizations.
> +
> + ? - Finally, in the comparison to exit the loop, the loop index must be
> + ? ? compared against a variable that has a type at least as precise as
> + ? ? the loop index's type. ?For instance, something like:
> +
> + ? ? ? char limit;
> + ? ? ? short i;
> +
> + ? ? ? for (i = 0; i < limit; i++) ...
> +
> + ? ? would not be permitted. ?*/
> +
> +static bool
> +analyze_loop_index_definition_pattern (struct promote_info *pi)
> +{
> + ?gimple phi = SSA_NAME_DEF_STMT (pi->loop_index_name);
> + ?bool ok = false, warn = false;
> + ?tree in0, in1;
> + ?bool inside0, inside1;
> + ?gimple def0, def1;
> + ?tree op0, op1, increment = NULL_TREE;
> +
> + ?if (gimple_code (phi) != GIMPLE_PHI
> + ? ? ?|| gimple_phi_num_args (phi) != 2)
> + ? ?goto done;
> +
> + ?in0 = PHI_ARG_DEF (phi, 0);
> + ?in1 = PHI_ARG_DEF (phi, 1);
> +
> + ?/* Figure out which value comes from outside the loop. ?*/
> + ?def0 = TREE_CODE (in0) == SSA_NAME ? SSA_NAME_DEF_STMT (in0) : NULL;
> + ?def1 = TREE_CODE (in1) == SSA_NAME ? SSA_NAME_DEF_STMT (in1) : NULL;
> +
> + ?inside0 = stmt_in_loop_p (def0, pi->loop);
> + ?inside1 = stmt_in_loop_p (def1, pi->loop);
> +
> + ?if (inside0 && inside1)
> + ? ?goto done;
> + ?else if (inside0)
> + ? ?{
> + ? ? ?tree t = in0;
> + ? ? ?gimple g;
> + ? ? ?in0 = in1;
> + ? ? ?in1 = t;
> + ? ? ?g = def0;
> + ? ? ?def0 = def1;
> + ? ? ?def1 = g;
> + ? ?}
> + ?else if (!inside1)
> + ? ?goto done;
> +
> + ?/* IN0 comes from outside the loop, IN1 from inside. ?Analyze IN1. ?*/
> + ?if (gimple_code (def1) != GIMPLE_ASSIGN)
> + ? ?goto done;
> +
> + ?switch (gimple_assign_rhs_code (def1))
> + ? ?{
> + ? ?case CONVERT_EXPR:
> + ? ?case NOP_EXPR:
> + ? ? ?if (!signed_arithmetic_overflow_idiom_p (gimple_assign_rhs1 (def1),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&op0, &op1))
> + ? ? ? goto done;
> + ? ? ?goto plus;
> + ? ?case PLUS_EXPR:
> + ? ? ?op0 = gimple_assign_rhs1 (def1);
> + ? ? ?op1 = gimple_assign_rhs2 (def1);
> + ? ?plus:
> + ? ? ?{
> + ? ? ? bool op0_li = op0 == PHI_RESULT (phi);
> + ? ? ? bool op1_li = op1 == PHI_RESULT (phi);
> + ? ? ? if (op0_li && op1_li)
> + ? ? ? ? /* This is weird, and definitely is not a case we can support
> + ? ? ? ? ? ?for promotion. ?*/
> + ? ? ? ? goto done;
> + ? ? ? else if (op0_li)
> + ? ? ? ? increment = op1;
> + ? ? ? else if (op1_li)
> + ? ? ? ? increment = op0;
> + ? ? ? else
> + ? ? ? ? goto done;
> + ? ? ? break;
> + ? ? ?}
> + ? ?default:
> + ? ? ?break;
> + ? ?}
> +
> +
> + ?/* Check that the exit condition for the loop is OK. ?*/
> + ?{
> + ? ?enum tree_code code = gimple_cond_code (pi->exit_expr);
> +
> + ? ?op0 = gimple_cond_lhs (pi->exit_expr);
> + ? ?op1 = gimple_cond_rhs (pi->exit_expr);
> +
> + ? ?if (op0 == pi->loop_limit)
> + ? ? ?{
> + ? ? ? tree t = op0;
> + ? ? ? op0 = op1;
> + ? ? ? op1 = t;
> + ? ? ? code = swap_tree_comparison (code);
> + ? ? ?}
> +
> + ? ?if (code != LT_EXPR && code != LE_EXPR)
> + ? ? ?goto done;
> +
> + ? ?if (!types_compatible_p (TREE_TYPE (pi->loop_index_name),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_TYPE (pi->loop_limit)))
> + ? ? ?{
> + ? ? ? switch (TREE_CODE (pi->loop_limit))
> + ? ? ? ? {
> + ? ? ? ? case INTEGER_CST:
> + ? ? ? ? ? if (!int_fits_type_p (pi->loop_limit,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TREE_TYPE (pi->loop_index_name)))
> + ? ? ? ? ? ? goto done;
> + ? ? ? ? ? break;
> + ? ? ? ? case SSA_NAME:
> + ? ? ? ? ? {
> + ? ? ? ? ? ? tree v = pi->loop_limit;
> + ? ? ? ? ? ? gimple def = SSA_NAME_DEF_STMT (v);
> +
> + ? ? ? ? ? ? /* Backtrack through CONVERT_EXPRs and/or NOP_EXPRs to
> + ? ? ? ? ? ? ? ?determine if the variables "started out" as the same
> + ? ? ? ? ? ? ? ?type. ?*/
> + ? ? ? ? ? ? while (gimple_code (def) == GIMPLE_ASSIGN)
> + ? ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? ? enum tree_code rhs_code = gimple_assign_rhs_code (def);
> +
> + ? ? ? ? ? ? ? ? if (rhs_code != NOP_EXPR && rhs_code != CONVERT_EXPR)
> + ? ? ? ? ? ? ? ? ? break;
> +
> + ? ? ? ? ? ? ? ? v = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
> + ? ? ? ? ? ? ? ? def = SSA_NAME_DEF_STMT (v);
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? /* Permit comparisons between non-compatible types with
> + ? ? ? ? ? ? ? ?flag_unsafe_loop_optimizations, since we can assume the
> + ? ? ? ? ? ? ? ?loop index does not overflow. ?*/
> + ? ? ? ? ? ? if (types_compatible_p (TREE_TYPE (pi->loop_index_name),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TREE_TYPE (v))
> + ? ? ? ? ? ? ? ? || flag_unsafe_loop_optimizations)
> + ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? /* Fallthrough. ?*/
> + ? ? ? ? ? default:
> + ? ? ? ? ? ? goto done;
> + ? ? ? ? ? }
> + ? ? ? ? }
> + ? ? ?}
> + ?}
> +
> + ?if (increment == NULL_TREE)
> + ? ?goto done;
> + ?if (TREE_CODE (increment) != INTEGER_CST
> + ? ? ?|| compare_tree_int (increment, 1) != 0)
> + ? ?{
> + ? ? ?warn = true;
> + ? ? ?if (!flag_unsafe_loop_optimizations)
> + ? ? ? goto done;
> + ? ?}
> +
> + ?ok = true;
> + done:
> + ?if (warn && !pi->warned)
> + ? ?{
> + ? ? ?pi->warned = true;
> + ? ? ?/* We can promote unsigned indices only if -funsafe-loop-optimizations
> + ? ? ? ?is in effect, since the user might be depending on the modulo
> + ? ? ? ?wraparound behavior of unsigned types. ?*/
> + ? ? ?if (warn_unsafe_loop_optimizations)
> + ? ? ? {
> + ? ? ? ? const char *wording;
> +
> + ? ? ? ? wording = (flag_unsafe_loop_optimizations
> + ? ? ? ? ? ? ? ? ? ?? N_("assuming that the loop counter does not overflow")
> + ? ? ? ? ? ? ? ? ? ?: N_("cannot optimize loop, the loop counter may overflow"));
> + ? ? ? ? warning (OPT_Wunsafe_loop_optimizations, "%s", gettext (wording));
> + ? ? ? }
> + ? ?}
> +
> + ?return ok;
> +}
> +
> +/* Analyze the loop associated with PI_ to see if its loop index can be
> + ? promoted. ?*/
> +
> +static bool
> +analyze_loop (const void *pi_, void *data)
> +{
> + ?struct promote_info *pi = CONST_CAST (struct promote_info *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (const struct promote_info *) pi_);
> + ?bool *changed = (bool *) data;
> +
> + ?/* We previously determined we can't promote this; go ahead and
> + ? ? continue iterating. ?*/
> + ?if (pi->loop_index_name == NULL_TREE)
> + ? ?return true;
> +
> + ?/* Assume we can always promote the loop index, even if it doesn't
> + ? ? exist. ?*/
> + ?pi->can_be_promoted_p = true;
> +
> + ?if (dump_file)
> + ? ?{
> + ? ? ?fprintf (dump_file, "Analyzing ");
> + ? ? ?print_generic_expr (dump_file, pi->loop_index_name, 0);
> + ? ? ?fprintf (dump_file, "\n");
> + ? ?}
> +
> + ?if (pi->loop_index_name
> + ? ? ?&& analyze_loop_index_definition_pattern (pi))
> + ? ?{
> + ? ? ?/* Clear any previously gathered information. ?*/
> + ? ? ?VEC_truncate (tree, pi->cast_types, 0);
> + ? ? ?VEC_truncate (int, pi->cast_counts, 0);
> +
> + ? ? ?walk_use_def_chains (pi->loop_index_name, analyze_loop_index, pi, false);
> + ? ?}
> + ?else
> + ? ?pi->can_be_promoted_p = false;
> +
> + ?/* If we determined the loop index is used in strange ways, clear it
> + ? ? so we don't examine it again. ?*/
> + ?if (!pi->can_be_promoted_p)
> + ? ?pi->loop_index_name = NULL_TREE;
> +
> + ?/* Let our caller know whether to re-do the analysis. ?*/
> + ?*changed = *changed || !pi->can_be_promoted_p;
> + ?/* Continue if PI is promotable. ?*/
> + ?return pi->can_be_promoted_p;
> +}
> +
> +/* Add PI_->LOOP_INDEX_NAME to the set of variables, DATA, that we are
> + ? considering for promotion. ?*/
> +
> +static bool
> +add_variable (const void *pi_, void *data ATTRIBUTE_UNUSED)
> +{
> + ?const struct promote_info *pi = (const struct promote_info *) pi_;
> + ?struct pointer_set_t *pset = (struct pointer_set_t *) data;
> + ?int presentp;
> +
> + ?if (pi->loop_index_name != NULL_TREE)
> + ? ?{
> + ? ? ?presentp = pointer_set_insert (pset, pi->loop_index_name);
> + ? ? ?gcc_assert (!presentp);
> + ? ?}
> +
> + ?/* Continue traversal. ?*/
> + ?return true;
> +}
> +
> +/* For each promotable variable:
> +
> + ? - create a new, promoted VAR_DECL;
> +
> + ? - walk through all the uses and defs and create new statements using
> + ? ? the promoted variables. ?We don't create new phi nodes; post-pass
> + ? ? SSA update will handle those for us. ?*/
> +
> +/* Make dump files readable. ?*/
> +#define PROMOTED_VAR_SUFFIX ".promoted"
> +
> +/* Create a variable NAME with TYPE and do the necessary work to inform
> + ? the SSA machinery about it. ?*/
> +
> +static tree
> +create_pli_var (tree type, char *name)
> +{
> + ?tree var = create_tmp_var (type, name);
> + ?create_var_ann (var);
> + ?mark_sym_for_renaming (var);
> + ?add_referenced_var (var);
> + ?return var;
> +}
> +
> +/* Associate the SSA_NAME VAR with the promoted variable DATA. ?*/
> +
> +static bool
> +associate_name_with_var (tree var, gimple def_stmt, void *data)
> +{
> + ?tree promoted_var = (tree) data;
> + ?void **p;
> +
> + ?gcc_assert (promoted_var != NULL_TREE);
> +
> + ?if (gimple_code (def_stmt) == GIMPLE_PHI)
> + ? ?var = PHI_RESULT (def_stmt);
> +
> + ?p = pointer_map_insert (variable_map, var);
> +
> + ?if (!*p)
> + ? ?{
> + ? ? ?if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "Associating ");
> + ? ? ? ? print_generic_expr (dump_file, var, 0);
> + ? ? ? ? fprintf (dump_file, " with ");
> + ? ? ? ? print_generic_expr (dump_file, promoted_var, 0);
> + ? ? ? ? fprintf (dump_file, "\n\n");
> + ? ? ? }
> + ? ? ?*(tree *)p = promoted_var;
> + ? ?}
> +
> + ?/* Continue traversal. ?*/
> + ?return false;
> +}
> +
> +/* Create a promoted variable for the variable from PI_. ?*/
> +
> +static bool
> +create_promoted_variable (const void *pi_, void *data ATTRIBUTE_UNUSED)
> +{
> + ?struct promote_info *pi = CONST_CAST (struct promote_info *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (const struct promote_info *) pi_);
> +
> + ?if (pi->can_be_promoted_p)
> + ? ?{
> + ? ? ?tree type = choose_profitable_promoted_type (pi);
> + ? ? ?tree orig_name = DECL_NAME (pi->var_decl);
> + ? ? ?size_t id_len = IDENTIFIER_LENGTH (orig_name);
> + ? ? ?size_t name_len = id_len + strlen (PROMOTED_VAR_SUFFIX) + 1;
> + ? ? ?char *name;
> +
> + ? ? ?name = (char *) alloca (name_len);
> + ? ? ?strcpy (name, IDENTIFIER_POINTER (orig_name));
> + ? ? ?strcpy (name + id_len, PROMOTED_VAR_SUFFIX);
> +
> + ? ? ?pi->promoted_type = type;
> + ? ? ?pi->promoted_var = create_pli_var (type, name);
> +
> + ? ? ?if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "Created new variable ");
> + ? ? ? ? print_generic_expr (dump_file, pi->promoted_var, 0);
> + ? ? ? ? fprintf (dump_file, " to stand in for ");
> + ? ? ? ? print_generic_expr (dump_file, pi->loop_index_name, 0);
> + ? ? ? ? fprintf (dump_file, "\n\n");
> + ? ? ? }
> +
> + ? ? ?walk_use_def_chains (pi->loop_index_name,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?associate_name_with_var,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?pi->promoted_var, false);
> + ? ?}
> +
> + ?/* Continue traversal. ?*/
> + ?return true;
> +}
> +
> +/* Rebuild T with newly promoted variables; STMT is the original
> + ? statement in which T appeared and may be equivalent to T. ?TYPE is
> + ? non-null when rebuilding the rhs of a GIMPLE_ASSIGN and indicates the
> + ? type of the lhs. ?*/
> +
> +static tree
> +rebuild_tree_with_promotion (tree t, gimple stmt, tree type,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?gimple_stmt_iterator gsi,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct promote_info *pi)
> +{
> + ?tree op0, op1;
> +
> + ?switch (TREE_CODE (t))
> + ? ?{
> + ? ?case NOP_EXPR:
> + ? ?case CONVERT_EXPR:
> + ? ? ?{
> + ? ? ? tree pvar = rebuild_tree_with_promotion (TREE_OPERAND (t, 0), stmt, type, gsi, pi);
> +
> + ? ? ? if (types_compatible_p (type, TREE_TYPE (pvar)))
> + ? ? ? ? return pvar;
> + ? ? ? else
> + ? ? ? ? return build1 (TREE_CODE (t), type, pvar);
> + ? ? ?}
> + ? ?case INTEGER_CST:
> + ? ? ?{
> + ? ? ? return build_int_cst_wide (pi->promoted_type,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_INT_CST_LOW (t),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_INT_CST_HIGH (t));
> + ? ? ?}
> + ? ?case COND_EXPR:
> + ? ? ?{
> + ? ? ? tree orig_op0 = TREE_OPERAND (t, 0);
> + ? ? ? op0 = rebuild_tree_with_promotion (orig_op0, stmt, type, gsi, pi);
> + ? ? ? gcc_assert (orig_op0 != op0);
> + ? ? ? TREE_OPERAND (t, 0) = op0;
> + ? ? ? return t;
> + ? ? ?}
> + ? ?case PLUS_EXPR:
> + ? ?case MINUS_EXPR:
> + ? ?case MULT_EXPR:
> + ? ? ?type = pi->promoted_type;
> + ? ? ?goto binary_expr;
> + ? ?case EQ_EXPR:
> + ? ?case NE_EXPR:
> + ? ?case LT_EXPR:
> + ? ?case LE_EXPR:
> + ? ?case GT_EXPR:
> + ? ?case GE_EXPR:
> + ? ? ?type = TREE_TYPE (t);
> + ? ?binary_expr:
> + ? ? ?op0 = TREE_OPERAND (t, 0);
> + ? ? ?op1 = TREE_OPERAND (t, 1);
> + ? ? ?op0 = rebuild_tree_with_promotion (op0, stmt, type, gsi, pi);
> + ? ? ?op1 = rebuild_tree_with_promotion (op1, stmt, type, gsi, pi);
> + ? ? ?return build2 (TREE_CODE (t), type, op0, op1);
> + ? ?case SSA_NAME:
> + ? ? ?{
> + ? ? ? void **p = pointer_map_contains (variable_map, t);
> +
> + ? ? ? if (p == NULL)
> + ? ? ? ? {
> + ? ? ? ? ? /* This is unexpected, but it does happen if we were dealing
> + ? ? ? ? ? ? ?with COND_EXPRs and such. ?Just go ahead and create a
> + ? ? ? ? ? ? ?temporary for it. ?*/
> + ? ? ? ? ? if (types_compatible_p (TREE_TYPE (t), pi->promoted_type)
> + ? ? ? ? ? ? ? || SSA_NAME_DEF_STMT (t) == stmt)
> + ? ? ? ? ? ? return t;
> + ? ? ? ? ? else
> + ? ? ? ? ? ? goto insert_cast;
> + ? ? ? ? }
> + ? ? ? else
> + ? ? ? ? return *(tree *)p;
> + ? ? ?}
> + ? ?case VAR_DECL:
> + ? ? ?return t;
> + ? ?default:
> + ? ?insert_cast:
> + ? ? ?{
> + ? ? ? gimple cast;
> + ? ? ? tree tmp, nop;
> + ? ? ? tree to_upcast = t;
> +
> + ? ? ? /* If we are dealing with a memory reference, then we can't have
> + ? ? ? ? ?wrap it in a NOP_EXPR; we need to load the value from memory
> + ? ? ? ? ?first, then convert it. ?*/
> + ? ? ? if (!is_gimple_reg (to_upcast))
> + ? ? ? ? {
> + ? ? ? ? ? tree tmp = create_pli_var (TREE_TYPE (to_upcast),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONST_CAST (char *, "loadtmp"));
> + ? ? ? ? ? gimple stmt = gimple_build_assign (tmp, to_upcast);
> + ? ? ? ? ? gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
> + ? ? ? ? ? to_upcast = tmp;
> + ? ? ? ? }
> +
> + ? ? ? tmp = create_pli_var (pi->promoted_type,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONST_CAST (char *, "promotetmp"));
> + ? ? ? nop = build1 (NOP_EXPR, pi->promoted_type, to_upcast);
> + ? ? ? cast = gimple_build_assign (tmp, nop);
> + ? ? ? if (dump_file)
> + ? ? ? ? {
> + ? ? ? ? ? fprintf (dump_file, "Inserting cast ");
> + ? ? ? ? ? print_gimple_stmt (dump_file, cast, 0, 0);
> + ? ? ? ? ? fprintf (dump_file, " prior to ");
> + ? ? ? ? ? print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? ? }
> + ? ? ? gsi_insert_before (&gsi, cast, GSI_SAME_STMT);
> + ? ? ? return tmp;
> + ? ? ?}
> + ? ?}
> +}
> +
> +/* Rebuild STMT, which contains uses or a def of the promotable variable
> + ? associated with PI. ?*/
> +
> +static void
> +rebuild_with_promotion (gimple stmt, struct promote_info *pi)
> +{
> + ?gimple_stmt_iterator gsi;
> +
> + ?if (pointer_set_insert (promoted_stmts, stmt))
> + ? ?return;
> +
> + ?if (dump_file)
> + ? ?{
> + ? ? ?fprintf (dump_file, "Rebuilding stmt ");
> + ? ? ?print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ?fprintf (dump_file, "\n");
> + ? ?}
> +
> + ?gsi = gsi_for_stmt (stmt);
> +
> + ?switch (gimple_code (stmt))
> + ? ?{
> + ? ?case GIMPLE_ASSIGN:
> + ? ? ?{
> + ? ? ? enum tree_code subcode = gimple_assign_rhs_code (stmt);
> + ? ? ? enum tree_code newcode = subcode;
> + ? ? ? tree lhs = gimple_assign_lhs (stmt);
> + ? ? ? tree rhs1 = gimple_assign_rhs1 (stmt);
> + ? ? ? tree rhs2 = gimple_assign_rhs2 (stmt);
> + ? ? ? tree x, y;
> + ? ? ? void **v;
> +
> + ? ? ? /* If we are defining a promotable variable, check for special
> + ? ? ? ? ?idioms. ?*/
> + ? ? ? v = pointer_map_contains (variable_map, lhs);
> + ? ? ? if (v != NULL
> + ? ? ? ? ? && *(tree *)v == pi->promoted_var
> + ? ? ? ? ? && (subcode == NOP_EXPR || subcode == CONVERT_EXPR)
> + ? ? ? ? ? && signed_arithmetic_overflow_idiom_p (rhs1, &x, &y))
> + ? ? ? ? {
> + ? ? ? ? ? void **xp;
> + ? ? ? ? ? void **yp;
> + ? ? ? ? ? if (TYPE_PRECISION (TREE_TYPE (rhs1))
> + ? ? ? ? ? ? ? >= TYPE_PRECISION (pi->promoted_type))
> + ? ? ? ? ? ? goto done;
> +
> + ? ? ? ? ? /* It's possible that we've already promoted the operands of
> + ? ? ? ? ? ? ?one or both of the NOP_EXPRs. ?In that case, we can
> + ? ? ? ? ? ? ?bypass the logic below and go straight to rebuilding the
> + ? ? ? ? ? ? ?rhs that we really want to transform. ?*/
> + ? ? ? ? ? if (TREE_CODE (x) == VAR_DECL
> + ? ? ? ? ? ? ? || TREE_CODE (y) == VAR_DECL)
> + ? ? ? ? ? ? goto build_fake;
> + ? ? ? ? ? xp = pointer_map_contains (variable_map, x);
> + ? ? ? ? ? yp = pointer_map_contains (variable_map, y);
> +
> + ? ? ? ? ? /* Nothing to see here. ?*/
> + ? ? ? ? ? if (!types_compatible_p (TREE_TYPE (x),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_TYPE (y))
> + ? ? ? ? ? ? ? || (xp == NULL && yp == NULL))
> + ? ? ? ? ? ? goto done;
> + ? ? ? ? ? x = (xp == NULL ? NULL_TREE : *(tree *)xp);
> + ? ? ? ? ? y = (yp == NULL ? NULL_TREE : *(tree *)yp);
> +
> + ? ? ? ? ? if (x != pi->promoted_var && y != pi->promoted_var)
> + ? ? ? ? ? ? goto done;
> +
> + ? ? ? ? build_fake:
> + ? ? ? ? ? newcode = PLUS_EXPR;
> + ? ? ? ? ? rhs1 = x;
> + ? ? ? ? ? rhs2 = y;
> + ? ? ? ? ? if (dump_file)
> + ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? fprintf (dump_file, "Substituting ");
> + ? ? ? ? ? ? ? print_generic_expr (dump_file, x, 0);
> + ? ? ? ? ? ? ? fprintf (dump_file, " + ");
> + ? ? ? ? ? ? ? print_generic_expr (dump_file, y, 0);
> + ? ? ? ? ? ? ? fprintf (dump_file, " for rhs of original statement\n");
> + ? ? ? ? ? ? }
> +
> + ? ? ? ? done:
> + ? ? ? ? ? ;
> + ? ? ? ? }
> +
> + ? ? ? lhs = rebuild_tree_with_promotion (lhs, stmt, NULL, gsi, pi);
> + ? ? ? rhs1 = rebuild_tree_with_promotion (rhs1, stmt, NULL, gsi, pi);
> + ? ? ? if (rhs2)
> + ? ? ? ? rhs2 = rebuild_tree_with_promotion (rhs2, stmt, NULL, gsi, pi);
> +
> + ? ? ? if (newcode != subcode)
> + ? ? ? ? {
> + ? ? ? ? ? gimple newstmt = gimple_build_assign_with_ops (newcode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?lhs, rhs1, rhs2);
> + ? ? ? ? ? gsi_replace (&gsi, newstmt, true);
> + ? ? ? ? ? stmt = newstmt;
> + ? ? ? ? }
> + ? ? ? else
> + ? ? ? ? {
> + ? ? ? ? ? gimple_assign_set_lhs (stmt, lhs);
> + ? ? ? ? ? gimple_assign_set_rhs1 (stmt, rhs1);
> + ? ? ? ? ? if (rhs2)
> + ? ? ? ? ? ? gimple_assign_set_rhs2 (stmt, rhs2);
> + ? ? ? ? }
> + ? ? ?}
> + ? ? ?break;
> + ? ?case GIMPLE_COND:
> + ? ? ?{
> + ? ? ? tree lhs = gimple_cond_lhs (stmt);
> + ? ? ? tree rhs = gimple_cond_rhs (stmt);
> +
> + ? ? ? lhs = rebuild_tree_with_promotion (lhs, stmt, NULL, gsi, pi);
> + ? ? ? rhs = rebuild_tree_with_promotion (rhs, stmt, NULL, gsi, pi);
> +
> + ? ? ? gimple_cond_set_lhs (stmt, lhs);
> + ? ? ? gimple_cond_set_rhs (stmt, rhs);
> + ? ? ?}
> + ? ? ?break;
> + ? ?default:
> + ? ? ?gcc_unreachable ();
> + ? ?}
> +
> + ?if (dump_file)
> + ? ?{
> + ? ? ?fprintf (dump_file, "Converted stmt ");
> + ? ? ?print_gimple_stmt (dump_file, stmt, 0, 0);
> + ? ? ?fprintf (dump_file, "\n\n");
> + ? ?}
> + ?update_stmt (stmt);
> +}
> +
> +/* Helper function for promote_variable that walks over use/def
> + ? chains. ?*/
> +
> +static bool
> +promote_variable_1 (tree var, gimple stmt, void *data)
> +{
> + ?struct promote_info *pi = (struct promote_info *) data;
> + ?imm_use_iterator imi;
> + ?gimple use_stmt;
> +
> + ?/* Due to the way walk_use_def_chains works, when STMT is a PHI_NODE,
> + ? ? VAR is actually an argument to the phi node, not the result of it.
> + ? ? Rebuild uses of the phi node's result after handle integer constant
> + ? ? inputs to the phi node. ?*/
> + ?if (gimple_code (stmt) == GIMPLE_PHI)
> + ? ?{
> + ? ? ?if (TREE_CODE (var) == INTEGER_CST)
> + ? ? ? {
> + ? ? ? ? edge e = loop_preheader_edge (pi->loop);
> + ? ? ? ? basic_block preheader = e->src;
> + ? ? ? ? gimple_stmt_iterator gsi = gsi_last_bb (preheader);
> + ? ? ? ? tree cst = build_int_cst_wide (pi->promoted_type,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_INT_CST_LOW (var),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?TREE_INT_CST_HIGH (var));
> + ? ? ? ? gimple assign = gimple_build_assign (pi->promoted_var, cst);
> + ? ? ? ? gsi_insert_after (&gsi, assign, GSI_NEW_STMT);
> + ? ? ? }
> + ? ? ?var = PHI_RESULT (stmt);
> + ? ?}
> + ?else
> + ? ?rebuild_with_promotion (stmt, pi);
> +
> + ?FOR_EACH_IMM_USE_STMT (use_stmt, imi, var)
> + ? ?{
> + ? ? ?if (gimple_code (use_stmt) != GIMPLE_PHI)
> + ? ? ? rebuild_with_promotion (use_stmt, pi);
> + ? ?}
> +
> + ?return false;
> +}
> +
> +/* Convert all uses and defs of PI_->LOOP_INDEX_NAME as linked by
> + ? use-def chains to uses and defs of PI_->PROMOTED_VAR. ?*/
> +
> +static bool
> +promote_variable (const void *pi_, void *data ATTRIBUTE_UNUSED)
> +{
> + ?const struct promote_info *pi = (const struct promote_info *) pi_;
> +
> + ?if (pi->can_be_promoted_p)
> + ? ?{
> + ? ? ?walk_use_def_chains (pi->loop_index_name, promote_variable_1,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?CONST_CAST (struct promote_info *, pi), false);
> + ? ?}
> +
> + ?/* Continue traversal. ?*/
> + ?return true;
> +}
> +
> +/* Free PI_ and its associated data. ?*/
> +
> +static bool
> +free_pi_entries (const void *pi_, void *data ATTRIBUTE_UNUSED)
> +{
> + ?struct promote_info *pi = CONST_CAST (struct promote_info *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (const struct promote_info *) pi_);
> +
> + ?VEC_free (tree, heap, pi->cast_types);
> + ?VEC_free (int, heap, pi->cast_counts);
> + ?free (pi);
> +
> + ?/* Continue traversal. ?*/
> + ?return true;
> +}
> +
> +/* Collect information about variables that we believe to be loop
> + ? indices in PROMOTION_INFO. ?*/
> +
> +static void
> +collect_promotion_candidates (void)
> +{
> + ?loop_iterator li;
> + ?struct loop *loop;
> +
> + ?FOR_EACH_LOOP (li, loop, 0)
> + ? ?{
> + ? ? ?basic_block header = loop->header;
> + ? ? ?gimple exit_cond = last_stmt (header);
> +
> + ? ? ?if (exit_cond && gimple_code (exit_cond) == GIMPLE_COND)
> + ? ? ? {
> + ? ? ? ? tree loop_index;
> + ? ? ? ? tree limit = NULL_TREE;
> + ? ? ? ? tree decl;
> + ? ? ? ? struct promote_info *pi;
> +
> + ? ? ? ? loop_index = find_promotion_candidate (loop, exit_cond, &limit);
> + ? ? ? ? if (loop_index == NULL_TREE)
> + ? ? ? ? ? continue;
> + ? ? ? ? decl = SSA_NAME_VAR (loop_index);
> + ? ? ? ? if (TREE_ADDRESSABLE (decl))
> + ? ? ? ? ? continue;
> +
> + ? ? ? ? if (dump_file)
> + ? ? ? ? ? {
> + ? ? ? ? ? ? fprintf (dump_file, "Found loop index ");
> + ? ? ? ? ? ? print_generic_expr (dump_file, loop_index, 0);
> + ? ? ? ? ? ? fprintf (dump_file, " involved in ");
> + ? ? ? ? ? ? print_gimple_stmt (dump_file, exit_cond, 0, 0);
> + ? ? ? ? ? ? fprintf (dump_file, "\n\n");
> + ? ? ? ? ? }
> +
> + ? ? ? ? pi = XCNEW (struct promote_info);
> + ? ? ? ? pi->loop = loop;
> + ? ? ? ? pi->exit_expr = exit_cond;
> + ? ? ? ? pi->loop_index_name = loop_index;
> + ? ? ? ? pi->loop_limit = limit;
> + ? ? ? ? pi->var_decl = decl;
> + ? ? ? ? /* We think so, anyway... ?*/
> + ? ? ? ? pi->can_be_promoted_p = true;
> + ? ? ? ? pointer_set_insert (promotion_info, pi);
> + ? ? ? }
> + ? ? ?else if (dump_file)
> + ? ? ? {
> + ? ? ? ? fprintf (dump_file, "\nSkipping analysis of loop %d (header bb #%d)\n",
> + ? ? ? ? ? ? ? ? ?loop->num, loop->header->index);
> + ? ? ? ? if (exit_cond)
> + ? ? ? ? ? {
> + ? ? ? ? ? ? fprintf (dump_file, "Exit condition was ");
> + ? ? ? ? ? ? print_gimple_stmt (dump_file, exit_cond, 0, 0);
> + ? ? ? ? ? ? fprintf (dump_file, "\n");
> + ? ? ? ? ? }
> + ? ? ? }
> + ? ?}
> +}
> +
> +/* Free memory associated with global variables that we used. ?*/
> +
> +static void
> +pli_cleanup (void)
> +{
> + ?if (promoted_stmts)
> + ? ?{
> + ? ? ?pointer_set_destroy (promoted_stmts);
> + ? ? ?promoted_stmts = NULL;
> + ? ?}
> + ?if (variable_map)
> + ? ?{
> + ? ? ?pointer_map_destroy (variable_map);
> + ? ? ?variable_map = NULL;
> + ? ?}
> + ?if (promotable_names)
> + ? ?{
> + ? ? ?pointer_set_destroy (promotable_names);
> + ? ? ?promotable_names = NULL;
> + ? ?}
> + ?if (promotion_info)
> + ? ?{
> + ? ? ?pointer_set_traverse (promotion_info, free_pi_entries, NULL);
> + ? ? ?pointer_set_destroy (promotion_info);
> + ? ? ?promotion_info = NULL;
> + ? ?}
> +}
> +
> +/* The guts of the pass. ?*/
> +
> +static unsigned int
> +promote_short_indices (void)
> +{
> + ?bool did_something = false;
> + ?bool changed;
> + ?size_t max_iterations, i, n_promoted;
> +
> + ?promotion_info = pointer_set_create ();
> + ?collect_promotion_candidates ();
> +
> + ?if (dump_file)
> + ? ?fprintf (dump_file, "Found %d candidates for promotion\n",
> + ? ? ? ? ? ?(int) pointer_set_n_elements (promotion_info));
> +
> + ?/* Nothing to do. ?*/
> + ?if (pointer_set_n_elements (promotion_info) == 0)
> + ? ?goto cleanup;
> +
> + ?/* We have information about which variables are loop index variables.
> + ? ? We now need to determine the promotability of the loop indices.
> + ? ? Since the promotability of loop indices may depend on other loop
> + ? ? indices, we need to repeat this until we reach a fixed point. ?*/
> + ?changed = true;
> + ?max_iterations = pointer_set_n_elements (promotion_info);
> + ?i = 0;
> +
> + ?promotable_names = pointer_set_create ();
> +
> + ?while (changed)
> + ? ?{
> + ? ? ?changed = false;
> + ? ? ?pointer_set_clear (promotable_names);
> + ? ? ?pointer_set_traverse (promotion_info, add_variable,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? promotable_names);
> + ? ? ?n_promoted = pointer_set_n_elements (promotable_names);
> +
> + ? ? ?if (dump_file)
> + ? ? ? fprintf (dump_file, "\nIteration %d, have %d variables to consider\n",
> + ? ? ? ? ? ? ? ?(int) i, (int) n_promoted);
> +
> + ? ? ?if (n_promoted == 0)
> + ? ? ? break;
> + ? ? ?gcc_assert (i < max_iterations);
> + ? ? ?pointer_set_traverse (promotion_info, analyze_loop, &changed);
> + ? ? ?i++;
> + ? ?}
> +
> + ?if (dump_file)
> + ? ?fprintf (dump_file, "Promoting %d variables\n",
> + ? ? ? ? ? ?(int) n_promoted);
> +
> + ?if (n_promoted != 0)
> + ? ?{
> + ? ? ?did_something = true;
> + ? ? ?variable_map = pointer_map_create ();
> + ? ? ?promoted_stmts = pointer_set_create ();
> + ? ? ?pointer_set_traverse (promotion_info, create_promoted_variable, NULL);
> + ? ? ?pointer_set_traverse (promotion_info, promote_variable, NULL);
> + ? ?}
> +
> + cleanup:
> + ?pli_cleanup ();
> + ?return did_something ? TODO_update_ssa : 0;
> +}
> +
> +/* Entry point for the short loop index promotion pass. ?*/
> +
> +static unsigned int
> +tree_short_index_promotion (void)
> +{
> + ?unsigned int changed = 0;
> +
> + ?/* Initialize all the necessary loop infrastructure. ?*/
> + ?loop_optimizer_init (LOOPS_HAVE_PREHEADERS | LOOPS_HAVE_SIMPLE_LATCHES | LOOPS_HAVE_RECORDED_EXITS);
> + ?add_noreturn_fake_exit_edges ();
> + ?connect_infinite_loops_to_exit ();
> +
> + ?if (number_of_loops () > 1)
> + ? ?changed = promote_short_indices ();
> +
> + ?/* Tear down loop optimization infrastructure. ?*/
> + ?remove_fake_exit_edges ();
> + ?free_numbers_of_iterations_estimates ();
> + ?loop_optimizer_finalize ();
> +
> + ?return changed;
> +}
> +
> +static bool
> +gate_short_index_promotion (void)
> +{
> + ?return optimize > 0 && flag_promote_loop_indices;
> +}
> +
> +struct gimple_opt_pass pass_promote_indices =
> +{
> + ?{
> + ? ?GIMPLE_PASS,
> + ? ?"promoteshort", ? ? ? ? ? ? ? ? ? ?/* name */
> + ? ?gate_short_index_promotion, ? ? ? ? ? ? ? ?/* gate */
> + ? ?tree_short_index_promotion, ? ? ? ? ? ? ? ?/* execute */
> + ? ?NULL, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* sub */
> + ? ?NULL, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* next */
> + ? ?0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* static_pass_number */
> + ? ?TV_TREE_LOOP_PROMOTE, ? ? ? ? ? ? ?/* tv_id */
> + ? ?PROP_cfg | PROP_ssa, ? ? ? ? ? ? ? /* properties_required */
> + ? ?0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* properties_provided */
> + ? ?0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* properties_destroyed */
> + ? ?0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* todo_flags_start */
> + ? ?TODO_dump_func | TODO_verify_loops
> + ? ?| TODO_ggc_collect ? ? ? ? ? ? ? ? /* todo_flags_finish */
> + ?}
> +};
> Index: common.opt
> ===================================================================
> --- common.opt ?(revision 146526)
> +++ common.opt ?(working copy)
> @@ -906,6 +906,10 @@ fprofile-values
> ?Common Report Var(flag_profile_values)
> ?Insert code to profile values of expressions
>
> +fpromote-loop-indices
> +Common Report Var(flag_promote_loop_indices) Optimization
> +Promote loop indices to word-sized indices when safe
> +
> ?frandom-seed
> ?Common
>
> Index: Makefile.in
> ===================================================================
> --- Makefile.in (revision 146526)
> +++ Makefile.in (working copy)
> @@ -1250,6 +1250,7 @@ OBJS-common = \
> ? ? ? ?tree-ssa-loop-manip.o \
> ? ? ? ?tree-ssa-loop-niter.o \
> ? ? ? ?tree-ssa-loop-prefetch.o \
> + ? ? ? tree-ssa-loop-promote.o \
> ? ? ? ?tree-ssa-loop-unswitch.o \
> ? ? ? ?tree-ssa-loop.o \
> ? ? ? ?tree-ssa-math-opts.o \
> @@ -2273,6 +2274,12 @@ tree-ssa-loop-prefetch.o: tree-ssa-loop-
> ? ?$(CFGLOOP_H) $(PARAMS_H) langhooks.h $(BASIC_BLOCK_H) hard-reg-set.h \
> ? ?tree-chrec.h $(TOPLEV_H) langhooks.h $(TREE_INLINE_H) $(TREE_DATA_REF_H) \
> ? ?$(OPTABS_H)
> +tree-ssa-loop-promote.o: tree-ssa-loop-promote.c \
> + ? coretypes.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TOPLEV_H) \
> + ? $(RTL_H) $(TM_P_H) hard-reg-set.h $(OBSTACK_H) $(BASIC_BLOCK_H) \
> + ? pointer-set.h intl.h $(TREE_H) $(GIMPLE_H) $(HASHTAB_H) $(DIAGNOSTIC_H) \
> + ? $(TREE_FLOW_H) $(TREE_DUMP_H) $(CFGLOOP_H) $(FLAGS_H) $(TIMEVAR_H) \
> + ? tree-pass.h $(TM_H)
> ?tree-predcom.o: tree-predcom.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TM_P_H) \
> ? ?$(CFGLOOP_H) $(TREE_FLOW_H) $(GGC_H) $(TREE_DATA_REF_H) $(SCEV_H) \
> ? ?$(PARAMS_H) $(DIAGNOSTIC_H) $(TREE_PASS_H) $(TM_H) coretypes.h \
> Index: passes.c
> ===================================================================
> --- passes.c ? ?(revision 146526)
> +++ passes.c ? ?(working copy)
> @@ -553,6 +553,7 @@ init_optimization_passes (void)
> ? ? ? ? ?NEXT_PASS (pass_remove_cgraph_callee_edges);
> ? ? ? ? ?NEXT_PASS (pass_rename_ssa_copies);
> ? ? ? ? ?NEXT_PASS (pass_ccp);
> + ? ? ? ? NEXT_PASS (pass_promote_indices);
> ? ? ? ? ?NEXT_PASS (pass_forwprop);
> ? ? ? ? ?NEXT_PASS (pass_update_address_taken);
> ? ? ? ? ?NEXT_PASS (pass_sra_early);
>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]