-
Notifications
You must be signed in to change notification settings - Fork 13.3k
LLVM failed to optimize out the empty loop for _ in 0 .. 100 {}
#41097
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
Comments
Interestingly if the type of the range is at least 64-bit on x86_64, the loop is also completely elided. Loop-vectorization failed if the type is // Completely fine:
fn main() {
for _ in 0usize .. 100_000_000usize {}
} |
Probably the number 100 comes from the default value of the brute force parameter. Seems it can only recognize the loop iteration count if the type is >= the native word size, and falls back to brute force if its not. This is fine as well: #![feature(i128_type)]
fn main() {
for i in 0..100_000_000_000_000_000u128 {}
} |
Been playing around with this - this seems pretty bad and severely degrades my confidence in the ability of the compiler to optimize complex code! I tried desugaring the for loop to get an idea as to what's going on -- the following replicates the problem: struct Range {
start: u32,
end: u32,
}
impl Iterator for Range {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.start < self.end {
let n = self.start;
self.start = self.start + 1;
Some(n)
} else {
// self.start = self.start + 1 // uncomment me for speedup
None
}
}
}
fn main() {
let mut range = Range { start: 0, end: 100 };
loop {
match range.next() {
Some(_) => {},
None => break,
}
}
} Changing the unused return type of |
The following c code is optimized in a very similar way by LLVM via clang - so does look like an LLVM bug. Not that we can't work around it. For reference - this code optimizes down to nothing in gcc 6.3.0. struct range {
int start;
int end;
};
struct option {
int ret;
int tag;
};
struct option next(struct range *range) {
if (range->start < range->end) {
int n = range->start;
range->start++;
struct option option = {n, 1};
return option;
} else {
struct option option = {0, 0};
return option;
}
}
int main() {
struct range range = {0, 100};
for(;;) {
struct option option = next(&range);
if (option.tag) {
} else {
break;
}
}
return 0;
} |
Minimal example - fn main() {
let mut start = 0;
let end = 10000;
loop {
let tag;
if start < end {
start += 1;
tag = true;
} else {
tag = false;
}
if !tag { break; }
}
} int main() {
int start = 0;
int end = 10000;
int tag = 0;
do {
tag = 0;
if (start < end) start++, tag = 1;
} while (tag);
return 0;
} |
How about changing the range to start at
Below is the result where the loop is removed.
I wonder why the results differ if started at |
@nateozem that's probably because then the loop count is below 100 (below |
Interestingly, in your example either changing the loop condition to One possible workaround to this issue could be if for loops started using the new |
@djzin i've filled https://bugs.llvm.org/show_bug.cgi?id=34538 and giving you credit for the code and the mwe, you might want to subscribe to the issue |
Fixed in #47828 |
Test case:
Run with:
Expected: The loop is elided
Actual result: The loop still remains, in both LLVM-IR and ASM output.
The loop is elided when the upper limit is 99.
-C opt-level=2
and3
make no difference.Compiler output, showing LLVM failed to vectorize the loop because it could not determine number of loop iterations:
Versions:
(See discussion on http://stackoverflow.com/a/43234445/224671 for details)
The text was updated successfully, but these errors were encountered: