Description of problem: Some unit tests of MongoDB are failing now with new gcc6. Here is a upstream bug - https://jira.mongodb.org/browse/SERVER-22756 With removed std::bind and using lambda instead of it, this test passes. However with replaced: auto&& scheduleResult = _scheduleDbWorkFn(stdx::bind( &CollectionCloner::_insertDocumentsCallback, this, stdx::placeholders::_1, lastBatch) by this: auto&& scheduleResult = _scheduleDbWorkFn(stdx::bind( &CollectionCloner::_insertDocumentsCallback, this, lastBatch, stdx::placeholders::_1)); also work. But we think that first order of arguments is right. So it might be a bug in libstdc++ in gcc6. For more information see - https://jira.mongodb.org/browse/SERVER-22756?focusedCommentId=1191056&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-1191056 Version-Release number of selected component (if applicable): gcc-6.0.0-0.13.fc24
Is stdx::bind std::bind? Please provide preprocessed source, there is not enough information here (or in jira) to see what's happening.
Yes, it is. stdx is only changed namespace name. Jonathan Reams was able to prepare a simple reproducer: https://jira.mongodb.org/browse/SERVER-22756?focusedCommentId=1195599&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-1195599 It seems that this bug appears only with enabled optimization. Tested in current F24: g++ (GCC) 6.0.0 20160302 (Red Hat 6.0.0-0.14)
Should I report this bug to the upstream?
The testcase in the above mentioned jira URL changed behavior with http://gcc.gnu.org/r222305, and passes even with trunk with -fno-lifetime-dse or with -flifetime-dse=1, so most likely this is about assuming storage of some constructed variable contains specific values from before it has been constructed. I don't know libstdc++ enough to find out where the bug is, from the dumps it seems that the bool true initialization is dead store eliminated.
Reduced: extern "C" int printf(const char*, ...); #include <string> #include <tuple> namespace placeholders { struct P1 { } _1; } template<typename Sig> struct _Bind; template<typename _Functor, typename... _Bound_args> struct _Bind<_Functor(_Bound_args...)> { _Functor _M_f; std::tuple<_Bound_args...> _M_bound_args; template<typename... _Args> explicit _Bind(const _Functor& __f, _Args&&... __args) : _M_f(__f), _M_bound_args(std::forward<_Args>(__args)...) { } template<typename _Arg> auto operator()(_Arg&& __arg) { return (std::get<0>(_M_bound_args)->*_M_f)(__arg, std::get<2>(_M_bound_args)); } } ; template<typename F, typename... Args> auto bind(F f, Args... args) { return _Bind<F(Args...)>(f, std::forward<Args>(args)...); } struct testStruct { void runme() { bool second = true; auto&& c = ::bind(&testStruct::testFn, this, placeholders::_1, second); printf("%d\n", (int) c("foo")); } bool testFn(std::string blah, bool second) { printf("%d\n", (int)second); return (blah=="foo" && second); } }; int main() { testStruct s; s.runme(); } tmp$ g++ bind.cc -O2 bind.cc: In function ‘int main()’: bind.cc:28:47: warning: ‘*((void*)&<anonymous> +16)’ is used uninitialized in this function [-Wuninitialized] return (std::get<0>(_M_bound_args)->*_M_f)(__arg, std::get<2>(_M_bound_args)); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bind.cc:43:74: note: ‘<anonymous>’ was declared here auto&& c = ::bind(&testStruct::testFn, this, placeholders::_1, second); ^ tmp$ ./a.out 192 0 The problem seems to be related to the empty P1 struct in the tuple, although the uninitialized warning is unintelligible (what is 16 bytes from <anonymous>?!)
extern "C" int printf(const char*, ...); extern "C" void abort() throw() __attribute((noreturn)); #include <tuple> struct P1 { } _1; struct testStruct; struct _Bind { std::tuple<testStruct*, P1, bool> _M_bound_args; explicit _Bind(testStruct* arg) : _M_bound_args(arg, _1, true) { } bool operator()() { return std::get<2>(_M_bound_args); } }; struct testStruct { void runme() { _Bind c(this); if ( c() == false ) abort(); } }; int main() { testStruct s; s.runme(); } tmp$ g++ -O1 bind.cc bind.cc: In function ‘int main()’: bind.cc:28:5: warning: ‘c’ is used uninitialized in this function [-Wuninitialized] if ( c() == false ) ^~ bind.cc:27:11: note: ‘c’ was declared here _Bind c(this); ^ tmp$ ./a.out Aborted (core dumped)
What can I do to get this bug fixed?
So, looking at this with -O2 -flifetime-dse=2 -fdump-tree-all-lineno -fdump-ipa-all -fno-early-inlining, I'm seeing that after inlining we have: <bb 2>: arg = &s; MEM[(struct &)&c] ={v} {CLOBBER}; D.31253 = 1; _8 = &c._M_bound_args; MEM[(struct &)_8] ={v} {CLOBBER}; _34 = &D.31253; _9 = _34; _35 = &arg; _10 = _35; _11 = &_8->D.27581; MEM[(struct &)_11] ={v} {CLOBBER}; _37 = _9; _12 = _37; _13 = &_11->D.27117; MEM[(struct &)_13] ={v} {CLOBBER}; _39 = _12; _18 = _39; _19 = &_13->D.26390; MEM[(struct &)_19] ={v} {CLOBBER}; _41 = _18; _20 = _41; _21 = &_19->D.25659; MEM[(struct &)_21] ={v} {CLOBBER}; _43 = _20; _22 = _43; _23 = *_22; _21->_M_head_impl = _23; MEM[(struct &)_13] ={v} {CLOBBER}; _46 = _10; _14 = _46; _15 = &_11->D.27118; MEM[(struct &)_15] ={v} {CLOBBER}; _48 = _14; _16 = _48; _17 = *_16; _15->_M_head_impl = _17; D.31253 ={v} {CLOBBER}; _5 = &c._M_bound_args; _24 = &_5->D.27581.D.27117.D.26390; _27 = &_24->D.25659; _29 = &_27->_M_head_impl; _51 = _29; _28 = _51; _52 = _28; _26 = _52; _53 = _26; _25 = _53; _54 = _25; _6 = _54; _7 = *_6; _55 = _7; _4 = _55; if (_4 != 0) goto <bb 4>; else goto <bb 3>; <bb 3>: abort (); <bb 4>: c ={v} {CLOBBER}; s ={v} {CLOBBER}; return 0; in main. Most of the CLOBBERs look reasonable from quick look, they clobber smaller and smaller subobjects of c. Then there is the important initialization _21->_M_head_impl = _23; where we actually store the bool value (true) into the structure. And there are two weird CLOBBERS where at least one of them breaks this, MEM[(struct &)_13] ={v} {CLOBBER}; _46 = _10; _14 = _46; _15 = &_11->D.27118; MEM[(struct &)_15] ={v} {CLOBBER}; As you can see, the first one is clobberring something that has been clobbered earlier (also using _13 as base), and the second one some artificial structure that has not been mentioned elsewhere (haven't checked yet if it overlaps with the c._M_bound_args.D.27581.D.27117.D.26390.D.25659._M_head_impl or not, but MEM[(struct &)_13] most likely does. The first one comes from: constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2- 3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] (struct _Tuple_impl * const this, struct P1 & __head, bool & __tail#0) { bool & _6; struct _Tuple_impl * _7; struct P1 & _11; <bb 2>: [rh1314174.ii:11890:19] [rh1314174.ii:11890:19] MEM[(struct &)this_2(D)] ={v} {CLOBBER}; [rh1314174.ii:11892:38] _6 = std::forward<bool> (__tail#0_4(D)); [rh1314174.ii:11892:38] _7 = [rh1314174.ii:11892:38] &this_2(D)->D.26390; [rh1314174.ii:11892:38] std::_Tuple_impl<2ul, bool>::_Tuple_impl<bool> (_7, _6); [rh1314174.ii:11892:38] _11 = std::forward<P1&> (__head_9(D)); [rh1314174.ii:11892:38] std::_Head_base<1ul, P1, true>::_Head_base<P1&> (this_2(D), _11); [rh1314174.ii:11892:42] return; }
Created attachment 1137096 [details] debug_inline_stack (gimple *) Debugging hack (perhaps it will be useful in the future again).
Using that debugging hack on the CLOBBERs in main after inlining, I get: In member function _Bind::_Bind(testStruct*) inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)&c] ={v} {CLOBBER}; In member function constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_8] ={v} {CLOBBER}; In member function constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_11] ={v} {CLOBBER}; In member function constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] at rh1314174.ii:11892:38 inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_13] ={v} {CLOBBER}; In member function constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(_UHead&&) [with _UHead = bool; long unsigned int _Idx = 2ul; _Head = bool] inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] at rh1314174.ii:11892:38 inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] at rh1314174.ii:11892:38 inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_19] ={v} {CLOBBER}; In member function constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = bool; long unsigned int _Idx = 2ul; _Head = bool] inlined from constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(_UHead&&) [with _UHead = bool; long unsigned int _Idx = 2ul; _Head = bool] at rh1314174.ii:12017:38 inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] at rh1314174.ii:11892:38 inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] at rh1314174.ii:11892:38 inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_21] ={v} {CLOBBER}; In member function constexpr std::_Head_base<_Idx, _Head, true>::_Head_base(_UHead&&) [with _UHead = P1&; long unsigned int _Idx = 1ul; _Head = P1] inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] at rh1314174.ii:11892:38 inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] at rh1314174.ii:11892:38 inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_13] ={v} {CLOBBER}; In member function constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = testStruct*&; long unsigned int _Idx = 0ul; _Head = testStruct*] inlined from constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = testStruct*&; _UTail = {P1&, bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 0ul; _Head = testStruct*; _Tail = {P1, bool}] at rh1314174.ii:11892:38 inlined from constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {testStruct*&, P1&, bool}; <template-parameter-2-2> = void; _Elements = {testStruct*, P1, bool}] at rh1314174.ii:12114:54 inlined from _Bind::_Bind(testStruct*) at rh1314174.ii:12654:32 inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 MEM[(struct &)_15] ={v} {CLOBBER}; In member function _Bind::_Bind(testStruct*) inlined from void testStruct::runme() at rh1314174.ii:12664:17 inlined from int main() at rh1314174.ii:12671:12 D.31253 ={v} {CLOBBER};
The corresponding clobbered byte sizes are: MEM[(struct &)&c] ={v} {CLOBBER}; 16 bytes, address equals to &c MEM[(struct &)_8] ={v} {CLOBBER}; 16 bytes, address equals to &c MEM[(struct &)_11] ={v} {CLOBBER}; 16 bytes, address equals to &c MEM[(struct &)_13] ={v} {CLOBBER}; 1 byte, address equals to &c MEM[(struct &)_19] ={v} {CLOBBER}; 1 byte, address equals to &c MEM[(struct &)_21] ={v} {CLOBBER}; 1 byte, address equals to &c MEM[(struct &)_13] ={v} {CLOBBER}; 1 byte, address equals to &c MEM[(struct &)_15] ={v} {CLOBBER}; 8 bytes, address equals to ((char *)&c)+8 Which means all clobbers but the second MEM[(struct &)_13] ={v} {CLOBBER}; are correct. The second _13 clobber has one more virtual backtraces, so this means: explicit constexpr _Tuple_impl(_UHead&& __head, _UTail&&... __tail) : _Inherited(std::forward<_UTail>(__tail)...), _Base(std::forward<_UHead>(__head)) { } gimple for this method is: MEM[(struct &)this] = {CLOBBER}; { D.31147 = std::forward<bool> (__tail#1); D.31148 = std::forward<P1&> (__tail#0); D.31149 = &this->D.27117; std::_Tuple_impl<1ul, P1, bool>::_Tuple_impl<P1&, bool> (D.31149, D.31148, D.31147); D.31150 = std::forward<testStruct*&> (__head); D.31151 = &this->D.27118; std::_Head_base<0ul, testStruct*, false>::_Head_base<testStruct*&> (D.31151, D.31150); } and the above CLOBBER is the first _13 clobber. Will try to find out what the D.27118 field is next.
Actually, I think the problem is elsewhere. We have: constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = P1&; _UTail = {bool}; <template-parameter-2-3> = void; long unsigned int _Idx = 1ul; _Head = P1; _Tail = {bool}] (struct _Tuple_impl * const this, struct P1 & __head, bool & __tail#0) { bool & D.31152; struct _Tuple_impl * D.31153; struct P1 & D.31154; MEM[(struct &)this] = {CLOBBER}; // I presume this is the first _13 CLOBBER, 1 byte. { D.31152 = std::forward<bool> (__tail#0); D.31153 = &this->D.26390; std::_Tuple_impl<2ul, bool>::_Tuple_impl<bool> (D.31153, D.31152); // This constructs stuff at this + 0 bytes, has other 1 byte clobbers in there and finally sets the bool field to something. D.31154 = std::forward<P1&> (__head); std::_Head_base<1ul, P1, true>::_Head_base<P1&> (this, D.31154); // And this calls following method: } } constexpr std::_Head_base<_Idx, _Head, true>::_Head_base(_UHead&&) [with _UHead = P1&; long unsigned int _Idx = 1ul; _Head = P1] (struct _Head_base * const this, struct P1 & __h) { struct P1 & D.31159; MEM[(struct &)this] = {CLOBBER}; // I presume this is the second _13 CLOBBER, 1 byte again. And the bug is that this really shouldn't have clobbered 0 byte, because it is an empty structure. { D.31159 = std::forward<P1&> (__h); } } So, I'd say the bug is that in constructors of empty classes (for whatever suitable definition of empty C++ has), which has size 1 only because C++ forces it to, but can happily overlap adjacent objects, we either should never emit the -flifetime-dse=2 ctor clobbers (and dtor clobbers?), or emit them only conditionally (only in in-charge ctors?, though wouldn't that result in not being able to share those anymore?). Jason, can you please pick this up from here?
Tracking in upstream PR70259 now.
mozjs38 is also failing with enabled optimization - bug 1307784 and builds fine with disabled optimization. Is this bug a general problem with optimization and above mentioned bug could be fixed by fix of this bug? Or is this bug and its solution only specific to this MongoDB case?
This bug is specific to the -flifetime-dse optimization and class hierarchies with empty base classes. This bug causes incorrect code to be generated, it can't cause a FTBFS bug. I can't even conceive what "a general problem with optimization" could possibly mean - there are dozens of different optimizations in GCC.
In mozjs38 case FTBFS bug is caused by failed tests (same as for MongoDB FTBFS bug which results in this bug). I really don't know where the problem could be in mozjs38 case and what I only know is that with disabled optimizations all tests pass. OK, I will see if this fix helps also for mozjs38. Thanks for help with this MongoDB problem.
I saw this error in the mozjs38 build.log: /builddir/build/BUILD/mozjs-38.0.0/js/src/jsapi-tests/testScriptObject.cpp:38:69: error: size of array 'uc_code' is not an integral constant-expression which made me think it's not just a run-time test failure but a compilation failure.
You are right. However I am not able to reproduce build failure mentioned in that bug for some time. Now it builds fine and only tests are failing.
This message is a reminder that Fedora 24 is nearing its end of life. Approximately 2 (two) weeks from now Fedora will stop maintaining and issuing updates for Fedora 24. It is Fedora's policy to close all bug reports from releases that are no longer maintained. At that time this bug will be closed as EOL if it remains open with a Fedora 'version' of '24'. Package Maintainer: If you wish for this bug to remain open because you plan to fix it in a currently maintained version, simply change the 'version' to a later Fedora version. Thank you for reporting this issue and we are sorry that we were not able to fix it before Fedora 24 is end of life. If you would still like to see this bug fixed and are able to reproduce it against a later version of Fedora, you are encouraged change the 'version' to a later Fedora version prior this bug is closed as described in the policy above. Although we aim to fix as many bugs as possible during every release's lifetime, sometimes those efforts are overtaken by events. Often a more recent Fedora release includes newer upstream software that fixes bugs or makes them obsolete.
This was fixed by an f24 update.