https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65656

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org

--- Comment #6 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
I don't think this change works properly though.
Consider say -O2 -std=c++11 testcase from tree-ssa-ccp.c:
template <typename T> class generic_wide_int;
template <int N> struct fixed_wide_int_storage;
typedef generic_wide_int <fixed_wide_int_storage <(((128 + 64) / 64) * 64)> >
widest_int;
namespace wi {
  enum precision_type { FLEXIBLE_PRECISION, VAR_PRECISION, CONST_PRECISION };
  template <typename T> struct int_traits;
  template <typename T> unsigned int get_precision (const T &);
  template <typename T1, typename T2, enum precision_type P1 = int_traits
<T1>::precision_type, enum precision_type P2 = int_traits <T2>::precision_type>
struct binary_traits;
  template <typename T> struct unary_traits : public binary_traits <T, T> {};
  template <typename T1, typename T2> struct binary_traits <T1, T2,
CONST_PRECISION, CONST_PRECISION> {
    typedef generic_wide_int <fixed_wide_int_storage <int_traits
<T1>::precision> > result_type;
  };
  template <typename T1, typename T2> bool geu_p (const T1 &, const T2);
  template <typename T1, typename T2> typename wi::unary_traits
<T1>::result_type lrshift (const T1 &, const T2 &);
  struct storage_ref {
    storage_ref (const long *, unsigned int, unsigned int);
    const long *val;
    unsigned int len;
    unsigned int precision;
    unsigned int get_len () const;
    unsigned int get_precision () const;
    const long *get_val () const;
  };
  inline::wi::storage_ref::storage_ref (const long *val_in, unsigned int
len_in, unsigned int precision_in) : val (val_in), len (len_in), precision
(precision_in) {}
  inline unsigned int wi::storage_ref::get_len () const { return len; }
  inline unsigned int wi::storage_ref::get_precision () const { return
precision; }
  inline const long * wi::storage_ref::get_val () const { return val; }
}
template <typename storage> class generic_wide_int : public storage {
 public:
  generic_wide_int ();
  template <typename T> generic_wide_int (const T &);
  unsigned long to_uhwi () const;
};
template <typename storage> template <typename T> inline generic_wide_int
<storage>::generic_wide_int (const T &x) : storage (x) {}
namespace wi {
  template <typename storage> struct int_traits <generic_wide_int <storage> > :
public wi::int_traits <storage> {
    static unsigned int get_precision (const generic_wide_int <storage> &);
    static wi::storage_ref decompose (long *, unsigned int, const
generic_wide_int <storage> &);
  };
  template <typename storage> inline wi::storage_ref wi::int_traits
<generic_wide_int <storage> >::decompose (long *, unsigned int precision, const
generic_wide_int <storage> &x) {
    return wi::storage_ref (x.get_val (), x.get_len (), precision);
  }
}
template <bool SE> struct wide_int_ref_storage : public wi::storage_ref {
  long scratch[2];
  template <typename T> wide_int_ref_storage (const T &);
};
template <bool SE> template <typename T> inline wide_int_ref_storage
<SE>::wide_int_ref_storage (const T &x) : storage_ref (wi::int_traits
<T>::decompose (scratch, wi::get_precision (x), x)) {}
template <int N> struct fixed_wide_int_storage {
  long val[(N + 64 + 1) / 64];
  unsigned int len;
  unsigned int get_precision () const;
  const long *get_val () const;
  unsigned int get_len () const;
};
template <int N> inline unsigned int fixed_wide_int_storage <N>::get_precision
() const { return N; }
template <int N> inline const long * fixed_wide_int_storage <N>::get_val ()
const { return val; }
template <int N> inline unsigned int fixed_wide_int_storage <N>::get_len ()
const { return len; }
template <typename storage> inline unsigned int wi::int_traits
<generic_wide_int <storage> >::get_precision (const generic_wide_int <storage>
& x) { return x.get_precision (); }
namespace wi {
  template <int N> struct int_traits <fixed_wide_int_storage <N> > {
    static const enum precision_type precision_type = CONST_PRECISION;
    static const bool is_sign_extended = true;
    static const unsigned int precision = N;
  };
}
template <typename T> inline unsigned int wi::get_precision (const T &x) {
return wi::int_traits <T>::get_precision (x); }
void bar ();
template <typename T1, typename T2> inline typename wi::unary_traits
<T1>::result_type wi::lrshift (const T1 &x, const T2 &y) {
  generic_wide_int <wide_int_ref_storage <wi::int_traits
<T1>::is_sign_extended> > xi (x);
  generic_wide_int <wide_int_ref_storage <wi::int_traits
<T2>::is_sign_extended> > yi (y);
  if (geu_p (yi, xi.precision)) {
    unsigned int shift = yi.to_uhwi ();
    if ((__builtin_constant_p (xi.precision > 64) && (xi.precision > 64)) ?
(shift < 64 && xi.len == 1 && xi.val[0] >= 0) : xi.precision <= 64) {
      bar ();
    }
  }
}
void foo (widest_int *a, widest_int &b, widest_int &c) {
  *a = wi::lrshift (b, c);
}

It doesn't seem that anything is used in constexpr context, and when compiled
with g++ 5.x we end up after all inlining with conditional call
if (shift < 64 && xi.len == 1 && xi.val[0] >= 0) bar ();
But with trunk, __builtin_constant_p (xi.precision > 64) seems to be folded to
0 prematurely and thus we end up with if (xi.precision <= 64) that is soon
folded into if (0) because after early inlining xi.precision is known to be
192.
I see fold_builtin_constant_p being called first during cp_fold, which ensures
that it is not optimized into 0, and then from fold_non_dependent_expr (when
tsubst* instantiates the condition), which uses constexpr.c stuff and folds it
to 0, despite not being in constexpr function nor e.g. resolved in constexpr
context.

Reply via email to