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.