llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
26.68k stars 10.93k forks source link

`exact` flag in `udiv` isn't used when folded away inside comparison [InstCombine] #87895

Open scottmcm opened 2 months ago

scottmcm commented 2 months ago

Today https://llvm.godbolt.org/z/aqEabMT17, LLVM optimizes

define noundef i1 @src(i64 noundef %a, i64 noundef %b) unnamed_addr #0 {
start:
  %db = sub nuw i64 %b, %a
  %de = udiv exact i64 %db, 4
  %inrange = icmp ule i64 %de, 127
  tail call void @llvm.assume(i1 %inrange)
  %z = icmp eq i64 %de, 0
  ret i1 %z
}

to

  %inrange = icmp ult i64 %db, 512
  tail call void @llvm.assume(i1 %inrange)

That's good, but it could be better -- that ult(%db, 512) is what it needs to be when it might be in-exact.

Because the udivision is marked exact here, though, it could be simplified to a tighter check:

  %inrange = icmp ult i64 %db, 509
  tail call void @llvm.assume(i1 %inrange)

Alive proof: https://alive2.llvm.org/ce/z/YTVqCH


Context: I'm trying to give LLVM more value range information for slice iterators in rust, and was surprised to see

    %_10.i = icmp ult i64 %5, -9223372036854775807 ; 0x8000000000000001
    tail call void @llvm.assume(i1 %_10.i)

in the output, when it could have just been ult(%5, 0x80…00) aka sgt(%5, -1).

nikic commented 2 months ago

This is somewhat tricky in practice because for comparisons used in branches rather than assumes, tightening the bound in either direction makes the inverse looser.

scottmcm commented 2 months ago

Very true. Maybe this would be best phrased in terms of range operand bundles instead, so it's not an icmp?

I can't figure out how to write this on trunk, but something like

%y = udiv exact i64 %x, 4
call void @llvm.assume(i1 true) ["range"(i64 %y, i64 0, 128)]

turning into

call void @llvm.assume(i1 true) ["range"(i64 %x, i64 0, 509)]

is more obviously good.