On Sun, 27 Sep 2026, Dominic P wrote:

> A load covered by a wider load of the same memory state, as a union, a type
> pun or BB SLP produces, is not value numbered as an extract from it: the
> store side, vn_reference_lookup_3, is the walk_non_aliased_vuses callback,
> which a load never reaches.  Do it at the load: look the naturally aligned
> windows containing it up in the reference table, which matches an entry of
> any type of that size, and build the store case's BIT_FIELD_REF for a
> dominating one.  Only the final FRE does it: an earlier rewrite would hide
> the load from the reference lookups that follow.  It is limited to indirect
> accesses: rewriting a piece of a declaration hides it from ifcombine
> (gcc.dg/field-merge-15.c).
> 
> In a 6502 emulator built with its debugger for arm1176, 1100 ldrb that reload
> the emulated registers after each hook become 825 uxtb and 275 lsr; on a Pi
> Zero the interpreter is 3.5-4 % faster when the hooks run every instruction
> and unchanged when they do not.  x86_64 code is byte-identical.
> 
> Bootstrapped and regression tested on x86_64-pc-linux-gnu, and regression
> tested on arm-none-eabi (cortex-a15) and aarch64-linux-gnu.
> 
> Assisted-by: Claude Opus 5 (Anthropic)
> 
> gcc/ChangeLog:
> 
>       * passes.def: Pass a last_p flag to pass_fre, true for the final one.
>       * tree-ssa-sccvn.cc (vn_last_fre_p): New variable.
>       (free_rpo_vn): Clear it.
>       (vn_reference_lookup_covering_load): New function.
>       (visit_reference_op_load): Use it when the reference lookup fails.
>       (class pass_fre): Add a last_p parameter.
>       (pass_fre::execute): Set vn_last_fre_p from it.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/torture/fre-covering-load.c: New test.
>       * gcc.dg/tree-ssa/ssa-fre-115.c: New test.
>       * gcc.dg/tree-ssa/ssa-fre-116.c: New test.
>       * gcc.dg/tree-ssa/ssa-fre-117.c: New test.
> 
> Signed-off-by: Dominic P <[email protected]>
> ---
> OK for trunk?
> 
>  gcc/passes.def                                |  10 +-
>  .../gcc.dg/torture/fre-covering-load.c        | 143 ++++++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c   |  40 +++++
>  gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-116.c   |  45 ++++++
>  gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-117.c   |  21 +++
>  gcc/tree-ssa-sccvn.cc                         | 126 ++++++++++++++-
>  6 files changed, 376 insertions(+), 9 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/torture/fre-covering-load.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-116.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-117.c
> 
> diff --git a/gcc/passes.def b/gcc/passes.def
> index 9095c134f49..9cd29c55c70 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -92,7 +92,7 @@ along with GCC; see the file COPYING3.  If not see
>         NEXT_PASS (pass_build_ealias);
>         /* Do phiprop before FRE so we optimize std::min and std::max well.  
> */
>         NEXT_PASS (pass_phiprop);
> -       NEXT_PASS (pass_fre, true /* may_iterate */);
> +       NEXT_PASS (pass_fre, true /* may_iterate */, false /* last_p */);
>         NEXT_PASS (pass_early_vrp);
>         NEXT_PASS (pass_sccopy);
>         NEXT_PASS (pass_merge_phi);
> @@ -131,7 +131,7 @@ along with GCC; see the file COPYING3.  If not see
>         NEXT_PASS (pass_oacc_kernels);
>         PUSH_INSERT_PASSES_WITHIN (pass_oacc_kernels)
>             NEXT_PASS (pass_ch);
> -           NEXT_PASS (pass_fre, true /* may_iterate */);
> +           NEXT_PASS (pass_fre, true /* may_iterate */, false /* last_p */);
>             /* We use pass_lim to rewrite in-memory iteration and reduction
>                variable accesses in loops into local variables accesses.  */
>             NEXT_PASS (pass_lim);
> @@ -231,7 +231,7 @@ along with GCC; see the file COPYING3.  If not see
>        execute TODO_rebuild_alias at this point.  */
>        NEXT_PASS (pass_build_alias);
>        NEXT_PASS (pass_return_slot);
> -      NEXT_PASS (pass_fre, true /* may_iterate */);
> +      NEXT_PASS (pass_fre, true /* may_iterate */, false /* last_p */);
>        NEXT_PASS (pass_merge_phi);
>        NEXT_PASS (pass_thread_jumps_full, /*first=*/true);
>        NEXT_PASS (pass_vrp, false /* final_p*/);
> @@ -327,7 +327,7 @@ along with GCC; see the file COPYING3.  If not see
>         NEXT_PASS (pass_complete_unroll);
>         NEXT_PASS (pass_pre_slp_scalar_cleanup);
>         PUSH_INSERT_PASSES_WITHIN (pass_pre_slp_scalar_cleanup)
> -           NEXT_PASS (pass_fre, false /* may_iterate */);
> +           NEXT_PASS (pass_fre, false /* may_iterate */, false /* last_p */);
>             NEXT_PASS (pass_dse);
>         POP_INSERT_PASSES ()
>         NEXT_PASS (pass_slp_vectorize);
> @@ -352,7 +352,7 @@ along with GCC; see the file COPYING3.  If not see
>        NEXT_PASS (pass_reassoc, false /* early_p */);
>        NEXT_PASS (pass_strength_reduction);
>        NEXT_PASS (pass_tracer);
> -      NEXT_PASS (pass_fre, false /* may_iterate */);
> +      NEXT_PASS (pass_fre, false /* may_iterate */, true /* last_p */);
>        /* After late FRE we rewrite no longer addressed locals into SSA
>           form if possible.  */
>        NEXT_PASS (pass_thread_jumps, /*first=*/false);
> diff --git a/gcc/testsuite/gcc.dg/torture/fre-covering-load.c 
> b/gcc/testsuite/gcc.dg/torture/fre-covering-load.c
> new file mode 100644
> index 00000000000..f47d6dec795
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/fre-covering-load.c
> @@ -0,0 +1,143 @@
> +/* { dg-do run } */
> +/* { dg-options "-Wno-psabi" } */
> +
> +/* Each check compares a plain narrow access, which the covering-load rewrite
> +   replaces, against a volatile access to the same bytes, which it cannot
> +   touch.  The oracle is therefore never the value under test, and reading
> +   both through the same type keeps every check endian-independent.  */
> +
> +#define REF(T, p, off) (*(volatile T *) ((char *) (p) + (off)))
> +
> +typedef unsigned char u8;
> +typedef unsigned short u16;
> +typedef unsigned int u32;
> +typedef unsigned long long u64;
> +typedef unsigned char v4qi __attribute__((vector_size (4)));
> +
> +struct R { u8 a, x, y, p; };
> +static struct R g = { 0x11, 0x22, 0x33, 0x44 };
> +static u64 g8 = 0x8102030405060788ULL;
> +static float gf = -1.5f;
> +static u32 g4[2] = { 0x89abcdefu, 0x01234567u };
> +
> +__attribute__((noipa)) static void sink (u32 a, u32 b, u32 c, u32 d)
> +{ (void) a; (void) b; (void) c; (void) d; }
> +
> +__attribute__((noipa)) static void vsink (v4qi v) { (void) v; }
> +
> +__attribute__((const, noipa)) static int cst (int i) { return i; }
> +
> +__attribute__((noipa)) static int
> +word_cover (struct R *r)
> +{
> +  u32 w = *(u32 *) r;
> +  u8 a = r->a, x = r->x, y = r->y, p = r->p;
> +  sink (w, a, x, y);
> +  return a == REF (u8, r, 0) && x == REF (u8, r, 1)
> +      && y == REF (u8, r, 2) && p == REF (u8, r, 3);
> +}
> +
> +__attribute__((noipa)) static int
> +vec_cover (struct R *r)
> +{
> +  v4qi v = *(v4qi *) r;
> +  u8 a = r->a, x = r->x, y = r->y, p = r->p;
> +  vsink (v);
> +  return a == REF (u8, r, 0) && x == REF (u8, r, 1)
> +      && y == REF (u8, r, 2) && p == REF (u8, r, 3);
> +}
> +
> +__attribute__((noipa)) static int
> +half_cover (u32 *q)
> +{
> +  u32 w = *q;
> +  u16 h0 = ((u16 *) q)[0], h1 = ((u16 *) q)[1];
> +  sink (w, 0, 0, 0);
> +  return h0 == REF (u16, q, 0) && h1 == REF (u16, q, 2);
> +}
> +
> +__attribute__((noipa)) static int
> +wide64 (u64 *q)
> +{
> +  u64 w = *q;
> +  signed char s0 = ((signed char *) q)[0];
> +  signed char s7 = ((signed char *) q)[7];
> +  u32 w4 = ((u32 *) q)[1];
> +  sink (w, 0, 0, 0);
> +  return s0 == REF (signed char, q, 0) && s7 == REF (signed char, q, 7)
> +      && w4 == REF (u32, q, 4);
> +}
> +
> +__attribute__((noipa)) static int
> +float_cover (float *q)
> +{
> +  float f = *q;
> +  u8 b1 = ((u8 *) q)[1], b3 = ((u8 *) q)[3];
> +  sink ((u32) (int) f, 0, 0, 0);
> +  return b1 == REF (u8, q, 1) && b3 == REF (u8, q, 3);
> +}
> +
> +/* A const call has no VDEF, so the rewrite fires across it; the memory it
> +   reads cannot have changed.  */
> +__attribute__((noipa)) static int
> +const_call_between (struct R *r)
> +{
> +  u32 w = *(u32 *) r;
> +  int k = cst (3);
> +  u8 a = r->a;
> +  sink (w, k, 0, 0);
> +  return a == REF (u8, r, 0) && k == 3;
> +}
> +
> +/* The store gives a new memory state, so the byte must be re-read.  */
> +__attribute__((noipa)) static int
> +store_between (struct R *r)
> +{
> +  u32 w = *(u32 *) r;
> +  r->x = 0x55;
> +  u8 x = r->x;
> +  sink (w, 0, 0, 0);
> +  return x == 0x55 && x == REF (u8, r, 1);
> +}
> +
> +__attribute__((noipa)) static int
> +volatile_narrow (struct R *r)
> +{
> +  u32 w = *(u32 *) r;
> +  r->a = 0x77;
> +  sink (w, 0, 0, 0);
> +  return REF (u8, r, 0) == 0x77;
> +}
> +
> +#ifdef __SIZEOF_INT128__
> +__attribute__((noipa)) static int
> +ptr_out_of_int128 (void *p)
> +{
> +  unsigned __int128 w = *(unsigned __int128 *) p;
> +  void *q = ((void **) p)[1];
> +  sink ((u32) w, 0, 0, 0);
> +  return q == REF (void *, p, sizeof (void *));
> +}
> +static void *gp[4] __attribute__((aligned (16)))
> +  = { (void *) 1, (void *) 2, (void *) 3, (void *) 4 };
> +#endif
> +
> +int
> +main (void)
> +{
> +  if (!word_cover (&g)) __builtin_abort ();
> +  if (!vec_cover (&g)) __builtin_abort ();
> +  if (!half_cover (g4)) __builtin_abort ();
> +  if (!wide64 (&g8)) __builtin_abort ();
> +  if (!float_cover (&gf)) __builtin_abort ();
> +  if (!const_call_between (&g)) __builtin_abort ();
> +  if (!store_between (&g)) __builtin_abort ();
> +  g.a = 0x11;
> +  if (!volatile_narrow (&g)) __builtin_abort ();
> +#ifdef __SIZEOF_INT128__
> +  if (sizeof (void *) * 2 == sizeof (unsigned __int128)
> +      && !ptr_out_of_int128 (gp))
> +    __builtin_abort ();
> +#endif
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
> new file mode 100644
> index 00000000000..fbcef78a685
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
> @@ -0,0 +1,40 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target int32 } */
> +/* { dg-options "-O2 -Wno-psabi -fdump-tree-fre5" } */
> +
> +struct R { unsigned char a, x, y, p; };
> +typedef unsigned char v4qi __attribute__((vector_size (4)));
> +
> +void use (unsigned, unsigned, unsigned, unsigned);
> +void usev (v4qi);
> +
> +unsigned
> +int_cover (struct R *r)
> +{
> +  unsigned w = *(unsigned *) r;
> +  use (r->a, r->x, r->y, r->p);
> +  return w;
> +}
> +
> +void
> +vec_cover (struct R *r)
> +{
> +  v4qi v = *(v4qi *) r;
> +  use (r->a, r->x, r->y, r->p);
> +  usev (v);
> +}
> +
> +/* The covering load is in a dominating block, not the same one.  */
> +unsigned
> +cross_bb (struct R *r, int c)
> +{
> +  unsigned w = *(unsigned *) r;
> +  if (c)
> +    use (r->a, 0, 0, 0);
> +  return w;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "->a;" "fre5" } } */
> +/* { dg-final { scan-tree-dump-not "->x;" "fre5" } } */
> +/* { dg-final { scan-tree-dump-not "->y;" "fre5" } } */
> +/* { dg-final { scan-tree-dump-not "->p;" "fre5" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-116.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-116.c
> new file mode 100644
> index 00000000000..a4906c8165c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-116.c
> @@ -0,0 +1,45 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-fre5" } */
> +
> +struct R { unsigned char a, x, y, p; };
> +
> +unsigned
> +store_between (struct R *r, unsigned char *q)
> +{
> +  unsigned w = *(unsigned *) r;
> +  *q = 7;
> +  return w + r->a;
> +}
> +
> +unsigned
> +call_between (struct R *r, void (*f) (void))
> +{
> +  unsigned w = *(unsigned *) r;
> +  f ();
> +  return w + r->a;
> +}
> +
> +unsigned
> +volatile_narrow (struct R *r)
> +{
> +  unsigned w = *(unsigned *) r;
> +  return w + *(volatile unsigned char *) &r->a;
> +}
> +
> +unsigned
> +volatile_wide (struct R *r)
> +{
> +  unsigned w = *(volatile unsigned *) r;
> +  return w + r->a;
> +}
> +
> +unsigned
> +not_covered (struct R *r)
> +{
> +  unsigned short h = *(unsigned short *) &r->x;
> +  return h + *(unsigned *) r;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "->a;" 3 "fre5" } } */
> +/* { dg-final { scan-tree-dump-times " ={v} " 2 "fre5" } } */
> +/* { dg-final { scan-tree-dump-not "BIT_FIELD_REF" "fre5" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-117.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-117.c
> new file mode 100644
> index 00000000000..f8558de90d6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-117.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target int32 } */
> +/* { dg-options "-O2 -fdump-tree-fre5" } */
> +
> +typedef unsigned char v4qi __attribute__((vector_size (4)));
> +struct R { unsigned char a, x, y, p; };
> +
> +/* Both reads of the byte are value numbered as the one extract from the
> +   covering vector load.  */
> +unsigned
> +f (struct R *r, v4qi *o)
> +{
> +  v4qi w = *(v4qi *) r;
> +  unsigned a1 = r->a;
> +  unsigned a2 = r->a;
> +  *o = w;
> +  return a1 + a2;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "->a;" "fre5" } } */
> +/* { dg-final { scan-tree-dump-times "BIT_FIELD_REF" 1 "fre5" } } */
> diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
> index c496ff93d11..7254ee172db 100644
> --- a/gcc/tree-ssa-sccvn.cc
> +++ b/gcc/tree-ssa-sccvn.cc
> @@ -142,6 +142,9 @@ along with GCC; see the file COPYING3.  If not see
>  
>  static vn_lookup_kind default_vn_walk_kind;
>  
> +/* True while the final FRE of a function is value numbering.  */
> +static bool vn_last_fre_p;
> +
>  /* vn_nary_op hashtable helpers.  */
>  
>  struct vn_nary_op_hasher : nofree_ptr_hash <vn_nary_op_s>
> @@ -6225,6 +6228,110 @@ visit_reference_op_call (tree lhs, gcall *stmt)
>    return changed;
>  }
>  
> +/* Value number the load OP of STMT as an extract from a wider load of the
> +   same memory state.  The candidates are the naturally aligned windows that
> +   contain OP; each is looked up in the reference table, which matches an
> +   entry of any type of that size.  Return the value or NULL_TREE.
> +
> +   The result is an extract, not a reference, so an earlier rewrite would
> +   hide the load from the reference lookups of the FREs that follow
> +   (gcc.dg/tree-ssa/foldconst-3.c).  */
> +
> +static tree
> +vn_reference_lookup_covering_load (tree op, gimple *stmt)
> +{
> +  if (!vn_last_fre_p)
> +    return NULL_TREE;
> +
> +  poly_int64 offset, size, maxsize;
> +  bool reverse;
> +  tree base = get_ref_base_and_extent (op, &offset, &size, &maxsize, 
> &reverse);
> +  HOST_WIDE_INT offseti, sizei;
> +  if (reverse
> +      || !known_size_p (maxsize)
> +      || maybe_ne (maxsize, size)
> +      || !offset.is_constant (&offseti)
> +      || !size.is_constant (&sizei)
> +      || sizei % BITS_PER_UNIT != 0
> +      || (INTEGRAL_TYPE_P (TREE_TYPE (op))
> +       && sizei != TYPE_PRECISION (TREE_TYPE (op))))
> +    return NULL_TREE;
> +
> +  /* Only for an indirect access: a declaration is addressed directly, so
> +     re-reading a piece of it costs no more than extracting that piece, and
> +     rewriting it hides the access from the passes that match on it.  */

?

> +  offset_int boff;
> +  if (TREE_CODE (base) != MEM_REF
> +      || !mem_ref_offset (base).is_constant (&boff))
> +    return NULL_TREE;
> +  tree ptr = TREE_OPERAND (base, 0);
> +  tree alias_type = TREE_TYPE (TREE_OPERAND (base, 1));
> +
> +  offset_int abs = boff * BITS_PER_UNIT + offseti;
> +  if (!wi::fits_shwi_p (abs))
> +    return NULL_TREE;
> +  HOST_WIDE_INT absi = abs.to_shwi ();
> +
> +  tree vuse = gimple_vuse (stmt);
> +  tree wref = NULL_TREE;
> +  scalar_int_mode wmode;
> +  for (unsigned bits = BITS_PER_UNIT * 2;
> +       int_mode_for_size (bits, 0).exists (&wmode); bits *= 2)
> +    {

This seems like an awful way to work around the inherent inability
to peek for a possibly available wider load.  So definitely NACK,
it's a hack in its current form.

If you look at DOM it has one nice property that avoids most of the
costly alias-walks when finiding a _store_ - it hashes expressions
without the virtual operand, so the expression table acts as an
optimistic one that has to be verified by extra manual work.

Of course vn_reference_lookup_3 can look through aggregate copies
and more, so optimistic hashing isn't generally possible.  But this
idea could at least be leveraged for "covered loads", disregarding
the more fancy tricks of vn_reference_lookup_3 but still allowing
some VN_WALK ...

> +      if (bits <= (unsigned HOST_WIDE_INT) sizei)
> +     continue;
> +      HOST_WIDE_INT woff = absi & ~(HOST_WIDE_INT) (bits - 1);
> +      if (absi + sizei > woff + (HOST_WIDE_INT) bits)
> +     continue;
> +      tree wtype = lang_hooks.types.type_for_mode (wmode, 1);
> +      if (!wtype)
> +     continue;
> +
> +      /* One node, retyped per window: a fresh one per probe is a GC cost.  
> */
> +      tree woffset = wide_int_to_tree (alias_type, woff / BITS_PER_UNIT);
> +      if (!wref)
> +     wref = build2 (MEM_REF, wtype, ptr, woffset);
> +      else
> +     {
> +       TREE_TYPE (wref) = wtype;
> +       TREE_OPERAND (wref, 1) = woffset;
> +     }
> +      tree wval = vn_reference_lookup (wref, vuse, VN_NOWALK, NULL, true);

... and w/ VN_NOWALK hashing in the VUSE would work as well as working
more in the line of store-merging and not as part of value-numbering.
Gather all loads with the same VUSE, sort them and associate/simplify
so the covering loads are first and the extracts are last.

So that's my suggestion - don't perform this as part of value-numbering.
Instead it looks more similar to store-merging and it could be done
as part of that pass.

> +      if (!wval
> +       || (INTEGRAL_TYPE_P (TREE_TYPE (wval))
> +           && !type_has_mode_precision_p (TREE_TYPE (wval))))
> +     continue;
> +      if (TREE_CODE (wval) == SSA_NAME)
> +     {
> +       gimple *def = SSA_NAME_DEF_STMT (wval);
> +       if (!SSA_NAME_IS_DEFAULT_DEF (wval)
> +           && (!gimple_bb (def)
> +               || !dominated_by_p (CDI_DOMINATORS, vn_context_bb,
> +                                   gimple_bb (def))))
> +         continue;
> +     }
> +
> +      gimple_match_op mop (gimple_match_cond::UNCOND, BIT_FIELD_REF,
> +                        TREE_TYPE (op), wval, bitsize_int (sizei),
> +                        bitsize_int (absi - woff));
> +      tree val = vn_nary_build_or_lookup (&mop);
> +      if (val
> +       && (TREE_CODE (val) != SSA_NAME
> +           || !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (val)))
> +     {
> +       if (dump_file && (dump_flags & TDF_DETAILS))
> +         {
> +           fprintf (dump_file, "Using covering load of ");
> +           print_generic_expr (dump_file, wval);
> +           fprintf (dump_file, "\n");
> +         }
> +       return val;
> +     }
> +    }
> +
> +  return NULL_TREE;
> +}
> +
>  /* Visit a load from a reference operator RHS, part of STMT, value number it,
>     and return true if the value number of the LHS has changed as a result.  
> */
>  
> @@ -6238,6 +6345,8 @@ visit_reference_op_load (tree lhs, tree op, gimple 
> *stmt)
>    tree vuse = gimple_vuse (stmt);
>    tree last_vuse = vuse;
>    result = vn_reference_lookup (op, vuse, default_vn_walk_kind, &res, true, 
> &last_vuse);
> +  if (!result)
> +    result = vn_reference_lookup_covering_load (op, stmt);
>  
>    /* We handle type-punning through unions by value-numbering based
>       on offset and size of the access.  Be prepared to handle a
> @@ -6256,7 +6365,8 @@ visit_reference_op_load (tree lhs, tree op, gimple 
> *stmt)
>         gimple_match_op res_op (gimple_match_cond::UNCOND,
>                                 VIEW_CONVERT_EXPR, TREE_TYPE (op), result);
>         result = vn_nary_build_or_lookup (&res_op);
> -       if (result
> +       if (res
> +           && result
>             && TREE_CODE (result) == SSA_NAME
>             && VN_INFO (result)->needs_insertion)
>           /* Track whether this is the canonical expression for different
> @@ -8133,6 +8243,7 @@ run_rpo_vn (vn_lookup_kind kind)
>  void
>  free_rpo_vn (void)
>  {
> +  vn_last_fre_p = false;
>    free_vn_table (valid_info);
>    XDELETE (valid_info);
>    obstack_free (&vn_tables_obstack, NULL);
> @@ -9338,15 +9449,20 @@ class pass_fre : public gimple_opt_pass
>  {
>  public:
>    pass_fre (gcc::context *ctxt)
> -    : gimple_opt_pass (pass_data_fre, ctxt), may_iterate (true)
> +    : gimple_opt_pass (pass_data_fre, ctxt), may_iterate (true), last_p 
> (false)
>    {}
>  
>    /* opt_pass methods: */
>    opt_pass * clone () final override { return new pass_fre (m_ctxt); }
>    void set_pass_param (unsigned int n, bool param) final override
>      {
> -      gcc_assert (n == 0);
> -      may_iterate = param;
> +      if (n == 0)
> +     may_iterate = param;
> +      else
> +     {
> +       gcc_assert (n == 1);
> +       last_p = param;
> +     }
>      }
>    bool gate (function *) final override
>      {
> @@ -9356,6 +9472,7 @@ public:
>  
>  private:
>    bool may_iterate;
> +  bool last_p;
>  }; // class pass_fre
>  
>  unsigned int
> @@ -9369,6 +9486,7 @@ pass_fre::execute (function *fun)
>    if (iterate_p)
>      loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
>  
> +  vn_last_fre_p = last_p;
>    todo = do_rpo_vn_1 (fun, NULL, NULL, iterate_p, true, false, 
> VN_WALKREWRITE);
>    free_rpo_vn ();
>  
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to