Hi! As mentioned in the PR, while get_computation_at calls unshare_aff_combination and through that unshares the individual expressions, we then call aff_combination_to_tree which uses the fold-const.c APIs, and those do not guarantee the same non-shareable tree isn't used multiple times in the result, e.g. (a ? b : c) + d can be folded into a ? b + d : c + d and when say d is (cast) &whatever that will appear multiple times in the result. For debug stmts we are putting these expressions directly into the IL and so need to make sure there is no invalid tree sharing. As since my patch we can try to compute the expressions even in cases where it won't be then used in the end, the following patch does the unsharing only immediately before putting it into the IL, and removes unsharing in get_debug_computation_at because it will be unshared anyway later.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-10-30 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/92262 * tree-ssa-loop-ivopts.c (get_debug_computation_at): Don't unshare ubase or cbase here. (remove_unused_ivs): Unshare comp before using it. * g++.dg/opt/pr92262.C: New test. --- gcc/tree-ssa-loop-ivopts.c.jj 2019-10-23 14:35:42.994753407 +0200 +++ gcc/tree-ssa-loop-ivopts.c 2019-10-29 09:45:47.418217336 +0100 @@ -4152,8 +4152,6 @@ get_debug_computation_at (class loop *lo var = fold_convert (ctype, var); } - ubase = unshare_expr (ubase); - cbase = unshare_expr (cbase); if (stmt_after_increment (loop, cand, at)) var = fold_build2 (MINUS_EXPR, TREE_TYPE (var), var, unshare_expr (cstep)); @@ -7648,6 +7646,7 @@ remove_unused_ivs (struct ivopts_data *d if (!best_cand) continue; + comp = unshare_expr (comp); if (count > 1) { tree vexpr = make_node (DEBUG_EXPR_DECL); --- gcc/testsuite/g++.dg/opt/pr92262.C.jj 2019-10-29 11:20:46.822901945 +0100 +++ gcc/testsuite/g++.dg/opt/pr92262.C 2019-10-29 11:20:13.956410318 +0100 @@ -0,0 +1,85 @@ +// PR tree-optimization/92262 +// { dg-do compile { target c++11 } } +// { dg-options "-O2 -ftree-loop-distribution -g" } + +struct A; +struct B { template <typename T> using b = T *; }; +template <typename, typename T> using c = B::b<T>; +void *operator new (__SIZE_TYPE__, void *p) { return p; } +struct C { + template <typename T, typename... U> void + foo (T *x, U... y) { new (x) T(y...); } +}; +template <typename> class D : public C {}; +template <typename> struct E; +template <typename T> struct E<D<T>> { + using e = D<T>; + template <typename U> using f = D<U>; + template <typename U, typename... V> + static void + bar (e x, U y, V... z) { x.foo (y, z...); } +}; +template <typename T> struct F : E<T> { + template <typename U> struct G { typedef typename E<T>::template f<U> O; }; +}; +template <typename T, typename U, typename V> void +baz (T x, U y, V z) +{ + F<V>::bar (z, y, *x); +} +struct H { + typedef c<int, A> I; + typedef c<int, I> J; + I i; + J j; + void qux (J x) { j = x; } +}; +template <typename> +struct K { + K(D<A> x) : k (x) {} + typedef H::J L; + struct M { L m; H n, o; }; + struct N : F<D<int>>::G<A>::O, M { N (F<D>::G<A>::O); }; + void quux (); + N k; +}; +template <typename T> +void +K<T>::quux () +{ + L K (k.m - 1); + k.n.qux (K); +} +template <typename, typename = int> +struct P : K<int> { + template <typename T> + P (T x, T, D<A> y = D<A> ()) : K (y) { corge (x); } + template <typename T> void corge (T); + typedef L L; +}; +template <typename T, typename U> +template <typename V> +void P<T, U>::corge (V y) +{ + quux (); + for (L x = k.n.j; x < k.o.j; ++x) + { + ++y; + D<int> pv; + baz (y, *x, pv); + } + D<int> z; + baz (y, k.o.i, z); +} +struct A { + A (int x) : a (x) {} + int a; +}; +int a[2]{}; + +int +main () +{ + P<int> (a, a); + return 0; +} Jakub