Skip to content

Useless while loop with a step of 2 doesn't optimize away #60616

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

Closed
lambda opened this issue May 7, 2019 · 3 comments
Closed

Useless while loop with a step of 2 doesn't optimize away #60616

lambda opened this issue May 7, 2019 · 3 comments
Labels
A-codegen Area: Code generation 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

@lambda
Copy link
Contributor

lambda commented May 7, 2019

While investigating a variant of #59281 and #57517 reported on Reddit, I found that even a while loop version still had an extra loop that wasn't optimized away. A minimal example that demonstrates the issue:

pub fn bongo(num: u32) -> () {
  let mut x = 0;
  while x < num {
    x += 2
  }
}

While the same loop with a step increment of 1 does optimize away the loop:

pub fn bongo(num: u32) -> () {
  let mut x = 0;
  while x < num {
    x += 1
  }
}

It looks like this optimization happens in rustc before LLVM. Increment by 2:

; playground::bongo
; Function Attrs: norecurse nounwind nonlazybind readnone uwtable
define void @_ZN10playground5bongo17hf57bf6382d7a4b02E(i32 %num) unnamed_addr #0 {
start:
  br label %bb1

bb1:                                              ; preds = %bb1, %start
  %x.0 = phi i32 [ 0, %start ], [ %1, %bb1 ]
  %0 = icmp ult i32 %x.0, %num
  %1 = add i32 %x.0, 2
  br i1 %0, label %bb1, label %bb2

bb2:                                              ; preds = %bb1
  ret void
}

Increment by 1:

; playground::bongo
; Function Attrs: norecurse nounwind nonlazybind readnone uwtable
define void @_ZN10playground5bongo17hf57bf6382d7a4b02E(i32 %num) unnamed_addr #0 {
start:
  ret void
}
@Centril Centril added I-slow Issue: Problems and improvements with respect to performance of generated code. A-codegen Area: Code generation T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 7, 2019
@lambda
Copy link
Contributor Author

lambda commented May 7, 2019

Interestingly, clang has the same behavior if using unsigned int, but is able to perform the optimization for int; it always optimizes the loop away for int, but only optimized it away for 1 if I use unsigned int for all of the variables. For rustc, it doesn't seem to matter if I use signed or unsigned integers.

@Aatch
Copy link
Contributor

Aatch commented May 7, 2019

This is expected because Rust has defined behaviour for both signed and unsigned integer overflow, while C++ only defines behaviour for unsigned integers. The undefined behaviour for signed integer overflow is what allows LLVM to optimise away the loop in the C++ version.

@lambda
Copy link
Contributor Author

lambda commented May 7, 2019

@Aatch Ah, I see, that makes sense.

And indeed, if I use a u64 for the counter here but a u32 for num to preclude the possibility of overflow, rustc is able to optimize it away:

pub fn bongo(num: u32) -> () {
  let mut x = 0u64;
  while x < num as u64 {
    x += 2
  }
}

@lambda lambda closed this as completed May 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation 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

No branches or pull requests

3 participants