-
Notifications
You must be signed in to change notification settings - Fork 13.4k
LLVM failed to optimize out the empty loop for _ in 0 .. 100 {}
#41097
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # 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: