Summary: | ifcombine may move shift so it shifts more than bitwidth | ||
---|---|---|---|
Product: | gcc | Reporter: | Krister Walfridsson <kristerw> |
Component: | tree-optimization | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | NEW --- | ||
Severity: | normal | CC: | dushistov, rguenth, yinyuefengyi |
Priority: | P3 | Keywords: | wrong-code |
Version: | 13.0 | ||
Target Milestone: | --- | ||
See Also: |
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111000 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111924 |
||
Host: | Target: | ||
Build: | Known to work: | ||
Known to fail: | Last reconfirmed: | 2022-09-08 00:00:00 | |
Bug Depends on: | 106811 | ||
Bug Blocks: |
Description
Krister Walfridsson
2022-09-08 00:20:48 UTC
Confirmed. r13-131-gc2a0d2e6f636c6 addressed some issues, but leaves others such as shifts seen here. It's not clear if (int)1 << 33 is undefined behavior in GIMPLE, see PR106811. Note that ifcombine doesn't simply transform this to (x & c) & (x & (1 << b)), thus removing the short-circuiting, instead it computes tem = c | (1<<b) and checks (x & tem) == tem. That's probably safe for arbitrary results of the undefined operation though. One possibility might be to rewrite 1 << b to (1 << (b&31)) to make it always defined, but that comes at an extra cost compared to how we treat signed overflow. This optimization is invalid if (int)1 << 33 is _not_ undefined behavior in GIMPLE! Consider an architecture where (int)1 << 33 evaluates to 0. foo(2, 1, 33) evaluates to 0 for the original GIMPLE, but it evaluates to 2 in the optimized IR. A similar case is int r1, r2; int foo(int a, int s1, int s2) { if (a & (1 << s1)) return r1; if (a & (1 << s2)) return r1; return r2; } where reassoc2 optimizes this to always shift by s2. To clarify for the original testcase, ifcombine combines the two bit tests (x & (1<<a)) && (x & (1<<b)) to x & ((1<<a) | (1<<b)) == ((1<<a) | (1<<b)). We can rely on 1<<a being non-zero, otherwise the testcase invoked undefined behavior. That means whatever value 1<<b produced in the case it was unspecified we still get false when bit 1<<a was not set and a defined value when the value of 1<<b was well-defined. But yes, since we now unconditionally compute 1<<b after the transform GCC could assume b is in range. We are lacking a way to compute 1<<b optimally without that undefined behavior. (In reply to Richard Biener from comment #4) > But yes, since we now unconditionally compute 1<<b after the transform > GCC could assume b is in range. We are lacking a way to compute 1<<b > optimally without that undefined behavior. phiopt will almost definitely run into a similar thing though right now, it has not. One more similar case (that may be the same as comment #3): int g; void foo(int a, int b, int c, int d, int e) { if ((10 + a) * b) { g = (c || (g >> d)) << 1; } } In this case, reassoc1 optimizes the IR for c || (g >> d) to do (c | (g >> d)) != 0 and we are now always doing the shift, even when c is true. |