[Bug c++/106921] [11/12/13 Regression] -O1 and -fipa-icf -fpartial-inlining causes wrong code since r11-4987-g602c6cfc79ce4ae6
marxin at gcc dot gnu.org
gcc-bugzilla@gcc.gnu.org
Wed Dec 28 14:43:56 GMT 2022
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106921
Martin Liška <marxin at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |hubicka at gcc dot gnu.org
--- Comment #5 from Martin Liška <marxin at gcc dot gnu.org> ---
I've created a bit reduced test-case:
cat pr106921.c
#include <array>
#include <cstddef>
#include <exception>
template <size_t Bits>
class bitset {
private:
using word_t = size_t;
static constexpr size_t bits_per_word = sizeof(word_t) * 8;
public:
void foo(size_t n) const {
{
if (n > Bits)
std::terminate();
size_t i = 0;
for (; n > bits_per_word; n -= bits_per_word, i++)
{
__builtin_printf ("words[0]=%x, expected=%x\n", words_[i], ~word_t{});
if (words_[i] != ~word_t{0})
__builtin_abort ();
}
}
}
void fill() noexcept
{
for (auto& word : words_) {
word = ~word_t{0};
}
}
private:
std::array<word_t, 2> words_{};
};
volatile int X = 0;
int main()
{
bitset<1> bitset1;
bitset1.foo(1);
bitset<66> bitset2;
bitset2.fill();
bitset2.foo(65);
return 0;
}
So what happens? First, a split part is created and ICF merges the functions:
void bitset<1>::_ZNK6bitsetILm1EE3fooEm.part.0 (const struct bitset * const
this, size_t n)
{
size_t i;
const value_type & D.14201;
const value_type & D.14200;
long unsigned int _3;
long unsigned int _4;
<bb 7> [local count: 1073741824]:
goto <bb 5>; [100.00%]
<bb 2> [local count: 0]:
_3 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
__builtin_printf ("words[0]=%x, expected=%x\n", _3, 18446744073709551615);
_4 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
if (_4 != 18446744073709551615)
goto <bb 3>; [0.00%]
else
goto <bb 4>; [100.00%]
<bb 3> [count: 0]:
__builtin_abort ();
<bb 4> [local count: 0]:
n_6 = n_5 + 18446744073709551552;
i_7 = i_2 + 1;
<bb 5> [local count: 1073741824]:
# n_5 = PHI <n_6(4), n_8(D)(7)>
# i_2 = PHI <i_7(4), 0(7)>
if (n_5 > 64)
goto <bb 2>; [0.00%]
else
goto <bb 6>; [100.00%]
<bb 6> [local count: 1073741824]:
return;
}
void bitset<66>::_ZNK6bitsetILm66EE3fooEm.part.0 (const struct bitset * const
this, size_t n)
{
size_t i;
const value_type & D.14216;
const value_type & D.14215;
long unsigned int _3;
long unsigned int _4;
<bb 7> [local count: 536870913]:
goto <bb 5>; [100.00%]
<bb 2> [local count: 536870913]:
_3 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
__builtin_printf ("words[0]=%x, expected=%x\n", _3, 18446744073709551615);
_4 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
if (_4 != 18446744073709551615)
goto <bb 3>; [0.00%]
else
goto <bb 4>; [100.00%]
<bb 3> [count: 0]:
__builtin_abort ();
<bb 4> [local count: 536870913]:
n_6 = n_5 + 18446744073709551552;
i_7 = i_2 + 1;
<bb 5> [local count: 1073741824]:
# n_5 = PHI <n_6(4), n_8(D)(7)>
# i_2 = PHI <i_7(4), 0(7)>
if (n_5 > 64)
goto <bb 2>; [50.00%]
else
goto <bb 6>; [50.00%]
<bb 6> [local count: 536870913]:
return;
}
I don't see there any problem, later on, the functions are inlined back and we
end up with the following in a-pr106921.c.094t.fixup_cfg3:
;; Function bitset<1>::_ZNK6bitsetILm1EE3fooEm.part.0
(_ZNK6bitsetILm1EE3fooEm.part.0, funcdef_no=539, decl_uid=14195,
cgraph_uid=120, symbol_order=148) (executed once)
void bitset<1>::_ZNK6bitsetILm1EE3fooEm.part.0 (const struct bitset * const
this, size_t n)
{
size_t i;
const value_type & D.14201;
const value_type & D.14200;
long unsigned int _3;
long unsigned int _4;
<bb 7> [local count: 1073741824]:
goto <bb 5>; [100.00%]
<bb 2> [local count: 0]:
_3 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
__builtin_printf ("words[0]=%x, expected=%x\n", _3, 18446744073709551615);
_4 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
if (_4 != 18446744073709551615)
goto <bb 3>; [0.00%]
else
goto <bb 4>; [100.00%]
<bb 3> [count: 0]:
__builtin_abort ();
<bb 4> [local count: 0]:
n_6 = n_5 + 18446744073709551552;
i_7 = i_2 + 1;
<bb 5> [local count: 1073741824]:
# n_5 = PHI <n_6(4), n_8(D)(7)>
# i_2 = PHI <i_7(4), 0(7)>
if (n_5 > 64)
goto <bb 2>; [0.00%]
else
goto <bb 6>; [100.00%]
<bb 6> [local count: 1073741824]:
return;
}
;; Function main (main, funcdef_no=525, decl_uid=13979, cgraph_uid=106,
symbol_order=122) (executed once)
int main ()
{
value_type * __for_begin;
struct bitset bitset2;
struct bitset bitset1;
<bb 2> [local count: 357878152]:
bitset1 = {};
bitset<1>::_ZNK6bitsetILm1EE3fooEm.part.0 (&bitset1, 1);
bitset2 = {};
goto <bb 4>; [100.00%]
<bb 3> [local count: 715863673]:
MEM[(long unsigned int &)__for_begin_6] = 18446744073709551615;
__for_begin_7 = __for_begin_6 + 8;
<bb 4> [local count: 1073741824]:
# __for_begin_6 = PHI <&MEM[(struct array *)&bitset2]._M_elems(2),
__for_begin_7(3)>
if (&MEM <struct bitset> [(void *)&bitset2 + 16B] != __for_begin_6)
goto <bb 3>; [66.67%]
else
goto <bb 5>; [33.33%]
<bb 5> [local count: 357878152]:
bitset<66>::_ZNK6bitsetILm66EE3fooEm.part.0 (&bitset2, 65);
bitset1 ={v} {CLOBBER(eol)};
bitset2 ={v} {CLOBBER(eol)};
return 0;
}
Which seems correct as the abort guard is based on:
_4 = MEM <const struct array> [(const value_type &)this_1(D)]._M_elems[i_2];
if (_4 != 18446744073709551615)
goto <bb 3>; [0.00%]
else
goto <bb 4>; [100.00%]
<bb 3> [count: 0]:
__builtin_abort ();
which is equivalent to MEM <const struct array> [(const value_type
&)this_1(D)]._M_elems[0] != -1 for n > 64.
For some reason, probably due to corrupted aliasing info, we eventually
optimize out the 'if (_4 != 18446744073709551615)'
statement and we end up with abort.
@Honza, may I ask you for a help, please?
More information about the Gcc-bugs
mailing list