On Fri, 17 Sep 2021, Richard Biener wrote: > On Fri, 17 Sep 2021, Richard Sandiford wrote: > > > Richard Biener <rguent...@suse.de> writes: > > > This adds the capability to analyze the dependence of mixed > > > pointer/array accesses. The example is from where using a masked > > > load/store creates the pointer-based access when an otherwise > > > unconditional access is array based. Other examples would include > > > accesses to an array mixed with accesses from inlined helpers > > > that work on pointers. > > > > > > The idea is quite simple and old - analyze the data-ref indices > > > as if the reference was pointer-based. The following change does > > > this by changing dr_analyze_indices to work on the indices > > > sub-structure and storing an alternate indices substructure in > > > each data reference. That alternate set of indices is analyzed > > > lazily by initialize_data_dependence_relation when it fails to > > > match-up the main set of indices of two data references. > > > initialize_data_dependence_relation is refactored into a head > > > and a tail worker and changed to work on one of the indices > > > structures and thus away from using DR_* access macros which > > > continue to reference the main indices substructure. > > > > > > There are quite some vectorization and loop distribution opportunities > > > unleashed in SPEC CPU 2017, notably 520.omnetpp_r, 548.exchange2_r, > > > 510.parest_r, 511.povray_r, 521.wrf_r, 526.blender_r, 527.cam4_r and > > > 544.nab_r see amendments in what they report with -fopt-info-loop while > > > the rest of the specrate set sees no changes there. Measuring runtime > > > for the set where changes were reported reveals nothing off-noise > > > besides 511.povray_r which seems to regress slightly for me > > > (on a Zen2 machine with -Ofast -march=native). > > > > > > Changes from the [RFC] version are properly handling bitfields > > > that we cannot take the address of and optimization of refs > > > that already are MEM_REFs and thus won't see any change. I've > > > also elided changing the set of vect_masked_stores targets in > > > favor of explicitely listing avx (but I did not verify if the > > > testcase passes on aarch64-sve or amdgcn). > > > > > > The improves cases like the following from Povray: > > > > > > for(i = 0; i < Sphere_Sweep->Num_Modeling_Spheres; i++) > > > { > > > VScaleEq(Sphere_Sweep->Modeling_Sphere[i].Center, Vector[X]); > > > Sphere_Sweep->Modeling_Sphere[i].Radius *= Vector[X]; > > > } > > > > > > where there is a plain array access mixed with abstraction > > > using T[] or T* arguments. That should be a not too uncommon > > > situation in the wild. The loop above is now vectorized and was not > > > without the change. > > > > > > Bootstrapped and tested on x86_64-unknown-linux-gnu and I've > > > built and run SPEC CPU 2017 successfully. > > > > > > OK? > > > > Took a while to page this stuff back in :-/ > > > > I guess if we're adding alt_indices to the main data_reference, > > we'll need to free the access_fns in free_data_ref. It looked like > > the patch as posted might have a memory leak. > > Doh, yes - thanks for noticing. > > > Perhaps instead we could use local indices structures for the > > alt_indices and pass them in to initialize_data_dependence_relation? > > Not that that's very elegant… > > Yeah, I had that but then since for N data references we possibly > call initialize_data_dependence_relation N^2 times we'd do this > alternate analysis N^2 times at worst instead of N so it looked worth > caching it in the data reference. Since we have no idea why the > first try fails we're going to try this fallback in the majority > of cases that we cannot figure out otherwise so I didn't manage > to hand-wave the quadraticness away ;) OTOH one might argue > it's just a constant factor ontop of the number of > initialize_data_dependence_relation invocations. > > So I can probably be convinced either way - guess I'll gather > some statistics.
I built SPEC 2017 CPU rate with -Ofast -march=znver2, overall there are 4433976 calls to the first stage initialize_data_dependence_relation (skipping the cases dr_may_alias returned false) 360248 (8%) ended up recursing with a set of alt_indices 83512 times we computed alt_indices of a DR (that's with the cache) 14905 (0.3%) times the recursive invocation ended with != chrec_dont_know thus when not doing the caching we'd compute alt_indices about 10 times more often. I didn't collect the number of distinct DRs (that's difficult at this point), but I'd estimate from the above that we have 3 times more "unused" alt_indices than used. OK, so that didn't really help me avoid flipping a coin ;) Richard. > Richard. > > > > > > Thanks, > > > Richard. > > > > > > 2021-09-08 Richard Biener <rguent...@suse.de> > > > > > > PR tree-optimization/65206 > > > * tree-data-ref.h (struct data_reference): Add alt_indices, > > > order it last. > > > * tree-data-ref.c (dr_analyze_indices): Work on > > > struct indices and get DR_REF as tree. > > > (create_data_ref): Adjust. > > > (initialize_data_dependence_relation): Split into head > > > and tail. When the base objects fail to match up try > > > again with pointer-based analysis of indices. > > > * tree-vectorizer.c (vec_info_shared::check_datarefs): Do > > > not compare the lazily computed alternate set of indices. > > > > > > * gcc.dg/torture/20210916.c: New testcase. > > > * gcc.dg/vect/pr65206.c: Likewise. > > > --- > > > gcc/testsuite/gcc.dg/torture/20210916.c | 20 +++ > > > gcc/testsuite/gcc.dg/vect/pr65206.c | 22 +++ > > > gcc/tree-data-ref.c | 173 ++++++++++++++++-------- > > > gcc/tree-data-ref.h | 9 +- > > > gcc/tree-vectorizer.c | 3 +- > > > 5 files changed, 167 insertions(+), 60 deletions(-) > > > create mode 100644 gcc/testsuite/gcc.dg/torture/20210916.c > > > create mode 100644 gcc/testsuite/gcc.dg/vect/pr65206.c > > > > > > diff --git a/gcc/testsuite/gcc.dg/torture/20210916.c > > > b/gcc/testsuite/gcc.dg/torture/20210916.c > > > new file mode 100644 > > > index 00000000000..0ea6d45e463 > > > --- /dev/null > > > +++ b/gcc/testsuite/gcc.dg/torture/20210916.c > > > @@ -0,0 +1,20 @@ > > > +/* { dg-do compile } */ > > > + > > > +typedef union tree_node *tree; > > > +struct tree_base { > > > + unsigned : 1; > > > + unsigned lang_flag_2 : 1; > > > +}; > > > +struct tree_type { > > > + tree main_variant; > > > +}; > > > +union tree_node { > > > + struct tree_base base; > > > + struct tree_type type; > > > +}; > > > +tree finish_struct_t, finish_struct_x; > > > +void finish_struct() > > > +{ > > > + for (; finish_struct_t->type.main_variant;) > > > + finish_struct_x->base.lang_flag_2 = 0; > > > +} > > > diff --git a/gcc/testsuite/gcc.dg/vect/pr65206.c > > > b/gcc/testsuite/gcc.dg/vect/pr65206.c > > > new file mode 100644 > > > index 00000000000..3b6262622c0 > > > --- /dev/null > > > +++ b/gcc/testsuite/gcc.dg/vect/pr65206.c > > > @@ -0,0 +1,22 @@ > > > +/* { dg-do compile } */ > > > +/* { dg-require-effective-target vect_double } */ > > > +/* { dg-additional-options "-fno-trapping-math > > > -fno-allow-store-data-races" } */ > > > +/* { dg-additional-options "-mavx" { target avx } } */ > > > + > > > +#define N 1024 > > > + > > > +double a[N], b[N]; > > > + > > > +void foo () > > > +{ > > > + for (int i = 0; i < N; ++i) > > > + if (b[i] < 3.) > > > + a[i] += b[i]; > > > +} > > > + > > > +/* We get a .MASK_STORE because while the load of a[i] does not trap > > > + the store would introduce store data races. Make sure we still > > > + can handle the data dependence with zero distance. */ > > > + > > > +/* { dg-final { scan-tree-dump-not "versioning for alias required" > > > "vect" { target { vect_masked_store || avx } } } } */ > > > +/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" { > > > target { vect_masked_store || avx } } } } */ > > > diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c > > > index e061baa7c20..3656b7ba80e 100644 > > > --- a/gcc/tree-data-ref.c > > > +++ b/gcc/tree-data-ref.c > > > @@ -99,6 +99,7 @@ along with GCC; see the file COPYING3. If not see > > > #include "internal-fn.h" > > > #include "vr-values.h" > > > #include "range-op.h" > > > +#include "tree-ssa-loop-ivopts.h" > > > > > > static struct datadep_stats > > > { > > > @@ -1300,22 +1301,18 @@ base_supports_access_fn_components_p (tree base) > > > DR, analyzed in LOOP and instantiated before NEST. */ > > > > > > static void > > > -dr_analyze_indices (struct data_reference *dr, edge nest, loop_p loop) > > > +dr_analyze_indices (struct indices *dri, tree ref, edge nest, loop_p > > > loop) > > > { > > > - vec<tree> access_fns = vNULL; > > > - tree ref, op; > > > - tree base, off, access_fn; > > > - > > > /* If analyzing a basic-block there are no indices to analyze > > > and thus no access functions. */ > > > if (!nest) > > > { > > > - DR_BASE_OBJECT (dr) = DR_REF (dr); > > > - DR_ACCESS_FNS (dr).create (0); > > > + dri->base_object = ref; > > > + dri->access_fns.create (0); > > > return; > > > } > > > > > > - ref = DR_REF (dr); > > > + vec<tree> access_fns = vNULL; > > > > > > /* REALPART_EXPR and IMAGPART_EXPR can be handled like accesses > > > into a two element array with a constant index. The base is > > > @@ -1338,8 +1335,8 @@ dr_analyze_indices (struct data_reference *dr, edge > > > nest, loop_p loop) > > > { > > > if (TREE_CODE (ref) == ARRAY_REF) > > > { > > > - op = TREE_OPERAND (ref, 1); > > > - access_fn = analyze_scalar_evolution (loop, op); > > > + tree op = TREE_OPERAND (ref, 1); > > > + tree access_fn = analyze_scalar_evolution (loop, op); > > > access_fn = instantiate_scev (nest, loop, access_fn); > > > access_fns.safe_push (access_fn); > > > } > > > @@ -1370,16 +1367,16 @@ dr_analyze_indices (struct data_reference *dr, > > > edge nest, loop_p loop) > > > analyzed nest, add it as an additional independent access-function. > > > */ > > > if (TREE_CODE (ref) == MEM_REF) > > > { > > > - op = TREE_OPERAND (ref, 0); > > > - access_fn = analyze_scalar_evolution (loop, op); > > > + tree op = TREE_OPERAND (ref, 0); > > > + tree access_fn = analyze_scalar_evolution (loop, op); > > > access_fn = instantiate_scev (nest, loop, access_fn); > > > if (TREE_CODE (access_fn) == POLYNOMIAL_CHREC) > > > { > > > - tree orig_type; > > > tree memoff = TREE_OPERAND (ref, 1); > > > - base = initial_condition (access_fn); > > > - orig_type = TREE_TYPE (base); > > > + tree base = initial_condition (access_fn); > > > + tree orig_type = TREE_TYPE (base); > > > STRIP_USELESS_TYPE_CONVERSION (base); > > > + tree off; > > > split_constant_offset (base, &base, &off); > > > STRIP_USELESS_TYPE_CONVERSION (base); > > > /* Fold the MEM_REF offset into the evolutions initial > > > @@ -1424,7 +1421,7 @@ dr_analyze_indices (struct data_reference *dr, edge > > > nest, loop_p loop) > > > base, memoff); > > > MR_DEPENDENCE_CLIQUE (ref) = MR_DEPENDENCE_CLIQUE (old); > > > MR_DEPENDENCE_BASE (ref) = MR_DEPENDENCE_BASE (old); > > > - DR_UNCONSTRAINED_BASE (dr) = true; > > > + dri->unconstrained_base = true; > > > access_fns.safe_push (access_fn); > > > } > > > } > > > @@ -1436,8 +1433,8 @@ dr_analyze_indices (struct data_reference *dr, edge > > > nest, loop_p loop) > > > build_int_cst (reference_alias_ptr_type (ref), 0)); > > > } > > > > > > - DR_BASE_OBJECT (dr) = ref; > > > - DR_ACCESS_FNS (dr) = access_fns; > > > + dri->base_object = ref; > > > + dri->access_fns = access_fns; > > > } > > > > > > /* Extracts the alias analysis information from the memory reference DR. > > > */ > > > @@ -1497,7 +1494,7 @@ create_data_ref (edge nest, loop_p loop, tree > > > memref, gimple *stmt, > > > > > > dr_analyze_innermost (&DR_INNERMOST (dr), memref, > > > nest != NULL ? loop : NULL, stmt); > > > - dr_analyze_indices (dr, nest, loop); > > > + dr_analyze_indices (&dr->indices, DR_REF (dr), nest, loop); > > > dr_analyze_alias (dr); > > > > > > if (dump_file && (dump_flags & TDF_DETAILS)) > > > @@ -3066,41 +3063,30 @@ access_fn_components_comparable_p (tree ref_a, > > > tree ref_b) > > > TREE_TYPE (TREE_OPERAND (ref_b, 0))); > > > } > > > > > > -/* Initialize a data dependence relation between data accesses A and > > > - B. NB_LOOPS is the number of loops surrounding the references: the > > > - size of the classic distance/direction vectors. */ > > > +/* Initialize a data dependence relation RES in LOOP_NEST. > > > USE_ALT_INDICES > > > + is true when the main indices of A and B were not comparable so we > > > try again > > > + with alternate indices computed on an indirect reference. */ > > > > > > struct data_dependence_relation * > > > -initialize_data_dependence_relation (struct data_reference *a, > > > - struct data_reference *b, > > > - vec<loop_p> loop_nest) > > > +initialize_data_dependence_relation (struct data_dependence_relation > > > *res, > > > + vec<loop_p> loop_nest, > > > + bool use_alt_indices) > > > { > > > - struct data_dependence_relation *res; > > > + struct data_reference *a = DDR_A (res); > > > + struct data_reference *b = DDR_B (res); > > > unsigned int i; > > > > > > - res = XCNEW (struct data_dependence_relation); > > > - DDR_A (res) = a; > > > - DDR_B (res) = b; > > > - DDR_LOOP_NEST (res).create (0); > > > - DDR_SUBSCRIPTS (res).create (0); > > > - DDR_DIR_VECTS (res).create (0); > > > - DDR_DIST_VECTS (res).create (0); > > > - > > > - if (a == NULL || b == NULL) > > > + struct indices *indices_a = &a->indices; > > > + struct indices *indices_b = &b->indices; > > > + if (use_alt_indices) > > > { > > > - DDR_ARE_DEPENDENT (res) = chrec_dont_know; > > > - return res; > > > + if (TREE_CODE (DR_REF (a)) != MEM_REF) > > > + indices_a = &a->alt_indices; > > > + if (TREE_CODE (DR_REF (b)) != MEM_REF) > > > + indices_b = &b->alt_indices; > > > } > > > > BTW, I didn't audit this to check that every DR_* macro access had > > been updated :-) > > > > > - > > > - /* If the data references do not alias, then they are independent. */ > > > - if (!dr_may_alias_p (a, b, loop_nest.exists () ? loop_nest[0] : NULL)) > > > - { > > > - DDR_ARE_DEPENDENT (res) = chrec_known; > > > - return res; > > > - } > > > - > > > - unsigned int num_dimensions_a = DR_NUM_DIMENSIONS (a); > > > - unsigned int num_dimensions_b = DR_NUM_DIMENSIONS (b); > > > + unsigned int num_dimensions_a = indices_a->access_fns.length (); > > > + unsigned int num_dimensions_b = indices_b->access_fns.length (); > > > if (num_dimensions_a == 0 || num_dimensions_b == 0) > > > { > > > DDR_ARE_DEPENDENT (res) = chrec_dont_know; > > > @@ -3125,9 +3111,9 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > > > > the a and b accesses have a single ARRAY_REF component reference [0] > > > but have two subscripts. */ > > > - if (DR_UNCONSTRAINED_BASE (a)) > > > + if (indices_a->unconstrained_base) > > > num_dimensions_a -= 1; > > > - if (DR_UNCONSTRAINED_BASE (b)) > > > + if (indices_b->unconstrained_base) > > > num_dimensions_b -= 1; > > > > > > /* These structures describe sequences of component references in > > > @@ -3210,6 +3196,10 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > B: [3, 4] (i.e. s.e) */ > > > while (index_a < num_dimensions_a && index_b < num_dimensions_b) > > > { > > > + /* The alternate indices form always has a single dimension > > > + with unconstrained base. */ > > > + gcc_assert (!use_alt_indices); > > > + > > > /* REF_A and REF_B must be one of the component access types > > > allowed by dr_analyze_indices. */ > > > gcc_checking_assert (access_fn_component_p (ref_a)); > > > @@ -3280,11 +3270,12 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > /* See whether FULL_SEQ ends at the base and whether the two bases > > > are equal. We do not care about TBAA or alignment info so we can > > > use OEP_ADDRESS_OF to avoid false negatives. */ > > > - tree base_a = DR_BASE_OBJECT (a); > > > - tree base_b = DR_BASE_OBJECT (b); > > > + tree base_a = indices_a->base_object; > > > + tree base_b = indices_b->base_object; > > > bool same_base_p = (full_seq.start_a + full_seq.length == > > > num_dimensions_a > > > && full_seq.start_b + full_seq.length == num_dimensions_b > > > - && DR_UNCONSTRAINED_BASE (a) == DR_UNCONSTRAINED_BASE (b) > > > + && (indices_a->unconstrained_base > > > + == indices_b->unconstrained_base) > > > && operand_equal_p (base_a, base_b, OEP_ADDRESS_OF) > > > && (types_compatible_p (TREE_TYPE (base_a), > > > TREE_TYPE (base_b)) > > > @@ -3323,7 +3314,7 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > both lvalues are distinct from the object's declared type. */ > > > if (same_base_p) > > > { > > > - if (DR_UNCONSTRAINED_BASE (a)) > > > + if (indices_a->unconstrained_base) > > > full_seq.length += 1; > > > } > > > else > > > @@ -3332,8 +3323,42 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > /* Punt if we didn't find a suitable sequence. */ > > > if (full_seq.length == 0) > > > { > > > - DDR_ARE_DEPENDENT (res) = chrec_dont_know; > > > - return res; > > > + if (use_alt_indices > > > + || !param_data_dep_alt_indices > > > > Didn't look like the patch has a definition of this. Did you decide > > to add a --param, or to ditch an earlier one? > > > > > + || (TREE_CODE (DR_REF (a)) == MEM_REF > > > + && TREE_CODE (DR_REF (b)) == MEM_REF) > > > > Might be a daft question, but do we gain anything by doing this > > when neither reference is a MEM_REF? I.e. could the && be a !=? > > > > Looks OK to me otherwise. > > > > Thanks, > > Richard > > > > > + || may_be_nonaddressable_p (DR_REF (a)) > > > + || may_be_nonaddressable_p (DR_REF (b))) > > > + { > > > + /* Fully exhausted possibilities. */ > > > + DDR_ARE_DEPENDENT (res) = chrec_dont_know; > > > + return res; > > > + } > > > + > > > + /* Try evaluating both DRs as dereferences of pointers. */ > > > + if (!a->alt_indices.base_object > > > + && TREE_CODE (DR_REF (a)) != MEM_REF) > > > + { > > > + tree alt_ref = build2 (MEM_REF, TREE_TYPE (DR_REF (a)), > > > + build1 (ADDR_EXPR, ptr_type_node, DR_REF (a)), > > > + build_int_cst > > > + (reference_alias_ptr_type (DR_REF (a)), 0)); > > > + dr_analyze_indices (&a->alt_indices, alt_ref, > > > + loop_preheader_edge (loop_nest[0]), > > > + loop_containing_stmt (DR_STMT (a))); > > > + } > > > + if (!b->alt_indices.base_object > > > + && TREE_CODE (DR_REF (b)) != MEM_REF) > > > + { > > > + tree alt_ref = build2 (MEM_REF, TREE_TYPE (DR_REF (b)), > > > + build1 (ADDR_EXPR, ptr_type_node, DR_REF (b)), > > > + build_int_cst > > > + (reference_alias_ptr_type (DR_REF (b)), 0)); > > > + dr_analyze_indices (&b->alt_indices, alt_ref, > > > + loop_preheader_edge (loop_nest[0]), > > > + loop_containing_stmt (DR_STMT (b))); > > > + } > > > + return initialize_data_dependence_relation (res, loop_nest, true); > > > } > > > > > > if (!same_base_p) > > > @@ -3381,8 +3406,8 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > struct subscript *subscript; > > > > > > subscript = XNEW (struct subscript); > > > - SUB_ACCESS_FN (subscript, 0) = DR_ACCESS_FN (a, full_seq.start_a + > > > i); > > > - SUB_ACCESS_FN (subscript, 1) = DR_ACCESS_FN (b, full_seq.start_b + > > > i); > > > + SUB_ACCESS_FN (subscript, 0) = > > > indices_a->access_fns[full_seq.start_a + i]; > > > + SUB_ACCESS_FN (subscript, 1) = > > > indices_b->access_fns[full_seq.start_b + i]; > > > SUB_CONFLICTS_IN_A (subscript) = conflict_fn_not_known (); > > > SUB_CONFLICTS_IN_B (subscript) = conflict_fn_not_known (); > > > SUB_LAST_CONFLICT (subscript) = chrec_dont_know; > > > @@ -3393,6 +3418,40 @@ initialize_data_dependence_relation (struct > > > data_reference *a, > > > return res; > > > } > > > > > > +/* Initialize a data dependence relation between data accesses A and > > > + B. NB_LOOPS is the number of loops surrounding the references: the > > > + size of the classic distance/direction vectors. */ > > > + > > > +struct data_dependence_relation * > > > +initialize_data_dependence_relation (struct data_reference *a, > > > + struct data_reference *b, > > > + vec<loop_p> loop_nest) > > > +{ > > > + data_dependence_relation *res = XCNEW (struct > > > data_dependence_relation); > > > + DDR_A (res) = a; > > > + DDR_B (res) = b; > > > + DDR_LOOP_NEST (res).create (0); > > > + DDR_SUBSCRIPTS (res).create (0); > > > + DDR_DIR_VECTS (res).create (0); > > > + DDR_DIST_VECTS (res).create (0); > > > + > > > + if (a == NULL || b == NULL) > > > + { > > > + DDR_ARE_DEPENDENT (res) = chrec_dont_know; > > > + return res; > > > + } > > > + > > > + /* If the data references do not alias, then they are independent. */ > > > + if (!dr_may_alias_p (a, b, loop_nest.exists () ? loop_nest[0] : NULL)) > > > + { > > > + DDR_ARE_DEPENDENT (res) = chrec_known; > > > + return res; > > > + } > > > + > > > + return initialize_data_dependence_relation (res, loop_nest, false); > > > +} > > > + > > > + > > > /* Frees memory used by the conflict function F. */ > > > > > > static void > > > diff --git a/gcc/tree-data-ref.h b/gcc/tree-data-ref.h > > > index 685f33d85ae..74f579c9f3f 100644 > > > --- a/gcc/tree-data-ref.h > > > +++ b/gcc/tree-data-ref.h > > > @@ -166,14 +166,19 @@ struct data_reference > > > and runs to completion. */ > > > bool is_conditional_in_stmt; > > > > > > + /* Alias information for the data reference. */ > > > + struct dr_alias alias; > > > + > > > /* Behavior of the memory reference in the innermost loop. */ > > > struct innermost_loop_behavior innermost; > > > > > > /* Subscripts of this data reference. */ > > > struct indices indices; > > > > > > - /* Alias information for the data reference. */ > > > - struct dr_alias alias; > > > + /* Alternate subscripts initialized lazily and used by data-dependence > > > + analysis only when the main indices of two DRs are not comparable. > > > + Keep last to keep vec_info_shared::check_datarefs happy. */ > > > + struct indices alt_indices; > > > }; > > > > > > #define DR_STMT(DR) (DR)->stmt > > > diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c > > > index 3aa3e2a6783..20daa31187d 100644 > > > --- a/gcc/tree-vectorizer.c > > > +++ b/gcc/tree-vectorizer.c > > > @@ -507,7 +507,8 @@ vec_info_shared::check_datarefs () > > > return; > > > gcc_assert (datarefs.length () == datarefs_copy.length ()); > > > for (unsigned i = 0; i < datarefs.length (); ++i) > > > - if (memcmp (&datarefs_copy[i], datarefs[i], sizeof (data_reference)) > > > != 0) > > > + if (memcmp (&datarefs_copy[i], datarefs[i], > > > + offsetof (data_reference, alt_indices)) != 0) > > > gcc_unreachable (); > > > } > > > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)