-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
unwrap branches are not optimized away. #71257
Comments
In the trivial case, https://rust.godbolt.org/z/3TaPU2, this doesn't seem to reproduce, so I'm going to go ahead and mark as E-needs-mcve. I suspect that this might be a case of insufficient inlining or something like that. |
I can reproduce it with the following test case: ( https://rust.godbolt.org/z/t_9kCR ) use std::vec::Vec;
pub enum Foo {
First(usize),
Second(usize),
}
pub fn foo(mut v: Vec<Foo>) -> Foo {
assert!(v.len() == 1);
v.pop().unwrap()
} In the generated code we see the branch for the assertion, which remove the None case from
|
I will also note that without the assertion, the case which generate the |
Clang gets this right with the e.g. #include <stdlib.h>
enum Foo {
A,
B
};
struct Vec {
size_t len;
Foo *data;
};
static int pop(Vec &v) {
if (v.len) {
return v.data[v.len-1];
}
return (int)Foo::B + 1;
}
Foo foo(Vec &v) {
if (v.len == 0) { exit(1); }
int k = pop(v);
if (k > 1) {
exit(3);
}
return (Foo)k;
} with foo(Vec&): # @foo(Vec&)
pushq %rax
movq (%rdi), %rax
testq %rax, %rax
je .LBB0_3
movq 8(%rdi), %rcx
movl -4(%rcx,%rax,4), %eax
cmpl $2, %eax
jge .LBB0_4
popq %rcx
retq
.LBB0_3:
movl $1, %edi
callq exit
.LBB0_4:
movl $3, %edi
callq exit but with foo(Vec&): # @foo(Vec&)
pushq %rax
movq (%rdi), %rax
testq %rax, %rax
je .LBB0_2
movq 8(%rdi), %rcx
movl -4(%rcx,%rax,4), %eax
popq %rcx
retq
.LBB0_2:
movl $1, %edi
callq exit It seems like it would be reasonable for Rust to always act like |
rustc already adds range metadata to the load of the discriminant. It seems it gets lost at some point during optimizations. |
Hmm so it does. It seems like LLVM is pretty fragile here. #include <stdlib.h>
enum Foo {
A,
B,
C,
};
Foo foo(Foo* data) {
Foo k = data[1];
if (k > Foo::C) {
exit(3);
}
return k;
} produces the following with foo(Foo*): # @foo(Foo*)
pushq %rax
movl 4(%rdi), %eax
cmpl $3, %eax
je .LBB0_2
popq %rcx
retq
.LBB0_2:
movl $3, %edi
callq exit Interestingly enough the |
I filed an LLVM bug: |
So it turns out that https://bugs.llvm.org/show_bug.cgi?id=46279 was me making an incorrect assumption about enums in C++. I looked more closely at the rust issue and filed #73258 which probably needs to be fixed before making progress on this one. |
When creating a structure which has a variant type, and that this type is wrapped as an
Option<T>
, theunwrap
branch cannot be removed by LLVM, even after proving that the only path reachable is theSome(…)
case and never theNone
case.I tried this code:
https://github.com/mozilla-spidermonkey/jsparagus/blob/330722aaa4f7c6f39422b7daae7084ea8cbcbead/crates/parser/src/queue_stack.rs#L141-L153
Calling the previous function as:
This hint LLVM that the
self.top == 0
branch does not need to be generated. However, the unwrap condition remains as theNone
value is folded within the variant and that LLVM does not know about it.I think the Rust compiler should give range analysis information that the
None
value would never be part of the variant, and as such let LLVM remove the unwrapping condition from the generated code.Meta
Tested with both
rustc --version --verbose
:and
The text was updated successfully, but these errors were encountered: