-
Notifications
You must be signed in to change notification settings - Fork 12.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
Loops with inclusive ranges produce horrible assembly #75035
Comments
Probably caused by #68835 As a workaround, |
See also #75024 |
Seems like the following generates good assembly: extern {
fn f();
}
struct Inclusive(usize, Option<usize>);
impl Inclusive {
fn new(start: usize, end: usize) -> Self {
if start > end {
Inclusive(start, None)
} else {
Inclusive(start, Some(end - start))
}
}
}
impl Iterator for Inclusive {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(count) = self.1 {
self.1 = count.checked_sub(1);
let n = self.0;
if count != 0 {
self.0 = self.0 + 1;
}
Some(n)
} else {
None
}
}
}
pub unsafe fn test_loop() {
for _ in Inclusive::new(1, 7) {
f();
}
} I don't think this matches the behavior of inclusive ranges 1:1, but it seems that using a current value + counter pair optimizes correctly. |
This problem is not there any more in 1.47 beta: https://godbolt.org/z/eTPETc |
It needs a wider testing, with double and triple nested loops too. |
Still very bad with a nested loop: https://godbolt.org/z/djY1YW |
Unfortunately, to do this, the values in the range must be at least The simplest solution I found, while keeping the type signatures the same is this, which actually performs worse than the current impl 🤔 |
Unfortunately, the upgrade to LLVM 12 didn't fix the nested loop example, but made it even worse. cc @nikic Could you have a look at this? |
Cleaned up IR test case:
I believe CVP should be capable of inferring that |
Something like this would do it: https://gist.github.com/nikic/1beaff28e66771d39ec949814428005b |
Fixed upstream by llvm/llvm-project@a917fb8. |
After #87570 the nested loop generates good code as well:
Worth noting that inclusive range loop optimization is very hit and miss, but at least this particular case is handled well now. |
A problem isn't solved by the new LLVM. Is this worth reopening this issue or something? fn euler76() -> u32 {
const N: usize = 100; // Input.
let mut ways = [0; N + 1];
ways[0] = 1;
for j in 1 .. N {
//for i in j ..= N { // Adds a bound test.
for i in j .. N + 1 {
ways[i] += ways[i - j];
}
}
ways[N]
}
fn main() { assert_eq!(euler76(), 190_569_291); } |
Problem remains, even for a simple case. |
@berghetti The original example still optimizes well. I don't think reusing this ticket for similar but separate issues will get them much attention, especially years after this one has been closed. You'd better open a new report. Btw, |
This:
on 1.42:
on 1.43 and above:
https://godbolt.org/z/n63seh
The text was updated successfully, but these errors were encountered: