Skip to content

InstCombine introduces unnecessary and 1 when folding trunc nuw+zext #134908

Open
@scottmcm

Description

@scottmcm

Rust in which we spotted this originally: https://rust.godbolt.org/z/Knr5jdEY5, cc rust-lang/rust#139098 (comment)

Take this input IR:

define noundef range(i8 0, 14) i8 @match0(i8 noundef range(i8 0, 3) %0) unnamed_addr {
start:
  %1 = icmp eq i8 %0, 2
  %_2 = select i1 %1, i64 1, i64 0
  %2 = trunc nuw i64 %_2 to i1
  br i1 %2, label %bb2, label %bb3

bb2:                                              ; preds = %start
  br label %bb4

bb3:                                              ; preds = %start
  %b = trunc nuw i8 %0 to i1
  %3 = zext i1 %b to i8
  br label %bb4

bb4:                                              ; preds = %bb2, %bb3
  %_0.sroa.0.0 = phi i8 [ 13, %bb2 ], [ %3, %bb3 ]
  ret i8 %_0.sroa.0.0
}

Today https://llvm.godbolt.org/z/vnfGGxfbr, --passes=instcombine transforms that to

source_filename = "/app/example.ll"

define noundef range(i8 0, 14) i8 @match0(i8 noundef range(i8 0, 3) %0) unnamed_addr {
  %1 = icmp eq i8 %0, 2
  br i1 %1, label %bb2, label %bb3

bb2:                                              ; preds = %start
  br label %bb4

bb3:                                              ; preds = %start
  %2 = and i8 %0, 1 ; 😢
  br label %bb4

bb4:                                              ; preds = %bb3, %bb2
  %_0.sroa.0.0 = phi i8 [ 13, %bb2 ], [ %2, %bb3 ]
  ret i8 %_0.sroa.0.0
}

However it should not be adding that and i8 %0, 1 -- the trunc nuw means it ought to know it doesn't need to do that.

It should instead be emitting https://alive2.llvm.org/ce/z/oN3dig

define noundef range(i8 0, 14) i8 @tgt(i8 noundef range(i8 0, 3) %0) unnamed_addr {
start:
  %1 = icmp eq i8 %0, 2
  br i1 %1, label %bb2, label %bb3

bb2:                                              ; preds = %start
  br label %bb4

bb3:                                              ; preds = %start
  br label %bb4

bb4:                                              ; preds = %bb3, %bb2
  %_0.sroa.0.0 = phi i8 [ 13, %bb2 ], [ %0, %bb3 ]
  ret i8 %_0.sroa.0.0
}

Note that the correctness for this doesn't depend on the range information; removing that from the parameter it still validates https://alive2.llvm.org/ce/z/_ed2Ch.

(If you run a full -O2 or -O3 then CVP will get rid of the and, but that's not run in -O1. And InstCombine has the information it needs to do it correctly anyway, AFAICT, so it just should.)

Activity

nikic

nikic commented on Apr 8, 2025

@nikic
Contributor

See #88609. We currently do this because not emitting the and 1 causes regressions.

scottmcm

scottmcm commented on Apr 8, 2025

@scottmcm
Author

Ah :/

Shame it can't introduce, say, a range operand bundle there instead or something.

added
llvm:instcombineCovers the InstCombine, InstSimplify and AggressiveInstCombine passes
and removed on Apr 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    llvm:instcombineCovers the InstCombine, InstSimplify and AggressiveInstCombine passes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @nikic@EugeneZelenko@scottmcm@llvmbot

        Issue actions

          InstCombine introduces unnecessary `and 1` when folding `trunc nuw`+`zext` · Issue #134908 · llvm/llvm-project