Hacker News new | ask | show | jobs
by palmtree3000 2289 days ago
Yep. Here's an example I found once: https://godbolt.org/z/XQBcR9

  pub fn mul1(mut a: i32, b:i32) -> i32 {
      let mut out = 0;
      while a != 0 {
          out += b;
          a-=1;
      }
      out
  }

  pub fn mul2(a: i32, b: i32) -> i32 {
      if a == 0 {
          0
      } else {
          b + mul2(a-1, b)
      }
  }
Both are optimized to imul. But that's not actually correct: neither of these should terminate for negative a!

Incidentally, this is actually a rust bug, caused by LLVM performing this optimization despite it being illegal in rust.

2 comments

Interesting! In C/C++, optimizing to imul would be correct since if a was negative it would eventually underflow, and signed integer underflow is UB. Therefore ignoring that case is fine.

Presumably the same is true for LLVM IR, which would mean that it's Rust's responsibility to emit code that checks for that case since in Rust under/overflow is defined. Very interesting compiler bug! I noticed that they haven't fixed it for the latest version. Did you submit the bug report to Rust?

> Presumably the same is true for LLVM IR

I think it depends on the flags the multiplication operation is emitted with; namely, the nsw and nuw (no signed/unsigned wrap) would denote whether the optimization LLVM does is correct. If rust emits those flags then that would be a Rust bug; if Rust does not, then it might be an LLVM bug (I'm not sure what LLVM's semantics are for regular imul without flags).

I didn't: I assumed that[1] was sufficient. But (I assume, knowing little about C++) you're right that it's actually not an infinite loop, it's signed integer underflow. Given that, I'll file a bug.

[1] https://github.com/rust-lang/rust/issues/28728

I'm not entirely sure how that is an example I asked for. In this case, it seems like the compiler in both cases was able to see that the loop always terminates, either by hitting 0 directly, or by wrapping around and then hitting 0, or by simply assuming integer underflow to be UB.