-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
__builtin_assume with embedded function calls are ignored #91612
Comments
Noting here that libc++'s llvm-project/libcxx/include/__assert Lines 29 to 35 in 2083e97
|
@danakj Yeah, I think we should fix this so that |
Yeah, this is a known issue. To summarise: LLVM is currently not equipped to handle assumptions with side-effects properly, so we just drop the assumption if it has side-effects. The same applies to C++23’s |
@llvm/issue-subscribers-clang-frontend Author: David Benjamin (davidben)
I'm not sure if this is a bug or working as intended, but it surprised me. Consider this code:
https://godbolt.org/z/1G6cao4ss While this is a toy function, this is a minimized version of plausible patterns in real world code that wishes to simultaneously use bounds-checked STL mode, but also needs to manually annotate a few performance-critical invariants that the compiler is unable to discover. Although
This reads to me like saying that the side effects in However, it seems that it actually means that the whole invariant is discarded and the optimizer doesn't use the invariant. This is a bit non-obvious. It can be worked around with the pattern in
|
1 similar comment
@llvm/issue-subscribers-clang-frontend Author: David Benjamin (davidben)
I'm not sure if this is a bug or working as intended, but it surprised me. Consider this code:
https://godbolt.org/z/1G6cao4ss While this is a toy function, this is a minimized version of plausible patterns in real world code that wishes to simultaneously use bounds-checked STL mode, but also needs to manually annotate a few performance-critical invariants that the compiler is unable to discover. Although
This reads to me like saying that the side effects in However, it seems that it actually means that the whole invariant is discarded and the optimizer doesn't use the invariant. This is a bit non-obvious. It can be worked around with the pattern in
|
Also, as a remark, iirc someone on the libc++ team disabled |
That discussion seems unrelated to this issue. That seems to talk about how LLVM does not handle stray assumptions very well. That is, don't just add naively convert all asserts to assumptions and add them for specific optimization purpose. What libc++ ran into was that naive use was bad, and then it worked around with too big of a hammer. See #91801 Just because assumes are bad when used naively doesn't mean they're always bad. This issue is about the interaction of cases when they are valuable, such as informing the compiler about invariants around bounds. Now, when you write code in C++ or Rust or any other language that encourages abstractions, you will often call small accessors, which have "side effects". They don't actually have side effects, but you'll only discover this after inlining, which you always want in this context. This issue is about assumptions with "side effects" getting ignored before that inlining. You can work around this by storing things in locals, but the warning is ambiguously phrased and doesn't even make it clear that the assumption is being ignored, just the side effects, but there are no side effects to ignore. At the least, LLVM should rephrase the warning. |
I see yeah; I suppose the idea behind the warning is that if it worked correctly, you’d get the assumption information, but since the expression is never actually evaluated, then any of its side-effects will be discarded. As it stands, we just discard the entire assumption instead because LLVM can’t actually deal w/ discarding side-effects properly at the moment.
Yeah, we only really make a conservative assumption as to whether something could have side-effects, because this warning here is actually coming from Clang, not LLVM; we don’t actually look at the function body so we don’t know whether or not it has side-effects. I suppose since we are actually discading the assumption at the moment, it would be more correct to print something along the lines of ‘assumption will be ignored, as it contains side-effects’ and change it back to what is is right now once the backend can actually handle assumptions with side-effects. |
See the reply here #55636 (comment) |
Or perhaps even ‘as it contains potential side-effects’ |
Although, even better than rephrasing the warning, would be to simply lower assume like any other function call:
This makes Clang a better tool for programmers. Side effects work like anything else and we're no better or worse off w.r.t. naive vs reasoned uses of assume. But if there's some deeper reason why that's not viable, the warning should be rephrased at the very least. |
It’s not that something like that isn’t viable. Afaik that’s more or less what GCC does—it moves the assumption into a separate function so it can guarantee that it’s never evaluated. It’s just that someone would actually have to go ahead and implement that in LLVM... |
(Does GCC even have Ohh, okay I think I see the disconnect now. I was saying that the following would be pretty solid semantics for
That is, rather than muck about with side effects, just say the predicate is evaluated and move on. Evaluating something like That's how Rust does this, for example: But I suspect you all are thinking of the C++23 Given that, yeah, it makes sense that my suggestion isn't quite what we want. So probably the immediate thing to do is to rephrase the warning, at least until Clang is able to implement the C++23 attribute more fully. |
It does not, but it has
I won’t deny that there are cases where that would probably not be an issue, but I don’t think we can change the semantics of #define _Assume(expr) (!(expr) ? __builtin_assume(0) : (void)0) That said, I have no idea how good the optimiser would be at dealing with this; given that it already has problems optimising away assumptions that it doesn’t need—which can block other optimisations—I’m not sure how well this would work.
Yeah, we had an extensive discussion about this topic when I implemented C++23’s
Yeah, I can take a look at making the warning a bit clearer when I have the time. We should be able to support expressions w/ (potential) side-effects in both |
Currently, if the argument to `__builtin_assume` and friends contains side-effects, we issue the following diagnostic: ``` <source>:1:34: warning: the argument to '__builtin_assume' has side effects that will be discarded [-Wassume] 1 | void f(int x) { __builtin_assume(x++); } | ``` The issue here is that this diagnostic misrepresents what is actually happening: not only do we discard the side-effects of the expression, but we also don’t even emit any assumption information at all because the backend is not equipped to deal with eliminating side-effects in cases such as this. This has caused some confusion (see #91612) beacuse the current wording of the warning suggests that, sensibly, only the side-effects of the expression, and not the assumption itself, will be discarded. This pr updates the diagnostic to state what is actually happening: that the assumption has no effect at all because its argument contains side-effects: ``` <source>:1:34: warning: assumption is ignored because it contains (potential) side-effects [-Wassume] 1 | void f(int x) { __builtin_assume(x++); } | ``` I’ve deliberately included ‘(potential)’ here because even expressions that only contain potential side-effects (e.g. `true ? x : x++` or a call to a function that is pure, but we don’t know that it is) cause the assumption to be discarded. This, too, has caused some confusion because it was erroneously assumed that Clang would e.g. infer that a function call is pure and not discard the assumption as a result when that isn’t the case. This is intended to be temporary; we should revert back to the original diagnostic once we have proper support for assumptions with side-effects in the backend (in which case the side-effects will still be discarded, but the assumption won’t) This fixes #91612.
I'm not sure if this is a bug or working as intended, but it surprised me. Consider this code:
https://godbolt.org/z/1G6cao4ss
While this is a toy function, this is a minimized version of plausible patterns in real world code that wishes to simultaneously use bounds-checked STL mode, but also needs to manually annotate a few performance-critical invariants that the compiler is unable to discover.
Although
f2
andf3
look like they would be the same, Clang only deletes the check inf3
. Now, Clang does emit a warning, so perhaps this is intended, but the warning says:This reads to me like saying that the side effects in
sv.size()
will be discarded at program runtime, but not that the compiler will also discard the stated invariant. As a programmer, I would read this and think, well, okay,sv.size()
actually no side effects but the type system doesn't know that, so I can silence that warning, and assume that the optimizer will still figure it out.However, it seems that it actually means that the whole invariant is discarded and the optimizer doesn't use the invariant. This is a bit non-obvious. It can be worked around with the pattern in
f3
, but:__builtin_assume
will actually be a macro that expands to nothing on non-Clang compilers, but then we'll get a warning about unusedsize
, which is extra tedious.The text was updated successfully, but these errors were encountered: