Bug 2117528
| Summary: | GCC 11.2.1 segmentation fault of binary produced | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Red Hat Enterprise Linux 8 | Reporter: | Ankur deDev <ankur.dedev> | ||||
| Component: | gcc-toolset-11 | Assignee: | Marek Polacek <mpolacek> | ||||
| Status: | CLOSED NOTABUG | QA Contact: | qe-baseos-tools-bugs | ||||
| Severity: | unspecified | Docs Contact: | |||||
| Priority: | unspecified | ||||||
| Version: | 8.6 | CC: | sipoyare | ||||
| Target Milestone: | rc | Keywords: | Bugfix, Triaged | ||||
| Target Release: | --- | ||||||
| Hardware: | Unspecified | ||||||
| OS: | Unspecified | ||||||
| Whiteboard: | |||||||
| Fixed In Version: | Doc Type: | No Doc Update | |||||
| Doc Text: |
If this bug requires documentation, please select an appropriate Doc Type value.
|
Story Points: | --- | ||||
| Clone Of: | Environment: | ||||||
| Last Closed: | 2022-08-15 22:47:43 UTC | Type: | Bug | ||||
| Regression: | --- | Mount Type: | --- | ||||
| Documentation: | --- | CRM: | |||||
| Verified Versions: | Category: | --- | |||||
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |||||
| Cloudforms Team: | --- | Target Upstream Version: | |||||
| Embargoed: | |||||||
| Attachments: |
|
||||||
This looks like a bug in IPA modref, since it goes away with -fno-ipa-modref. Indeed, started with r11-3308. Reproduced with trunk as well. I'm going to have to file an upstream bug. The test also doesn't crash with -fno-strict-aliasing. Sorry, I actually think it's a bug in the program; I think the reinterpret_cast violates TBAA.
The function sum takes a reference to BarInner, but it's passed a BarOuter:
int sum(BarInner& bar) { ... }
BarOuter bar{ d0, d1, d2 };
int res{ sum(bar) };
The conversion from BarOuter to BarInner uses the reinterpret_cast from
Bar<Foo<Inner<0>, Outer<0> >, Foo<Inner<1>, Outer<1> >, Foo<Inner<2>, Outer<2> >> *
to
Bar<Foo<Inner<0>, Inner<0> >, Foo<Inner<1>, Inner<1> >, Foo<Inner<2>, Inner<2> >> *
but these types are not similar, and the result pointer is dereferenced.
DSE then thinks that sum doesn't use memory refs to bar, like MEM[(struct Foo *)&bar] (this
was figured by the new modref_may_conflict in ref_maybe_used_by_call_p_1), and it removes
dead stores, in effect, the initialization of bar. The fix should be just to
- BarOuter bar{ d0, d1, d2 };
+ BarInner bar{ d0, d1, d2 };
.dse1 removes:
--- test.cpp.041t.mergephi1 2022-08-15 17:22:11.592002137 -0400
+++ test.cpp.042t.dse1 2022-08-15 17:22:11.593002138 -0400
@@ -90,24 +90,18 @@ void Foo<Inner<2>, Outer<2> >::Foo (stru
void Bar<Foo<Inner<0>, Outer<0> >, Foo<Inner<1>, Outer<1> >, Foo<Inner<2>, Outer<2> > >::Bar (struct Bar * const this, struct Outer_t & objs#0, struct Outer_t & objs#1, struct Outer_t & objs#2)
{
- struct Foo * _1;
- struct Foo * _2;
- struct Foo * _3;
struct Inner * _13;
struct Inner * _14;
struct Inner * _15;
<bb 2> :
*this_5(D) ={v} {CLOBBER};
- _1 = &this_5(D)->D.2854;
MEM[(struct Foo *)this_5(D)] ={v} {CLOBBER};
_15 = &MEM[(struct Outer *)objs#0_7(D)].D.2554;
MEM[(struct Foo *)this_5(D)].obj_p = _15;
- _2 = &this_5(D)->D.2855;
MEM[(struct Foo *)this_5(D) + 8B] ={v} {CLOBBER};
_14 = &MEM[(struct Outer *)objs#1_9(D)].D.2635;
MEM[(struct Foo *)this_5(D) + 8B].obj_p = _14;
- _3 = &this_5(D)->D.2856;
MEM[(struct Foo *)this_5(D) + 16B] ={v} {CLOBBER};
_13 = &MEM[(struct Outer *)objs#2_11(D)].D.2716;
MEM[(struct Foo *)this_5(D) + 16B].obj_p = _13;
@@ -234,13 +228,6 @@ int main ()
d1.D.2635.ptr = &i1;
d2 ={v} {CLOBBER};
d2.D.2716.ptr = &i2;
- MEM[(struct Bar *)&bar] ={v} {CLOBBER};
- MEM[(struct Foo *)&bar] ={v} {CLOBBER};
- MEM[(struct Foo *)&bar].obj_p = &d0.D.2554;
- MEM[(struct Foo *)&bar + 8B] ={v} {CLOBBER};
- MEM[(struct Foo *)&bar + 8B].obj_p = &d1.D.2635;
- MEM[(struct Foo *)&bar + 16B] ={v} {CLOBBER};
- MEM[(struct Foo *)&bar + 16B].obj_p = &d2.D.2716;
res_12 = sum (&bar);
i0 ={v} {CLOBBER(eol)};
i1 ={v} {CLOBBER(eol)};
because it thinks that
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar + 16B].obj_p alias sets: 13->12
Deleted dead store: MEM[(struct Foo *)&bar + 16B].obj_p = &d2.D.2716;
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar + 16B] alias sets: 13->13
Deleted dead store: MEM[(struct Foo *)&bar + 16B] ={v} {CLOBBER};
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar + 8B].obj_p alias sets: 11->10
Deleted dead store: MEM[(struct Foo *)&bar + 8B].obj_p = &d1.D.2635;
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar + 8B] alias sets: 11->11
Deleted dead store: MEM[(struct Foo *)&bar + 8B] ={v} {CLOBBER};
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar].obj_p alias sets: 9->8
Deleted dead store: MEM[(struct Foo *)&bar].obj_p = &d0.D.2554;
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Foo *)&bar] alias sets: 9->9
Deleted dead store: MEM[(struct Foo *)&bar] ={v} {CLOBBER};
ipa-modref: call stmt res_12 = sum (&bar);
ipa-modref: call to int sum(BarInner&)/0 does not use ref: MEM[(struct Bar *)&bar] alias sets: 17->17
Deleted dead store: MEM[(struct Bar *)&bar] ={v} {CLOBBER};
Thanks a lot for looking in detail at the report. The use of a BarOuter object and the reinterpret_cast were put there on purpose but I understand your point and I agree that the C++ strict type aliasing rule is not respected in this case. I imagine that the code has been working well until now (including other compilers like Clang or ICC) because the source type (BarOuter) and the destination type (BarInner) of the cast have the exact same layout, that is three pointers where the first one is of type Inner<0>*, the second one is of type Inner<1>* and the third one is of type Inner<2>*. On top of this, the accesses made in the function "sum" through those three pointers are valid. This (simplified) test case does not convey the rationale for this construct but with the details you provided on the reasoning used in the newer versions of GCC, I will try to find a workaround that is compatible with the strict type aliasing rule of C++. I will let you know if I cannot get rid of the segmentation fault with this approach. Again, thanks a lot for your help. FWIW, it may be possible to use __attribute__ ((__may_alias__)) to work around this one specific problem. -fno-strict-aliasing would also help, but it's a big hammer. |
Created attachment 1904874 [details] test case With GCC 11.2.1 the binary produced by this code ends up with a segmentation fault. There is no problem when compiled with -O1 or -O3. From what I can see this is also the case with GCC 11.3 and GCC 12.1. On the other end, it works fine (even with -O2) on GCC 10.3.1. To reproduce, please download the attachment and run: /opt/rh/gcc-toolset-11/root/bin/g++ -Wall -O2 -std=c++11 test.cpp && ./a.out