Skip to content
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

.next().unwrap_unchecked() on Iterator doesn't optimize as expected #107681

Closed
scottmcm opened this issue Feb 5, 2023 · 12 comments · Fixed by #128584
Closed

.next().unwrap_unchecked() on Iterator doesn't optimize as expected #107681

scottmcm opened this issue Feb 5, 2023 · 12 comments · Fixed by #128584
Assignees
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@scottmcm
Copy link
Member

scottmcm commented Feb 5, 2023

Update: per #107681 (comment), this will be fixed with LLVM 19

(Context: this is a minimized example of the behaviour I hit while working on https://github.com/rust-lang/rust/pull/107634/files#r1096624164)

I tried this code: https://rust.godbolt.org/z/EjWscs1e9

pub unsafe fn demo_std(x: &mut Copied<Iter<'_, u32>>) -> u32 {
    x.next().unwrap_unchecked()
}

I expected to see this happen: the code would read and bump the pointer in the iterator without checking the end, something like

example::demo_std:
        mov     rax, qword ptr [rdi + 8]
        lea     rcx, [rax + 4]
        mov     qword ptr [rdi + 8], rcx
        mov     eax, dword ptr [rax]
        ret

Instead, this happened: it still checks the iterator ending condition

example::demo_std:
        mov     rax, qword ptr [rdi + 8]
        cmp     rax, qword ptr [rdi]
        je      .LBB0_1
        lea     rcx, [rax + 4]
        mov     qword ptr [rdi + 8], rcx
        mov     eax, dword ptr [rax]
        ret
.LBB0_1:
        ret

Dunno where the problem is here -- could be LLVM, could be how the functions are written, could be how MIR handles things...


LLVM after optimizing:

define noundef i32 @_ZN7example8demo_std17hd41113e4f46707e0E(ptr noalias nocapture noundef align 8 dereferenceable(16) %x) unnamed_addr #0 personality ptr @rust_eh_personality {
start:
  tail call void @llvm.experimental.noalias.scope.decl(metadata !2)
  %0 = getelementptr inbounds { ptr, ptr }, ptr %x, i64 0, i32 1
  %self1.i.i = load ptr, ptr %0, align 8, !alias.scope !5, !nonnull !8, !noundef !8
  %self2.i.i = load ptr, ptr %x, align 8, !alias.scope !5, !nonnull !8, !noundef !8
  %_10.i.i = icmp eq ptr %self1.i.i, %self2.i.i
  br i1 %_10.i.i, label %"_ZN104_$LT$core..iter..adapters..copied..Copied$LT$I$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$4next17h1de76f250ae29b09E.exit", label %bb3.i.i

bb3.i.i:                                          ; preds = %start
  %1 = getelementptr inbounds i32, ptr %self1.i.i, i64 1
  store ptr %1, ptr %0, align 8, !alias.scope !5
  %v.i.i = load i32, ptr %self1.i.i, align 4, !alias.scope !9, !noalias !2, !noundef !8
  br label %"_ZN104_$LT$core..iter..adapters..copied..Copied$LT$I$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$4next17h1de76f250ae29b09E.exit"

"_ZN104_$LT$core..iter..adapters..copied..Copied$LT$I$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$4next17h1de76f250ae29b09E.exit": ; preds = %start, %bb3.i.i
  %.sroa.3.0.i.i = phi i32 [ %v.i.i, %bb3.i.i ], [ undef, %start ]
  %2 = xor i1 %_10.i.i, true
  tail call void @llvm.assume(i1 %2)
  ret i32 %.sroa.3.0.i.i
}

Looks like CSE figured out that it's assuming the same thing as the br condition in start, but nothing moved it up to start even though that block dominates the assume? If it had, then I think the the branch and compare might have properly disappeared?

@scottmcm scottmcm added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. labels Feb 5, 2023
@nikic nikic added the I-slow Issue: Problems and improvements with respect to performance of generated code. label Feb 15, 2023
@nikic
Copy link
Contributor

nikic commented Feb 15, 2023

Looks like CSE figured out that it's assuming the same thing as the br condition in start, but nothing moved it up to start even though that block dominates the assume?

It's not sufficient for the block to dominate, we also need the assume to post-dominate and there to be no implicit control flow.

I think from the LLVM side, the better way to look at this is that the edge to the end block is dead because it would result in assume(false). One could interpret this is a degenerate jump threading optimization.

DianQK added a commit to llvm/llvm-project that referenced this issue Feb 18, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D144319
DianQK added a commit to llvm/llvm-project that referenced this issue Feb 19, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Differential Revision: https://reviews.llvm.org/D144319
CarlosAlbertoEnciso pushed a commit to SNSystems/llvm-debuginfo-analyzer that referenced this issue Feb 20, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D144319
CarlosAlbertoEnciso pushed a commit to SNSystems/llvm-debuginfo-analyzer that referenced this issue Feb 20, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Differential Revision: https://reviews.llvm.org/D144319
DianQK added a commit to DianQK/llvm-project that referenced this issue Feb 21, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D144319
CarlosAlbertoEnciso pushed a commit to SNSystems/llvm-debuginfo-analyzer that referenced this issue Feb 22, 2023
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D144319
@Noratrieb Noratrieb added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 5, 2023
@Joymfl
Copy link

Joymfl commented May 27, 2023

Hi, I'm pretty new to rust, so please forgive me if this sounds ignorant, but how did you get the asm equivalent of your code?

@the8472
Copy link
Member

the8472 commented Jun 11, 2023

@Joymfl there are various ways to do that

@Joymfl
Copy link

Joymfl commented Jun 11, 2023

@the8472 thanks! Just tried out emit --asm and my mind is blown lol. Thanks again

@DianQK
Copy link
Member

DianQK commented Jul 22, 2023

Upstream patch: https://reviews.llvm.org/D144319.
@rustbot claim

@DianQK
Copy link
Member

DianQK commented Jan 26, 2024

Upstream issue: llvm/llvm-project#60717
@rustbot label llvm-fixed-upstream

@rustbot rustbot added the llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade label Jan 26, 2024
@DianQK
Copy link
Member

DianQK commented Jul 8, 2024

LLVM 19 no longer seems to solve this problem, and I suspect it has something to do with inline changes. Still keeping llvm-fixed-upstream, I'll look into that this week.

@DianQK
Copy link
Member

DianQK commented Jul 14, 2024

Bisected to: #127113
New upstream issue: llvm/llvm-project#98799
@rustbot label -llvm-fixed-upstream

@rustbot rustbot removed the llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade label Jul 14, 2024
@DianQK
Copy link
Member

DianQK commented Jul 17, 2024

@rustbot label +llvm-fixed-upstream

@rustbot rustbot added the llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade label Jul 17, 2024
@nikic
Copy link
Contributor

nikic commented Aug 1, 2024

Confirmed fixed by #127513, needs codegen test.

@nikic nikic added E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. and removed llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade labels Aug 1, 2024
@DianQK
Copy link
Member

DianQK commented Aug 3, 2024

We can finally close it. 🥲

bors added a commit to rust-lang-ci/rust that referenced this issue Aug 4, 2024
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 4, 2024
Add a set of tests for LLVM 19

Close rust-lang#107681. Close rust-lang#118306. Close rust-lang#126585.

r? compiler

try-job: i686-msvc
try-job: arm-android
try-job: test-various
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 5, 2024
Add a set of tests for LLVM 19

Close rust-lang#107681. Close rust-lang#118306. Close rust-lang#126585.

r? compiler

try-job: i686-msvc
try-job: arm-android
try-job: test-various
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 5, 2024
Add a set of tests for LLVM 19

Close rust-lang#107681. Close rust-lang#118306. Close rust-lang#126585.

r? compiler

try-job: i686-msvc
try-job: arm-android
try-job: test-various
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 5, 2024
Add a set of tests for LLVM 19

Close rust-lang#107681. Close rust-lang#118306. Close rust-lang#126585.

r? compiler

try-job: i686-msvc
try-job: arm-android
try-job: test-various
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Aug 7, 2024
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 9, 2024
@bors bors closed this as completed in 69b380d Aug 10, 2024
@scottmcm
Copy link
Member Author

I appreciate your perseverance here, @DianQK !

veselypeta pushed a commit to veselypeta/cherillvm that referenced this issue Aug 12, 2024
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D144319
veselypeta pushed a commit to veselypeta/cherillvm that referenced this issue Aug 12, 2024
This should fix rust-lang/rust#107681.

Return undefined to a noundef return value is undefined.

Example:

```
define noundef i32 @test_ret_noundef(i1 %cond) {
entry:
  br i1 %cond, label %bb1, label %bb2
bb1:
  br label %bb2
bb2:
  %r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
  ret i32 %r
}
```

Differential Revision: https://reviews.llvm.org/D144319
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants